diff options
Diffstat (limited to 'drivers')
330 files changed, 20584 insertions, 6312 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 431f8b43955..7ec7d88c599 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -266,6 +266,7 @@ config ACPI_DEBUG_FUNC_TRACE config ACPI_PCI_SLOT tristate "PCI slot detection driver" + depends on SYSFS default n help This driver creates entries in /sys/bus/pci/slots/ for all PCI diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index 88e42abf5d8..0df8fcb687d 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -61,6 +61,7 @@ static int acpi_ac_open_fs(struct inode *inode, struct file *file); static int acpi_ac_add(struct acpi_device *device); static int acpi_ac_remove(struct acpi_device *device, int type); static int acpi_ac_resume(struct acpi_device *device); +static void acpi_ac_notify(struct acpi_device *device, u32 event); static const struct acpi_device_id ac_device_ids[] = { {"ACPI0003", 0}, @@ -72,10 +73,12 @@ static struct acpi_driver acpi_ac_driver = { .name = "ac", .class = ACPI_AC_CLASS, .ids = ac_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = acpi_ac_add, .remove = acpi_ac_remove, .resume = acpi_ac_resume, + .notify = acpi_ac_notify, }, }; @@ -220,16 +223,14 @@ static int acpi_ac_remove_fs(struct acpi_device *device) Driver Model -------------------------------------------------------------------------- */ -static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) +static void acpi_ac_notify(struct acpi_device *device, u32 event) { - struct acpi_ac *ac = data; - struct acpi_device *device = NULL; + struct acpi_ac *ac = acpi_driver_data(device); if (!ac) return; - device = ac->device; switch (event) { default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, @@ -253,7 +254,6 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) static int acpi_ac_add(struct acpi_device *device) { int result = 0; - acpi_status status = AE_OK; struct acpi_ac *ac = NULL; @@ -286,13 +286,6 @@ static int acpi_ac_add(struct acpi_device *device) ac->charger.get_property = get_ac_property; power_supply_register(&ac->device->dev, &ac->charger); #endif - status = acpi_install_notify_handler(device->handle, - ACPI_ALL_NOTIFY, acpi_ac_notify, - ac); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), acpi_device_bid(device), @@ -328,7 +321,6 @@ static int acpi_ac_resume(struct acpi_device *device) static int acpi_ac_remove(struct acpi_device *device, int type) { - acpi_status status = AE_OK; struct acpi_ac *ac = NULL; @@ -337,8 +329,6 @@ static int acpi_ac_remove(struct acpi_device *device, int type) ac = acpi_driver_data(device); - status = acpi_remove_notify_handler(device->handle, - ACPI_ALL_NOTIFY, acpi_ac_notify); #ifdef CONFIG_ACPI_SYSFS_POWER if (ac->charger.dev) power_supply_unregister(&ac->charger); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index b0de6312919..58b4517ce71 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -796,13 +796,12 @@ static void acpi_battery_remove_fs(struct acpi_device *device) Driver Interface -------------------------------------------------------------------------- */ -static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) +static void acpi_battery_notify(struct acpi_device *device, u32 event) { - struct acpi_battery *battery = data; - struct acpi_device *device; + struct acpi_battery *battery = acpi_driver_data(device); + if (!battery) return; - device = battery->device; acpi_battery_update(battery); acpi_bus_generate_proc_event(device, event, acpi_battery_present(battery)); @@ -819,7 +818,6 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) static int acpi_battery_add(struct acpi_device *device) { int result = 0; - acpi_status status = 0; struct acpi_battery *battery = NULL; if (!device) return -EINVAL; @@ -834,22 +832,12 @@ static int acpi_battery_add(struct acpi_device *device) acpi_battery_update(battery); #ifdef CONFIG_ACPI_PROCFS_POWER result = acpi_battery_add_fs(device); - if (result) - goto end; #endif - status = acpi_install_notify_handler(device->handle, - ACPI_ALL_NOTIFY, - acpi_battery_notify, battery); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler")); - result = -ENODEV; - goto end; - } - printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n", - ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), - device->status.battery_present ? "present" : "absent"); - end: - if (result) { + if (!result) { + printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n", + ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), + device->status.battery_present ? "present" : "absent"); + } else { #ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif @@ -860,15 +848,11 @@ static int acpi_battery_add(struct acpi_device *device) static int acpi_battery_remove(struct acpi_device *device, int type) { - acpi_status status = 0; struct acpi_battery *battery = NULL; if (!device || !acpi_driver_data(device)) return -EINVAL; battery = acpi_driver_data(device); - status = acpi_remove_notify_handler(device->handle, - ACPI_ALL_NOTIFY, - acpi_battery_notify); #ifdef CONFIG_ACPI_PROCFS_POWER acpi_battery_remove_fs(device); #endif @@ -896,10 +880,12 @@ static struct acpi_driver acpi_battery_driver = { .name = "battery", .class = ACPI_BATTERY_CLASS, .ids = battery_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = acpi_battery_add, .resume = acpi_battery_resume, .remove = acpi_battery_remove, + .notify = acpi_battery_notify, }, }; diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c index 09c69806c1f..f6baa77deef 100644 --- a/drivers/acpi/blacklist.c +++ b/drivers/acpi/blacklist.c @@ -192,6 +192,22 @@ static struct dmi_system_id acpi_osi_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "ESPRIMO Mobile V5505"), }, }, + { + .callback = dmi_disable_osi_vista, + .ident = "Sony VGN-NS10J_S", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "VGN-NS10J_S"), + }, + }, + { + .callback = dmi_disable_osi_vista, + .ident = "Sony VGN-SR290J", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Sony VGN-SR290J"), + }, + }, /* * BIOS invocation of _OSI(Linux) is almost always a BIOS bug. diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index ae862f1798d..2876fc70c3a 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -450,18 +450,16 @@ int acpi_bus_receive_event(struct acpi_bus_event *event) Notification Handling -------------------------------------------------------------------------- */ -static int -acpi_bus_check_device(struct acpi_device *device, int *status_changed) +static void acpi_bus_check_device(acpi_handle handle) { - acpi_status status = 0; + struct acpi_device *device; + acpi_status status; struct acpi_device_status old_status; - + if (acpi_bus_get_device(handle, &device)) + return; if (!device) - return -EINVAL; - - if (status_changed) - *status_changed = 0; + return; old_status = device->status; @@ -471,22 +469,15 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed) */ if (device->parent && !device->parent->status.present) { device->status = device->parent->status; - if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) { - if (status_changed) - *status_changed = 1; - } - return 0; + return; } status = acpi_bus_get_status(device); if (ACPI_FAILURE(status)) - return -ENODEV; + return; if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status)) - return 0; - - if (status_changed) - *status_changed = 1; + return; /* * Device Insertion/Removal @@ -498,33 +489,17 @@ acpi_bus_check_device(struct acpi_device *device, int *status_changed) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n")); /* TBD: Handle device removal */ } - - return 0; } -static int acpi_bus_check_scope(struct acpi_device *device) +static void acpi_bus_check_scope(acpi_handle handle) { - int result = 0; - int status_changed = 0; - - - if (!device) - return -EINVAL; - /* Status Change? */ - result = acpi_bus_check_device(device, &status_changed); - if (result) - return result; - - if (!status_changed) - return 0; + acpi_bus_check_device(handle); /* * TBD: Enumerate child devices within this device's scope and * run acpi_bus_check_device()'s on them. */ - - return 0; } static BLOCKING_NOTIFIER_HEAD(acpi_bus_notify_list); @@ -547,22 +522,19 @@ EXPORT_SYMBOL_GPL(unregister_acpi_bus_notifier); */ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) { - int result = 0; struct acpi_device *device = NULL; + struct acpi_driver *driver; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Notification %#02x to handle %p\n", + type, handle)); blocking_notifier_call_chain(&acpi_bus_notify_list, type, (void *)handle); - if (acpi_bus_get_device(handle, &device)) - return; - switch (type) { case ACPI_NOTIFY_BUS_CHECK: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received BUS CHECK notification for device [%s]\n", - device->pnp.bus_id)); - result = acpi_bus_check_scope(device); + acpi_bus_check_scope(handle); /* * TBD: We'll need to outsource certain events to non-ACPI * drivers via the device manager (device.c). @@ -570,10 +542,7 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) break; case ACPI_NOTIFY_DEVICE_CHECK: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received DEVICE CHECK notification for device [%s]\n", - device->pnp.bus_id)); - result = acpi_bus_check_device(device, NULL); + acpi_bus_check_device(handle); /* * TBD: We'll need to outsource certain events to non-ACPI * drivers via the device manager (device.c). @@ -581,44 +550,26 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) break; case ACPI_NOTIFY_DEVICE_WAKE: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received DEVICE WAKE notification for device [%s]\n", - device->pnp.bus_id)); /* TBD */ break; case ACPI_NOTIFY_EJECT_REQUEST: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received EJECT REQUEST notification for device [%s]\n", - device->pnp.bus_id)); /* TBD */ break; case ACPI_NOTIFY_DEVICE_CHECK_LIGHT: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received DEVICE CHECK LIGHT notification for device [%s]\n", - device->pnp.bus_id)); /* TBD: Exactly what does 'light' mean? */ break; case ACPI_NOTIFY_FREQUENCY_MISMATCH: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received FREQUENCY MISMATCH notification for device [%s]\n", - device->pnp.bus_id)); /* TBD */ break; case ACPI_NOTIFY_BUS_MODE_MISMATCH: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received BUS MODE MISMATCH notification for device [%s]\n", - device->pnp.bus_id)); /* TBD */ break; case ACPI_NOTIFY_POWER_FAULT: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Received POWER FAULT notification for device [%s]\n", - device->pnp.bus_id)); /* TBD */ break; @@ -629,7 +580,13 @@ static void acpi_bus_notify(acpi_handle handle, u32 type, void *data) break; } - return; + acpi_bus_get_device(handle, &device); + if (device) { + driver = device->driver; + if (driver && driver->ops.notify && + (driver->flags & ACPI_DRIVER_ALL_NOTIFY_EVENTS)) + driver->ops.notify(device, type); + } } /* -------------------------------------------------------------------------- diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 8bd2c2a6884..a8a5c29958c 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -140,46 +140,6 @@ struct device *acpi_get_physical_device(acpi_handle handle) EXPORT_SYMBOL(acpi_get_physical_device); -/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge - * This should work in general, but did not on a Lenovo T61 for the - * graphics card. But this must be fixed when the PCI device is - * bound and the kernel device struct is attached to the acpi device - * Note: A success call will increase reference count by one - * Do call put_device(dev) on the returned device then - */ -struct device *acpi_get_physical_pci_device(acpi_handle handle) -{ - struct device *dev; - long long device_id; - acpi_status status; - - status = - acpi_evaluate_integer(handle, "_ADR", NULL, &device_id); - - if (ACPI_FAILURE(status)) - return NULL; - - /* We need to attempt to determine whether the _ADR refers to a - PCI device or not. There's no terribly good way to do this, - so the best we can hope for is to assume that there'll never - be a device in the host bridge */ - if (device_id >= 0x10000) { - /* It looks like a PCI device. Does it exist? */ - dev = acpi_get_physical_device(handle); - } else { - /* It doesn't look like a PCI device. Does its parent - exist? */ - acpi_handle phandle; - if (acpi_get_parent(handle, &phandle)) - return NULL; - dev = acpi_get_physical_device(phandle); - } - if (!dev) - return NULL; - return dev; -} -EXPORT_SYMBOL(acpi_get_physical_pci_device); - static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index d916bea729f..71670719d61 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -79,6 +79,7 @@ static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; static struct workqueue_struct *kacpi_notify_wq; +static struct workqueue_struct *kacpi_hotplug_wq; struct acpi_res_list { resource_size_t start; @@ -192,8 +193,10 @@ acpi_status acpi_os_initialize1(void) { kacpid_wq = create_singlethread_workqueue("kacpid"); kacpi_notify_wq = create_singlethread_workqueue("kacpi_notify"); + kacpi_hotplug_wq = create_singlethread_workqueue("kacpi_hotplug"); BUG_ON(!kacpid_wq); BUG_ON(!kacpi_notify_wq); + BUG_ON(!kacpi_hotplug_wq); return AE_OK; } @@ -206,6 +209,7 @@ acpi_status acpi_os_terminate(void) destroy_workqueue(kacpid_wq); destroy_workqueue(kacpi_notify_wq); + destroy_workqueue(kacpi_hotplug_wq); return AE_OK; } @@ -716,6 +720,7 @@ 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", @@ -740,15 +745,17 @@ static acpi_status __acpi_os_execute(acpi_execute_type type, dpc->function = function; dpc->context = context; - if (!hp) { - INIT_WORK(&dpc->work, acpi_os_execute_deferred); - queue = (type == OSL_NOTIFY_HANDLER) ? - kacpi_notify_wq : kacpid_wq; - ret = queue_work(queue, &dpc->work); - } else { - INIT_WORK(&dpc->work, acpi_os_execute_hp_deferred); - ret = schedule_work(&dpc->work); - } + /* + * We can't run hotplug code in keventd_wq/kacpid_wq/kacpid_notify_wq + * because the hotplug code may call driver .remove() functions, + * which invoke flush_scheduled_work/acpi_os_wait_events_complete + * to flush these workqueues. + */ + 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); + ret = queue_work(queue, &dpc->work); if (!ret) { printk(KERN_ERR PREFIX diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c index bc46de3d967..a5a77b78a72 100644 --- a/drivers/acpi/pci_bind.c +++ b/drivers/acpi/pci_bind.c @@ -24,12 +24,7 @@ */ #include <linux/kernel.h> -#include <linux/module.h> -#include <linux/init.h> #include <linux/types.h> -#include <linux/proc_fs.h> -#include <linux/spinlock.h> -#include <linux/pm.h> #include <linux/pci.h> #include <linux/acpi.h> #include <acpi/acpi_bus.h> @@ -38,310 +33,76 @@ #define _COMPONENT ACPI_PCI_COMPONENT ACPI_MODULE_NAME("pci_bind"); -struct acpi_pci_data { - struct acpi_pci_id id; - struct pci_bus *bus; - struct pci_dev *dev; -}; - -static int acpi_pci_unbind(struct acpi_device *device); - -static void acpi_pci_data_handler(acpi_handle handle, u32 function, - void *context) -{ - - /* TBD: Anything we need to do here? */ - - return; -} - -/** - * acpi_get_pci_id - * ------------------ - * This function is used by the ACPI Interpreter (a.k.a. Core Subsystem) - * to resolve PCI information for ACPI-PCI devices defined in the namespace. - * This typically occurs when resolving PCI operation region information. - */ -acpi_status acpi_get_pci_id(acpi_handle handle, struct acpi_pci_id *id) +static int acpi_pci_unbind(struct acpi_device *device) { - int result = 0; - acpi_status status = AE_OK; - struct acpi_device *device = NULL; - struct acpi_pci_data *data = NULL; - - - if (!id) - return AE_BAD_PARAMETER; - - result = acpi_bus_get_device(handle, &device); - if (result) { - printk(KERN_ERR PREFIX - "Invalid ACPI Bus context for device %s\n", - acpi_device_bid(device)); - return AE_NOT_EXIST; - } - - status = acpi_get_data(handle, acpi_pci_data_handler, (void **)&data); - if (ACPI_FAILURE(status) || !data) { - ACPI_EXCEPTION((AE_INFO, status, - "Invalid ACPI-PCI context for device %s", - acpi_device_bid(device))); - return status; - } + struct pci_dev *dev; - *id = data->id; + dev = acpi_get_pci_dev(device->handle); + if (!dev || !dev->subordinate) + goto out; - /* - id->segment = data->id.segment; - id->bus = data->id.bus; - id->device = data->id.device; - id->function = data->id.function; - */ + acpi_pci_irq_del_prt(dev->subordinate); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %s has PCI address %04x:%02x:%02x.%d\n", - acpi_device_bid(device), id->segment, id->bus, - id->device, id->function)); + device->ops.bind = NULL; + device->ops.unbind = NULL; - return AE_OK; +out: + pci_dev_put(dev); + return 0; } -EXPORT_SYMBOL(acpi_get_pci_id); - -int acpi_pci_bind(struct acpi_device *device) +static int acpi_pci_bind(struct acpi_device *device) { - int result = 0; acpi_status status; - struct acpi_pci_data *data; - struct acpi_pci_data *pdata; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; acpi_handle handle; + struct pci_bus *bus; + struct pci_dev *dev; - if (!device || !device->parent) - return -EINVAL; - - data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) { - kfree(data); - return -ENODEV; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n", - (char *)buffer.pointer)); - - /* - * Segment & Bus - * ------------- - * These are obtained via the parent device's ACPI-PCI context. - */ - status = acpi_get_data(device->parent->handle, acpi_pci_data_handler, - (void **)&pdata); - if (ACPI_FAILURE(status) || !pdata || !pdata->bus) { - ACPI_EXCEPTION((AE_INFO, status, - "Invalid ACPI-PCI context for parent device %s", - acpi_device_bid(device->parent))); - result = -ENODEV; - goto end; - } - data->id.segment = pdata->id.segment; - data->id.bus = pdata->bus->number; - - /* - * Device & Function - * ----------------- - * These are simply obtained from the device's _ADR method. Note - * that a value of zero is valid. - */ - data->id.device = device->pnp.bus_address >> 16; - data->id.function = device->pnp.bus_address & 0xFFFF; - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %04x:%02x:%02x.%d\n", - data->id.segment, data->id.bus, data->id.device, - data->id.function)); - - /* - * TBD: Support slot devices (e.g. function=0xFFFF). - */ - - /* - * Locate PCI Device - * ----------------- - * Locate matching device in PCI namespace. If it doesn't exist - * this typically means that the device isn't currently inserted - * (e.g. docking station, port replicator, etc.). - */ - data->dev = pci_get_slot(pdata->bus, - PCI_DEVFN(data->id.device, data->id.function)); - if (!data->dev) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %04x:%02x:%02x.%d not present in PCI namespace\n", - data->id.segment, data->id.bus, - data->id.device, data->id.function)); - result = -ENODEV; - goto end; - } - if (!data->dev->bus) { - printk(KERN_ERR PREFIX - "Device %04x:%02x:%02x.%d has invalid 'bus' field\n", - data->id.segment, data->id.bus, - data->id.device, data->id.function); - result = -ENODEV; - goto end; - } + dev = acpi_get_pci_dev(device->handle); + if (!dev) + return 0; /* - * PCI Bridge? - * ----------- - * If so, set the 'bus' field and install the 'bind' function to - * facilitate callbacks for all of its children. + * Install the 'bind' function to facilitate callbacks for + * children of the P2P bridge. */ - if (data->dev->subordinate) { + if (dev->subordinate) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device %04x:%02x:%02x.%d is a PCI bridge\n", - data->id.segment, data->id.bus, - data->id.device, data->id.function)); - data->bus = data->dev->subordinate; + pci_domain_nr(dev->bus), dev->bus->number, + PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn))); device->ops.bind = acpi_pci_bind; device->ops.unbind = acpi_pci_unbind; } /* - * Attach ACPI-PCI Context - * ----------------------- - * Thus binding the ACPI and PCI devices. - */ - status = acpi_attach_data(device->handle, acpi_pci_data_handler, data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to attach ACPI-PCI context to device %s", - acpi_device_bid(device))); - result = -ENODEV; - goto end; - } - - /* - * PCI Routing Table - * ----------------- - * Evaluate and parse _PRT, if exists. This code is independent of - * PCI bridges (above) to allow parsing of _PRT objects within the - * scope of non-bridge devices. Note that _PRTs within the scope of - * a PCI bridge assume the bridge's subordinate bus number. + * Evaluate and parse _PRT, if exists. This code allows parsing of + * _PRT objects within the scope of non-bridge devices. Note that + * _PRTs within the scope of a PCI bridge assume the bridge's + * subordinate bus number. * * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? */ status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) { - if (data->bus) /* PCI-PCI bridge */ - acpi_pci_irq_add_prt(device->handle, data->id.segment, - data->bus->number); - else /* non-bridge PCI device */ - acpi_pci_irq_add_prt(device->handle, data->id.segment, - data->id.bus); - } - - end: - kfree(buffer.pointer); - if (result) { - pci_dev_put(data->dev); - kfree(data); - } - return result; -} - -static int acpi_pci_unbind(struct acpi_device *device) -{ - int result = 0; - acpi_status status; - struct acpi_pci_data *data; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - - - if (!device || !device->parent) - return -EINVAL; - - status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); if (ACPI_FAILURE(status)) - return -ENODEV; + goto out; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unbinding PCI device [%s]...\n", - (char *) buffer.pointer)); - kfree(buffer.pointer); + if (dev->subordinate) + bus = dev->subordinate; + else + bus = dev->bus; - status = - acpi_get_data(device->handle, acpi_pci_data_handler, - (void **)&data); - if (ACPI_FAILURE(status)) { - result = -ENODEV; - goto end; - } + acpi_pci_irq_add_prt(device->handle, bus); - status = acpi_detach_data(device->handle, acpi_pci_data_handler); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to detach data from device %s", - acpi_device_bid(device))); - result = -ENODEV; - goto end; - } - if (data->dev->subordinate) { - acpi_pci_irq_del_prt(data->id.segment, data->bus->number); - } - pci_dev_put(data->dev); - kfree(data); - - end: - return result; +out: + pci_dev_put(dev); + return 0; } -int -acpi_pci_bind_root(struct acpi_device *device, - struct acpi_pci_id *id, struct pci_bus *bus) +int acpi_pci_bind_root(struct acpi_device *device) { - int result = 0; - acpi_status status; - struct acpi_pci_data *data = NULL; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - - if (!device || !id || !bus) { - return -EINVAL; - } - - data = kzalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->id = *id; - data->bus = bus; device->ops.bind = acpi_pci_bind; device->ops.unbind = acpi_pci_unbind; - status = acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) { - kfree (data); - return -ENODEV; - } - - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to " - "%04x:%02x\n", (char *)buffer.pointer, - id->segment, id->bus)); - - status = acpi_attach_data(device->handle, acpi_pci_data_handler, data); - if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, - "Unable to attach ACPI-PCI context to device %s", - (char *)buffer.pointer)); - result = -ENODEV; - goto end; - } - - end: - kfree(buffer.pointer); - if (result != 0) - kfree(data); - - return result; + return 0; } diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 2faa9e2ac89..b794eb88ab9 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -182,7 +182,7 @@ static void do_prt_fixups(struct acpi_prt_entry *entry, } } -static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, +static int acpi_pci_irq_add_entry(acpi_handle handle, struct pci_bus *bus, struct acpi_pci_routing_table *prt) { struct acpi_prt_entry *entry; @@ -196,8 +196,8 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, * 1=INTA, 2=INTB. We use the PCI encoding throughout, so convert * it here. */ - entry->id.segment = segment; - entry->id.bus = bus; + entry->id.segment = pci_domain_nr(bus); + entry->id.bus = bus->number; entry->id.device = (prt->address >> 16) & 0xFFFF; entry->pin = prt->pin + 1; @@ -242,7 +242,7 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, return 0; } -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) +int acpi_pci_irq_add_prt(acpi_handle handle, struct pci_bus *bus) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; @@ -271,7 +271,7 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) entry = buffer.pointer; while (entry && (entry->length > 0)) { - acpi_pci_irq_add_entry(handle, segment, bus, entry); + acpi_pci_irq_add_entry(handle, bus, entry); entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } @@ -280,16 +280,17 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) return 0; } -void acpi_pci_irq_del_prt(int segment, int bus) +void acpi_pci_irq_del_prt(struct pci_bus *bus) { struct acpi_prt_entry *entry, *tmp; printk(KERN_DEBUG "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n", - segment, bus); + pci_domain_nr(bus), bus->number); spin_lock(&acpi_prt_lock); list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) { - if (segment == entry->id.segment && bus == entry->id.bus) { + if (pci_domain_nr(bus) == entry->id.segment + && bus->number == entry->id.bus) { list_del(&entry->list); kfree(entry); } diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 196f97d0095..8a5bf3b356f 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -63,9 +63,10 @@ static struct acpi_driver acpi_pci_root_driver = { struct acpi_pci_root { struct list_head node; - struct acpi_device * device; - struct acpi_pci_id id; + struct acpi_device *device; struct pci_bus *bus; + u16 segment; + u8 bus_nr; u32 osc_support_set; /* _OSC state of support bits */ u32 osc_control_set; /* _OSC state of control bits */ @@ -82,7 +83,7 @@ static DEFINE_MUTEX(osc_lock); int acpi_pci_register_driver(struct acpi_pci_driver *driver) { int n = 0; - struct list_head *entry; + struct acpi_pci_root *root; struct acpi_pci_driver **pptr = &sub_driver; while (*pptr) @@ -92,9 +93,7 @@ int acpi_pci_register_driver(struct acpi_pci_driver *driver) if (!driver->add) return 0; - list_for_each(entry, &acpi_pci_roots) { - struct acpi_pci_root *root; - root = list_entry(entry, struct acpi_pci_root, node); + list_for_each_entry(root, &acpi_pci_roots, node) { driver->add(root->device->handle); n++; } @@ -106,7 +105,7 @@ EXPORT_SYMBOL(acpi_pci_register_driver); void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) { - struct list_head *entry; + struct acpi_pci_root *root; struct acpi_pci_driver **pptr = &sub_driver; while (*pptr) { @@ -120,28 +119,48 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) if (!driver->remove) return; - list_for_each(entry, &acpi_pci_roots) { - struct acpi_pci_root *root; - root = list_entry(entry, struct acpi_pci_root, node); + list_for_each_entry(root, &acpi_pci_roots, node) driver->remove(root->device->handle); - } } EXPORT_SYMBOL(acpi_pci_unregister_driver); acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) { - struct acpi_pci_root *tmp; + struct acpi_pci_root *root; - list_for_each_entry(tmp, &acpi_pci_roots, node) { - if ((tmp->id.segment == (u16) seg) && (tmp->id.bus == (u16) bus)) - return tmp->device->handle; - } + list_for_each_entry(root, &acpi_pci_roots, node) + if ((root->segment == (u16) seg) && (root->bus_nr == (u16) bus)) + return root->device->handle; return NULL; } EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); +/** + * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge + * @handle - the ACPI CA node in question. + * + * Note: we could make this API take a struct acpi_device * instead, but + * for now, it's more convenient to operate on an acpi_handle. + */ +int acpi_is_root_bridge(acpi_handle handle) +{ + int ret; + struct acpi_device *device; + + ret = acpi_bus_get_device(handle, &device); + if (ret) + return 0; + + ret = acpi_match_device_ids(device, root_device_ids); + if (ret) + return 0; + else + return 1; +} +EXPORT_SYMBOL_GPL(acpi_is_root_bridge); + static acpi_status get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) { @@ -161,19 +180,22 @@ get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) return AE_OK; } -static acpi_status try_get_root_bridge_busnr(acpi_handle handle, int *busnum) +static acpi_status try_get_root_bridge_busnr(acpi_handle handle, + unsigned long long *bus) { acpi_status status; + int busnum; - *busnum = -1; + busnum = -1; status = acpi_walk_resources(handle, METHOD_NAME__CRS, - get_root_bridge_busnr_callback, busnum); + get_root_bridge_busnr_callback, &busnum); if (ACPI_FAILURE(status)) return status; /* Check if we really get a bus number from _CRS */ - if (*busnum == -1) + if (busnum == -1) return AE_ERROR; + *bus = busnum; return AE_OK; } @@ -298,6 +320,7 @@ static acpi_status acpi_pci_osc_support(struct acpi_pci_root *root, u32 flags) static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) { struct acpi_pci_root *root; + list_for_each_entry(root, &acpi_pci_roots, node) { if (root->device->handle == handle) return root; @@ -305,6 +328,87 @@ static struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle) return NULL; } +struct acpi_handle_node { + struct list_head node; + acpi_handle handle; +}; + +/** + * acpi_get_pci_dev - convert ACPI CA handle to struct pci_dev + * @handle: the handle in question + * + * Given an ACPI CA handle, the desired PCI device is located in the + * list of PCI devices. + * + * If the device is found, its reference count is increased and this + * function returns a pointer to its data structure. The caller must + * decrement the reference count by calling pci_dev_put(). + * If no device is found, %NULL is returned. + */ +struct pci_dev *acpi_get_pci_dev(acpi_handle handle) +{ + int dev, fn; + unsigned long long adr; + acpi_status status; + acpi_handle phandle; + struct pci_bus *pbus; + struct pci_dev *pdev = NULL; + struct acpi_handle_node *node, *tmp; + struct acpi_pci_root *root; + LIST_HEAD(device_list); + + /* + * Walk up the ACPI CA namespace until we reach a PCI root bridge. + */ + phandle = handle; + while (!acpi_is_root_bridge(phandle)) { + node = kzalloc(sizeof(struct acpi_handle_node), GFP_KERNEL); + if (!node) + goto out; + + INIT_LIST_HEAD(&node->node); + node->handle = phandle; + list_add(&node->node, &device_list); + + status = acpi_get_parent(phandle, &phandle); + if (ACPI_FAILURE(status)) + goto out; + } + + root = acpi_pci_find_root(phandle); + if (!root) + goto out; + + pbus = root->bus; + + /* + * Now, walk back down the PCI device tree until we return to our + * original handle. Assumes that everything between the PCI root + * bridge and the device we're looking for must be a P2P bridge. + */ + list_for_each_entry(node, &device_list, node) { + acpi_handle hnd = node->handle; + status = acpi_evaluate_integer(hnd, "_ADR", NULL, &adr); + if (ACPI_FAILURE(status)) + goto out; + dev = (adr >> 16) & 0xffff; + fn = adr & 0xffff; + + pdev = pci_get_slot(pbus, PCI_DEVFN(dev, fn)); + if (hnd == handle) + break; + + pbus = pdev->subordinate; + pci_dev_put(pdev); + } +out: + list_for_each_entry_safe(node, tmp, &device_list, node) + kfree(node); + + return pdev; +} +EXPORT_SYMBOL_GPL(acpi_get_pci_dev); + /** * acpi_pci_osc_control_set - commit requested control to Firmware * @handle: acpi_handle for the target ACPI object @@ -363,31 +467,46 @@ EXPORT_SYMBOL(acpi_pci_osc_control_set); static int __devinit acpi_pci_root_add(struct acpi_device *device) { - int result = 0; - struct acpi_pci_root *root = NULL; - struct acpi_pci_root *tmp; - acpi_status status = AE_OK; - unsigned long long value = 0; - acpi_handle handle = NULL; + unsigned long long segment, bus; + acpi_status status; + int result; + struct acpi_pci_root *root; + acpi_handle handle; struct acpi_device *child; u32 flags, base_flags; + segment = 0; + status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL, + &segment); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + printk(KERN_ERR PREFIX "can't evaluate _SEG\n"); + return -ENODEV; + } - if (!device) - return -EINVAL; + /* Check _CRS first, then _BBN. If no _BBN, default to zero. */ + bus = 0; + status = try_get_root_bridge_busnr(device->handle, &bus); + if (ACPI_FAILURE(status)) { + status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL, &bus); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + printk(KERN_ERR PREFIX + "no bus number in _CRS and can't evaluate _BBN\n"); + return -ENODEV; + } + } root = kzalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); if (!root) return -ENOMEM; - INIT_LIST_HEAD(&root->node); + INIT_LIST_HEAD(&root->node); root->device = device; + root->segment = segment & 0xFFFF; + root->bus_nr = bus & 0xFF; strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); device->driver_data = root; - device->ops.bind = acpi_pci_bind; - /* * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. @@ -395,79 +514,6 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; acpi_pci_osc_support(root, flags); - /* - * Segment - * ------- - * Obtained via _SEG, if exists, otherwise assumed to be zero (0). - */ - status = acpi_evaluate_integer(device->handle, METHOD_NAME__SEG, NULL, - &value); - switch (status) { - case AE_OK: - root->id.segment = (u16) value; - break; - case AE_NOT_FOUND: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Assuming segment 0 (no _SEG)\n")); - root->id.segment = 0; - break; - default: - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _SEG")); - result = -ENODEV; - goto end; - } - - /* - * Bus - * --- - * Obtained via _BBN, if exists, otherwise assumed to be zero (0). - */ - status = acpi_evaluate_integer(device->handle, METHOD_NAME__BBN, NULL, - &value); - switch (status) { - case AE_OK: - root->id.bus = (u16) value; - break; - case AE_NOT_FOUND: - ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming bus 0 (no _BBN)\n")); - root->id.bus = 0; - break; - default: - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BBN")); - result = -ENODEV; - goto end; - } - - /* Some systems have wrong _BBN */ - list_for_each_entry(tmp, &acpi_pci_roots, node) { - if ((tmp->id.segment == root->id.segment) - && (tmp->id.bus == root->id.bus)) { - int bus = 0; - acpi_status status; - - printk(KERN_ERR PREFIX - "Wrong _BBN value, reboot" - " and use option 'pci=noacpi'\n"); - - status = try_get_root_bridge_busnr(device->handle, &bus); - if (ACPI_FAILURE(status)) - break; - if (bus != root->id.bus) { - printk(KERN_INFO PREFIX - "PCI _CRS %d overrides _BBN 0\n", bus); - root->id.bus = bus; - } - break; - } - } - /* - * Device & Function - * ----------------- - * Obtained from _ADR (which has already been evaluated for us). - */ - root->id.device = device->pnp.bus_address >> 16; - root->id.function = device->pnp.bus_address & 0xFFFF; - /* * TBD: Need PCI interface for enumeration/configuration of roots. */ @@ -477,7 +523,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n", acpi_device_name(device), acpi_device_bid(device), - root->id.segment, root->id.bus); + root->segment, root->bus_nr); /* * Scan the Root Bridge @@ -486,11 +532,11 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) * PCI namespace does not get created until this call is made (and * thus the root bridge's pci_dev does not exist). */ - root->bus = pci_acpi_scan_root(device, root->id.segment, root->id.bus); + root->bus = pci_acpi_scan_root(device, segment, bus); if (!root->bus) { printk(KERN_ERR PREFIX "Bus %04x:%02x not present in PCI namespace\n", - root->id.segment, root->id.bus); + root->segment, root->bus_nr); result = -ENODEV; goto end; } @@ -500,7 +546,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) * ----------------------- * Thus binding the ACPI and PCI devices. */ - result = acpi_pci_bind_root(device, &root->id, root->bus); + result = acpi_pci_bind_root(device); if (result) goto end; @@ -511,8 +557,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) */ status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); if (ACPI_SUCCESS(status)) - result = acpi_pci_irq_add_prt(device->handle, root->id.segment, - root->id.bus); + result = acpi_pci_irq_add_prt(device->handle, root->bus); /* * Scan and bind all _ADR-Based Devices @@ -531,42 +576,28 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) if (flags != base_flags) acpi_pci_osc_support(root, flags); - end: - if (result) { - if (!list_empty(&root->node)) - list_del(&root->node); - kfree(root); - } + return 0; +end: + if (!list_empty(&root->node)) + list_del(&root->node); + kfree(root); return result; } static int acpi_pci_root_start(struct acpi_device *device) { - struct acpi_pci_root *root; + struct acpi_pci_root *root = acpi_driver_data(device); - - list_for_each_entry(root, &acpi_pci_roots, node) { - if (root->device == device) { - pci_bus_add_devices(root->bus); - return 0; - } - } - return -ENODEV; + pci_bus_add_devices(root->bus); + return 0; } static int acpi_pci_root_remove(struct acpi_device *device, int type) { - struct acpi_pci_root *root = NULL; - - - if (!device || !acpi_driver_data(device)) - return -EINVAL; - - root = acpi_driver_data(device); + struct acpi_pci_root *root = acpi_driver_data(device); kfree(root); - return 0; } diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 56665a63bf1..d74365d4a6e 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -194,7 +194,7 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) { - int result = 0, state; + int result = 0; int found = 0; acpi_status status = AE_OK; struct acpi_power_resource *resource = NULL; @@ -236,18 +236,6 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) if (ACPI_FAILURE(status)) return -ENODEV; - if (!acpi_power_nocheck) { - /* - * If acpi_power_nocheck is set, it is unnecessary to check - * the power state after power transition. - */ - result = acpi_power_get_state(resource->device->handle, - &state); - if (result) - return result; - if (state != ACPI_POWER_RESOURCE_STATE_ON) - return -ENOEXEC; - } /* Update the power resource's _device_ power state */ resource->device->power.state = ACPI_STATE_D0; @@ -258,7 +246,7 @@ static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) { - int result = 0, state; + int result = 0; acpi_status status = AE_OK; struct acpi_power_resource *resource = NULL; struct list_head *node, *next; @@ -293,18 +281,6 @@ static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) if (ACPI_FAILURE(status)) return -ENODEV; - if (!acpi_power_nocheck) { - /* - * If acpi_power_nocheck is set, it is unnecessary to check - * the power state after power transition. - */ - result = acpi_power_get_state(handle, &state); - if (result) - return result; - if (state != ACPI_POWER_RESOURCE_STATE_OFF) - return -ENOEXEC; - } - /* Update the power resource's _device_ power state */ resource->device->power.state = ACPI_STATE_D3; diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 23f0fb84f1c..84e0f3c0744 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -89,7 +89,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr); static const struct acpi_device_id processor_device_ids[] = { {ACPI_PROCESSOR_OBJECT_HID, 0}, - {ACPI_PROCESSOR_HID, 0}, + {"ACPI0007", 0}, {"", 0}, }; MODULE_DEVICE_TABLE(acpi, processor_device_ids); @@ -596,7 +596,21 @@ static int acpi_processor_get_info(struct acpi_device *device) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No bus mastering arbitration control\n")); - if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_HID)) { + if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_OBJECT_HID)) { + /* Declared with "Processor" statement; match ProcessorID */ + status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Evaluating processor object\n"); + return -ENODEV; + } + + /* + * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. + * >>> 'acpi_get_processor_id(acpi_id, &id)' in + * arch/xxx/acpi.c + */ + pr->acpi_id = object.processor.proc_id; + } else { /* * Declared with "Device" statement; match _UID. * Note that we don't handle string _UIDs yet. @@ -611,20 +625,6 @@ static int acpi_processor_get_info(struct acpi_device *device) } device_declaration = 1; pr->acpi_id = value; - } else { - /* Declared with "Processor" statement; match ProcessorID */ - status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); - if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Evaluating processor object\n"); - return -ENODEV; - } - - /* - * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. - * >>> 'acpi_get_processor_id(acpi_id, &id)' in - * arch/xxx/acpi.c - */ - pr->acpi_id = object.processor.proc_id; } cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id); @@ -649,7 +649,16 @@ static int acpi_processor_get_info(struct acpi_device *device) return -ENODEV; } } - + /* + * On some boxes several processors use the same processor bus id. + * But they are located in different scope. For example: + * \_SB.SCK0.CPU0 + * \_SB.SCK1.CPU0 + * Rename the processor device bus id. And the new bus id will be + * generated as the following format: + * CPU+CPU ID. + */ + sprintf(acpi_device_bid(device), "CPU%X", pr->id); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, pr->acpi_id)); @@ -731,6 +740,8 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) /* _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 diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 10a2d913635..0efa59e7e3a 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -139,7 +139,7 @@ static void acpi_safe_halt(void) * are affected too. We pick the most conservative approach: we assume * that the local APIC stops in both C2 and C3. */ -static void acpi_timer_check_state(int state, struct acpi_processor *pr, +static void lapic_timer_check_state(int state, struct acpi_processor *pr, struct acpi_processor_cx *cx) { struct acpi_processor_power *pwr = &pr->power; @@ -162,7 +162,7 @@ static void acpi_timer_check_state(int state, struct acpi_processor *pr, pr->power.timer_broadcast_on_state = state; } -static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) +static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { unsigned long reason; @@ -173,7 +173,7 @@ static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) } /* Power(C) State timer broadcast control */ -static void acpi_state_timer_broadcast(struct acpi_processor *pr, +static void lapic_timer_state_broadcast(struct acpi_processor *pr, struct acpi_processor_cx *cx, int broadcast) { @@ -190,10 +190,10 @@ static void acpi_state_timer_broadcast(struct acpi_processor *pr, #else -static void acpi_timer_check_state(int state, struct acpi_processor *pr, +static void lapic_timer_check_state(int state, struct acpi_processor *pr, struct acpi_processor_cx *cstate) { } -static void acpi_propagate_timer_broadcast(struct acpi_processor *pr) { } -static void acpi_state_timer_broadcast(struct acpi_processor *pr, +static void lapic_timer_propagate_broadcast(struct acpi_processor *pr) { } +static void lapic_timer_state_broadcast(struct acpi_processor *pr, struct acpi_processor_cx *cx, int broadcast) { @@ -515,7 +515,8 @@ static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx) static void acpi_processor_power_verify_c3(struct acpi_processor *pr, struct acpi_processor_cx *cx) { - static int bm_check_flag; + static int bm_check_flag = -1; + static int bm_control_flag = -1; if (!cx->address) @@ -545,12 +546,14 @@ static void acpi_processor_power_verify_c3(struct acpi_processor *pr, } /* All the logic here assumes flags.bm_check is same across all CPUs */ - if (!bm_check_flag) { + if (bm_check_flag == -1) { /* Determine whether bm_check is needed based on CPU */ acpi_processor_power_init_bm_check(&(pr->flags), pr->id); bm_check_flag = pr->flags.bm_check; + bm_control_flag = pr->flags.bm_control; } else { pr->flags.bm_check = bm_check_flag; + pr->flags.bm_control = bm_control_flag; } if (pr->flags.bm_check) { @@ -614,29 +617,25 @@ static int acpi_processor_power_verify(struct acpi_processor *pr) switch (cx->type) { case ACPI_STATE_C1: cx->valid = 1; - acpi_timer_check_state(i, pr, cx); break; case ACPI_STATE_C2: acpi_processor_power_verify_c2(cx); - if (cx->valid) - acpi_timer_check_state(i, pr, cx); break; case ACPI_STATE_C3: acpi_processor_power_verify_c3(pr, cx); - if (cx->valid) - acpi_timer_check_state(i, pr, cx); break; } - if (cx->valid) - tsc_check_state(cx->type); + if (!cx->valid) + continue; - if (cx->valid) - working++; + lapic_timer_check_state(i, pr, cx); + tsc_check_state(cx->type); + working++; } - acpi_propagate_timer_broadcast(pr); + lapic_timer_propagate_broadcast(pr); return (working); } @@ -839,7 +838,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, return 0; } - acpi_state_timer_broadcast(pr, cx, 1); + lapic_timer_state_broadcast(pr, cx, 1); kt1 = ktime_get_real(); acpi_idle_do_entry(cx); kt2 = ktime_get_real(); @@ -847,7 +846,7 @@ static int acpi_idle_enter_c1(struct cpuidle_device *dev, local_irq_enable(); cx->usage++; - acpi_state_timer_broadcast(pr, cx, 0); + lapic_timer_state_broadcast(pr, cx, 0); return idle_time; } @@ -892,7 +891,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, * Must be done before busmaster disable as we might need to * access HPET ! */ - acpi_state_timer_broadcast(pr, cx, 1); + lapic_timer_state_broadcast(pr, cx, 1); if (cx->type == ACPI_STATE_C3) ACPI_FLUSH_CPU_CACHE(); @@ -914,7 +913,7 @@ static int acpi_idle_enter_simple(struct cpuidle_device *dev, cx->usage++; - acpi_state_timer_broadcast(pr, cx, 0); + lapic_timer_state_broadcast(pr, cx, 0); cx->time += sleep_ticks; return idle_time; } @@ -981,7 +980,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, * Must be done before busmaster disable as we might need to * access HPET ! */ - acpi_state_timer_broadcast(pr, cx, 1); + lapic_timer_state_broadcast(pr, cx, 1); kt1 = ktime_get_real(); /* @@ -1026,7 +1025,7 @@ static int acpi_idle_enter_bm(struct cpuidle_device *dev, cx->usage++; - acpi_state_timer_broadcast(pr, cx, 0); + lapic_timer_state_broadcast(pr, cx, 0); cx->time += sleep_ticks; return idle_time; } diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 8ff510b91d8..781435d7e36 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -95,7 +95,7 @@ acpi_device_modalias_show(struct device *dev, struct device_attribute *attr, cha } static DEVICE_ATTR(modalias, 0444, acpi_device_modalias_show, NULL); -static int acpi_bus_hot_remove_device(void *context) +static void acpi_bus_hot_remove_device(void *context) { struct acpi_device *device; acpi_handle handle = context; @@ -104,10 +104,10 @@ static int acpi_bus_hot_remove_device(void *context) acpi_status status = AE_OK; if (acpi_bus_get_device(handle, &device)) - return 0; + return; if (!device) - return 0; + return; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Hot-removing device %s...\n", dev_name(&device->dev))); @@ -115,7 +115,7 @@ static int acpi_bus_hot_remove_device(void *context) if (acpi_bus_trim(device, 1)) { printk(KERN_ERR PREFIX "Removing device failed\n"); - return -1; + return; } /* power off device */ @@ -142,9 +142,10 @@ static int acpi_bus_hot_remove_device(void *context) */ status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); if (ACPI_FAILURE(status)) - return -ENODEV; + printk(KERN_WARNING PREFIX + "Eject device failed\n"); - return 0; + return; } static ssize_t @@ -155,7 +156,6 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, acpi_status status; acpi_object_type type = 0; struct acpi_device *acpi_device = to_acpi_device(d); - struct task_struct *task; if ((!count) || (buf[0] != '1')) { return -EINVAL; @@ -172,11 +172,7 @@ acpi_eject_store(struct device *d, struct device_attribute *attr, goto err; } - /* remove the device in another thread to fix the deadlock issue */ - task = kthread_run(acpi_bus_hot_remove_device, - acpi_device->handle, "acpi_hot_remove_device"); - if (IS_ERR(task)) - ret = PTR_ERR(task); + acpi_os_hotplug_execute(acpi_bus_hot_remove_device, acpi_device->handle); err: return ret; } @@ -198,12 +194,12 @@ acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *b int result; result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); - if(result) + if (result) goto end; result = sprintf(buf, "%s\n", (char*)path.pointer); kfree(path.pointer); - end: +end: return result; } static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); @@ -217,21 +213,21 @@ static int acpi_device_setup_files(struct acpi_device *dev) /* * Devices gotten from FADT don't have a "path" attribute */ - if(dev->handle) { + if (dev->handle) { result = device_create_file(&dev->dev, &dev_attr_path); - if(result) + if (result) goto end; } - if(dev->flags.hardware_id) { + if (dev->flags.hardware_id) { result = device_create_file(&dev->dev, &dev_attr_hid); - if(result) + if (result) goto end; } - if (dev->flags.hardware_id || dev->flags.compatible_ids){ + if (dev->flags.hardware_id || dev->flags.compatible_ids) { result = device_create_file(&dev->dev, &dev_attr_modalias); - if(result) + if (result) goto end; } @@ -242,7 +238,7 @@ static int acpi_device_setup_files(struct acpi_device *dev) status = acpi_get_handle(dev->handle, "_EJ0", &temp); if (ACPI_SUCCESS(status)) result = device_create_file(&dev->dev, &dev_attr_eject); - end: +end: return result; } @@ -262,9 +258,9 @@ static void acpi_device_remove_files(struct acpi_device *dev) if (dev->flags.hardware_id || dev->flags.compatible_ids) device_remove_file(&dev->dev, &dev_attr_modalias); - if(dev->flags.hardware_id) + if (dev->flags.hardware_id) device_remove_file(&dev->dev, &dev_attr_hid); - if(dev->handle) + if (dev->handle) device_remove_file(&dev->dev, &dev_attr_path); } /* -------------------------------------------------------------------------- @@ -512,7 +508,7 @@ static int acpi_device_register(struct acpi_device *device, break; } } - if(!found) { + 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"); acpi_device_bus_id->instance_no = 0; @@ -530,22 +526,21 @@ static int acpi_device_register(struct acpi_device *device, if (device->parent) device->dev.parent = &parent->dev; device->dev.bus = &acpi_bus_type; - device_initialize(&device->dev); device->dev.release = &acpi_device_release; - result = device_add(&device->dev); - if(result) { - dev_err(&device->dev, "Error adding device\n"); + result = device_register(&device->dev); + if (result) { + dev_err(&device->dev, "Error registering device\n"); goto end; } result = acpi_device_setup_files(device); - if(result) + if (result) printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", dev_name(&device->dev)); device->removal_type = ACPI_BUS_REMOVAL_NORMAL; return 0; - end: +end: mutex_lock(&acpi_device_lock); if (device->parent) list_del(&device->node); @@ -577,7 +572,7 @@ static void acpi_device_unregister(struct acpi_device *device, int type) * @device: the device to add and initialize * @driver: driver for the device * - * Used to initialize a device via its device driver. Called whenever a + * Used to initialize a device via its device driver. Called whenever a * driver is bound to a device. Invokes the driver's add() ops. */ static int @@ -585,7 +580,6 @@ acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver) { int result = 0; - if (!device || !driver) return -EINVAL; @@ -802,7 +796,7 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) if (!acpi_match_device_ids(device, button_device_ids)) device->wakeup.flags.run_wake = 1; - end: +end: if (ACPI_FAILURE(status)) device->flags.wake_capable = 0; return 0; @@ -1070,7 +1064,7 @@ static void acpi_device_set_id(struct acpi_device *device, break; } - /* + /* * \_SB * ---- * Fix for the system root bus device -- the only root-level device. @@ -1320,7 +1314,7 @@ acpi_add_single_object(struct acpi_device **child, device->parent->ops.bind(device); } - end: +end: if (!result) *child = device; else { @@ -1464,7 +1458,6 @@ acpi_bus_add(struct acpi_device **child, return result; } - EXPORT_SYMBOL(acpi_bus_add); int acpi_bus_start(struct acpi_device *device) @@ -1484,7 +1477,6 @@ int acpi_bus_start(struct acpi_device *device) } return result; } - EXPORT_SYMBOL(acpi_bus_start); int acpi_bus_trim(struct acpi_device *start, int rmdevice) @@ -1542,7 +1534,6 @@ 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) { int result = 0; @@ -1610,6 +1601,6 @@ int __init acpi_scan_init(void) if (result) acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); - Done: +Done: return result; } diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 1bdfb37377e..8851315ce85 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -76,6 +76,7 @@ MODULE_LICENSE("GPL"); static int brightness_switch_enabled = 1; module_param(brightness_switch_enabled, bool, 0644); +static int register_count = 0; static int acpi_video_bus_add(struct acpi_device *device); static int acpi_video_bus_remove(struct acpi_device *device, int type); static int acpi_video_resume(struct acpi_device *device); @@ -586,6 +587,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = { DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5315"), }, }, + { + .callback = video_set_bqc_offset, + .ident = "Acer Aspire 7720", + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7720"), + }, + }, {} }; @@ -976,6 +985,11 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->backlight->props.max_brightness = device->brightness->count-3; kfree(name); + result = sysfs_create_link(&device->backlight->dev.kobj, + &device->dev->dev.kobj, "device"); + if (result) + printk(KERN_ERR PREFIX "Create sysfs link\n"); + device->cdev = thermal_cooling_device_register("LCD", device->dev, &video_cooling_ops); if (IS_ERR(device->cdev)) @@ -1054,15 +1068,15 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) static int acpi_video_bus_check(struct acpi_video_bus *video) { acpi_status status = -ENOENT; - struct device *dev; + struct pci_dev *dev; if (!video) return -EINVAL; - dev = acpi_get_physical_pci_device(video->device->handle); + dev = acpi_get_pci_dev(video->device->handle); if (!dev) return -ENODEV; - put_device(dev); + pci_dev_put(dev); /* Since there is no HID, CID and so on for VGA driver, we have * to check well known required nodes. @@ -1990,6 +2004,7 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) status = acpi_remove_notify_handler(device->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); + sysfs_remove_link(&device->backlight->dev.kobj, "device"); backlight_device_unregister(device->backlight); if (device->cdev) { sysfs_remove_link(&device->dev->dev.kobj, @@ -2318,6 +2333,13 @@ static int __init intel_opregion_present(void) int acpi_video_register(void) { int result = 0; + if (register_count) { + /* + * if the function of acpi_video_register is already called, + * don't register the acpi_vide_bus again and return no error. + */ + return 0; + } acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); if (!acpi_video_dir) @@ -2329,10 +2351,35 @@ int acpi_video_register(void) return -ENODEV; } + /* + * When the acpi_video_bus is loaded successfully, increase + * the counter reference. + */ + register_count = 1; + return 0; } EXPORT_SYMBOL(acpi_video_register); +void acpi_video_unregister(void) +{ + if (!register_count) { + /* + * If the acpi video bus is already unloaded, don't + * unload it again and return directly. + */ + return; + } + acpi_bus_unregister_driver(&acpi_video_bus); + + remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); + + register_count = 0; + + return; +} +EXPORT_SYMBOL(acpi_video_unregister); + /* * This is kind of nasty. Hardware using Intel chipsets may require * the video opregion code to be run first in order to initialise @@ -2350,16 +2397,12 @@ static int __init acpi_video_init(void) return acpi_video_register(); } -void acpi_video_exit(void) +static void __exit acpi_video_exit(void) { - - acpi_bus_unregister_driver(&acpi_video_bus); - - remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); + acpi_video_unregister(); return; } -EXPORT_SYMBOL(acpi_video_exit); module_init(acpi_video_init); module_exit(acpi_video_exit); diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 09737275e25..7cd2b63435e 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -10,7 +10,7 @@ * assinged * * After PCI devices are glued with ACPI devices - * acpi_get_physical_pci_device() can be called to identify ACPI graphics + * acpi_get_pci_dev() can be called to identify ACPI graphics * devices for which a real graphics card is plugged in * * Now acpi_video_get_capabilities() can be called to check which @@ -36,6 +36,7 @@ #include <linux/acpi.h> #include <linux/dmi.h> +#include <linux/pci.h> ACPI_MODULE_NAME("video"); #define _COMPONENT ACPI_VIDEO_COMPONENT @@ -109,7 +110,7 @@ static acpi_status find_video(acpi_handle handle, u32 lvl, void *context, void **rv) { long *cap = context; - struct device *dev; + struct pci_dev *dev; struct acpi_device *acpi_dev; const struct acpi_device_id video_ids[] = { @@ -120,10 +121,10 @@ find_video(acpi_handle handle, u32 lvl, void *context, void **rv) return AE_OK; if (!acpi_match_device_ids(acpi_dev, video_ids)) { - dev = acpi_get_physical_pci_device(handle); + dev = acpi_get_pci_dev(handle); if (!dev) return AE_OK; - put_device(dev); + pci_dev_put(dev); *cap |= acpi_is_video_device(acpi_dev); } return AE_OK; diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 2aa1908e5ce..b17c57f8503 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -679,6 +679,14 @@ config PATA_PLATFORM If unsure, say N. +config PATA_AT91 + tristate "PATA support for AT91SAM9260" + depends on ARM && ARCH_AT91 + help + This option enables support for IDE devices on the Atmel AT91SAM9260 SoC. + + If unsure, say N. + config PATA_OF_PLATFORM tristate "OpenFirmware platform device PATA support" depends on PATA_PLATFORM && PPC_OF diff --git a/drivers/ata/Makefile b/drivers/ata/Makefile index 1558059874f..38906f9bbb4 100644 --- a/drivers/ata/Makefile +++ b/drivers/ata/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_PATA_SCH) += pata_sch.o obj-$(CONFIG_PATA_BF54X) += pata_bf54x.o obj-$(CONFIG_PATA_OCTEON_CF) += pata_octeon_cf.o obj-$(CONFIG_PATA_PLATFORM) += pata_platform.o +obj-$(CONFIG_PATA_AT91) += pata_at91.o obj-$(CONFIG_PATA_OF_PLATFORM) += pata_of_platform.o obj-$(CONFIG_PATA_ICSIDE) += pata_icside.o # Should be last but two libata driver diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index ca4d208ddf3..045a486a09e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -125,19 +125,19 @@ MODULE_PARM_DESC(force, "Force ATA configurations including cable type, link spe static int atapi_enabled = 1; module_param(atapi_enabled, int, 0444); -MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on)"); +MODULE_PARM_DESC(atapi_enabled, "Enable discovery of ATAPI devices (0=off, 1=on [default])"); static int atapi_dmadir = 0; module_param(atapi_dmadir, int, 0444); -MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off, 1=on)"); +MODULE_PARM_DESC(atapi_dmadir, "Enable ATAPI DMADIR bridge support (0=off [default], 1=on)"); int atapi_passthru16 = 1; module_param(atapi_passthru16, int, 0444); -MODULE_PARM_DESC(atapi_passthru16, "Enable ATA_16 passthru for ATAPI devices; on by default (0=off, 1=on)"); +MODULE_PARM_DESC(atapi_passthru16, "Enable ATA_16 passthru for ATAPI devices (0=off, 1=on [default])"); int libata_fua = 0; module_param_named(fua, libata_fua, int, 0444); -MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)"); +MODULE_PARM_DESC(fua, "FUA support (0=off [default], 1=on)"); static int ata_ignore_hpa; module_param_named(ignore_hpa, ata_ignore_hpa, int, 0644); @@ -153,11 +153,11 @@ MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)"); int libata_noacpi = 0; module_param_named(noacpi, libata_noacpi, int, 0444); -MODULE_PARM_DESC(noacpi, "Disables the use of ACPI in probe/suspend/resume when set"); +MODULE_PARM_DESC(noacpi, "Disable the use of ACPI in probe/suspend/resume (0=off [default], 1=on)"); int libata_allow_tpm = 0; module_param_named(allow_tpm, libata_allow_tpm, int, 0444); -MODULE_PARM_DESC(allow_tpm, "Permit the use of TPM commands"); +MODULE_PARM_DESC(allow_tpm, "Permit the use of TPM commands (0=off [default], 1=on)"); MODULE_AUTHOR("Jeff Garzik"); MODULE_DESCRIPTION("Library module for ATA devices"); @@ -1993,11 +1993,17 @@ unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd) * Check if the current speed of the device requires IORDY. Used * by various controllers for chip configuration. */ - unsigned int ata_pio_need_iordy(const struct ata_device *adev) { - /* Controller doesn't support IORDY. Probably a pointless check - as the caller should know this */ + /* Don't set IORDY if we're preparing for reset. IORDY may + * lead to controller lock up on certain controllers if the + * port is not occupied. See bko#11703 for details. + */ + if (adev->link->ap->pflags & ATA_PFLAG_RESETTING) + return 0; + /* Controller doesn't support IORDY. Probably a pointless + * check as the caller should know this. + */ if (adev->link->ap->flags & ATA_FLAG_NO_IORDY) return 0; /* CF spec. r4.1 Table 22 says no iordy on PIO5 and PIO6. */ @@ -2020,7 +2026,6 @@ unsigned int ata_pio_need_iordy(const struct ata_device *adev) * Compute the highest mode possible if we are not using iordy. Return * -1 if no iordy mode is available. */ - static u32 ata_pio_mask_no_iordy(const struct ata_device *adev) { /* If we have no drive specific rule, then PIO 2 is non IORDY */ diff --git a/drivers/ata/pata_at91.c b/drivers/ata/pata_at91.c new file mode 100644 index 00000000000..4b27617be26 --- /dev/null +++ b/drivers/ata/pata_at91.c @@ -0,0 +1,361 @@ +/* + * PATA driver for AT91SAM9260 Static Memory Controller + * with CompactFlash interface in True IDE mode + * + * Copyright (C) 2009 Matyukevich Sergey + * + * Based on: + * * generic platform driver by Paul Mundt: drivers/ata/pata_platform.c + * * pata_at32 driver by Kristoffer Nyborg Gregertsen + * * at91_ide driver by Stanislaw Gruszka + * + * 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/blkdev.h> +#include <scsi/scsi_host.h> +#include <linux/ata.h> +#include <linux/clk.h> +#include <linux/libata.h> +#include <linux/platform_device.h> +#include <linux/ata_platform.h> + +#include <mach/at91sam9260_matrix.h> +#include <mach/at91sam9_smc.h> +#include <mach/at91sam9260.h> +#include <mach/board.h> +#include <mach/gpio.h> + + +#define DRV_NAME "pata_at91" +#define DRV_VERSION "0.1" + +#define CF_IDE_OFFSET 0x00c00000 +#define CF_ALT_IDE_OFFSET 0x00e00000 +#define CF_IDE_RES_SIZE 0x08 + +struct at91_ide_info { + unsigned long mode; + unsigned int cs; + + void __iomem *ide_addr; + void __iomem *alt_addr; +}; + +const struct ata_timing initial_timing = + {XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0}; + +static unsigned int calc_mck_cycles(unsigned int ns, unsigned int mck_hz) +{ + unsigned long mul; + + /* + * cycles = x [nsec] * f [Hz] / 10^9 [ns in sec] = + * x * (f / 1_000_000_000) = + * x * ((f * 65536) / 1_000_000_000) / 65536 = + * x * (((f / 10_000) * 65536) / 100_000) / 65536 = + */ + + mul = (mck_hz / 10000) << 16; + mul /= 100000; + + return (ns * mul + 65536) >> 16; /* rounding */ +} + +static void set_smc_mode(struct at91_ide_info *info) +{ + at91_sys_write(AT91_SMC_MODE(info->cs), info->mode); + return; +} + +static void set_smc_timing(struct device *dev, + struct at91_ide_info *info, const struct ata_timing *ata) +{ + int read_cycle, write_cycle, active, recover; + int nrd_setup, nrd_pulse, nrd_recover; + int nwe_setup, nwe_pulse; + + int ncs_write_setup, ncs_write_pulse; + int ncs_read_setup, ncs_read_pulse; + + unsigned int mck_hz; + struct clk *mck; + + read_cycle = ata->cyc8b; + nrd_setup = ata->setup; + nrd_pulse = ata->act8b; + nrd_recover = ata->rec8b; + + mck = clk_get(NULL, "mck"); + BUG_ON(IS_ERR(mck)); + mck_hz = clk_get_rate(mck); + + read_cycle = calc_mck_cycles(read_cycle, mck_hz); + nrd_setup = calc_mck_cycles(nrd_setup, mck_hz); + nrd_pulse = calc_mck_cycles(nrd_pulse, mck_hz); + nrd_recover = calc_mck_cycles(nrd_recover, mck_hz); + + clk_put(mck); + + active = nrd_setup + nrd_pulse; + recover = read_cycle - active; + + /* Need at least two cycles recovery */ + if (recover < 2) + read_cycle = active + 2; + + /* (CS0, CS1, DIR, OE) <= (CFCE1, CFCE2, CFRNW, NCSX) timings */ + ncs_read_setup = 1; + ncs_read_pulse = read_cycle - 2; + + /* Write timings same as read timings */ + write_cycle = read_cycle; + nwe_setup = nrd_setup; + nwe_pulse = nrd_pulse; + ncs_write_setup = ncs_read_setup; + ncs_write_pulse = ncs_read_pulse; + + dev_dbg(dev, "ATA timings: nrd_setup = %d nrd_pulse = %d nrd_cycle = %d\n", + nrd_setup, nrd_pulse, read_cycle); + dev_dbg(dev, "ATA timings: nwe_setup = %d nwe_pulse = %d nwe_cycle = %d\n", + nwe_setup, nwe_pulse, write_cycle); + dev_dbg(dev, "ATA timings: ncs_read_setup = %d ncs_read_pulse = %d\n", + ncs_read_setup, ncs_read_pulse); + dev_dbg(dev, "ATA timings: ncs_write_setup = %d ncs_write_pulse = %d\n", + ncs_write_setup, ncs_write_pulse); + + at91_sys_write(AT91_SMC_SETUP(info->cs), + AT91_SMC_NWESETUP_(nwe_setup) | + AT91_SMC_NRDSETUP_(nrd_setup) | + AT91_SMC_NCS_WRSETUP_(ncs_write_setup) | + AT91_SMC_NCS_RDSETUP_(ncs_read_setup)); + + at91_sys_write(AT91_SMC_PULSE(info->cs), + AT91_SMC_NWEPULSE_(nwe_pulse) | + AT91_SMC_NRDPULSE_(nrd_pulse) | + AT91_SMC_NCS_WRPULSE_(ncs_write_pulse) | + AT91_SMC_NCS_RDPULSE_(ncs_read_pulse)); + + at91_sys_write(AT91_SMC_CYCLE(info->cs), + AT91_SMC_NWECYCLE_(write_cycle) | + AT91_SMC_NRDCYCLE_(read_cycle)); + + return; +} + +static void pata_at91_set_piomode(struct ata_port *ap, struct ata_device *adev) +{ + struct at91_ide_info *info = ap->host->private_data; + struct ata_timing timing; + int ret; + + /* Compute ATA timing and set it to SMC */ + ret = ata_timing_compute(adev, adev->pio_mode, &timing, 1000, 0); + if (ret) { + dev_warn(ap->dev, "Failed to compute ATA timing %d, \ + set PIO_0 timing\n", ret); + set_smc_timing(ap->dev, info, &initial_timing); + } else { + set_smc_timing(ap->dev, info, &timing); + } + + /* Setup SMC mode */ + set_smc_mode(info); + + return; +} + +static unsigned int pata_at91_data_xfer_noirq(struct ata_device *dev, + unsigned char *buf, unsigned int buflen, int rw) +{ + struct at91_ide_info *info = dev->link->ap->host->private_data; + unsigned int consumed; + unsigned long flags; + unsigned int mode; + + local_irq_save(flags); + mode = at91_sys_read(AT91_SMC_MODE(info->cs)); + + /* set 16bit mode before writing data */ + at91_sys_write(AT91_SMC_MODE(info->cs), + (mode & ~AT91_SMC_DBW) | AT91_SMC_DBW_16); + + consumed = ata_sff_data_xfer(dev, buf, buflen, rw); + + /* restore 8bit mode after data is written */ + at91_sys_write(AT91_SMC_MODE(info->cs), + (mode & ~AT91_SMC_DBW) | AT91_SMC_DBW_8); + + local_irq_restore(flags); + return consumed; +} + +static struct scsi_host_template pata_at91_sht = { + ATA_PIO_SHT(DRV_NAME), +}; + +static struct ata_port_operations pata_at91_port_ops = { + .inherits = &ata_sff_port_ops, + + .sff_data_xfer = pata_at91_data_xfer_noirq, + .set_piomode = pata_at91_set_piomode, + .cable_detect = ata_cable_40wire, + .port_start = ATA_OP_NULL, +}; + +static int __devinit pata_at91_probe(struct platform_device *pdev) +{ + struct at91_cf_data *board = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct at91_ide_info *info; + struct resource *mem_res; + struct ata_host *host; + struct ata_port *ap; + int irq_flags = 0; + int irq = 0; + int ret; + + /* get platform resources: IO/CTL memories and irq/rst pins */ + + if (pdev->num_resources != 1) { + dev_err(&pdev->dev, "invalid number of resources\n"); + return -EINVAL; + } + + mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!mem_res) { + dev_err(dev, "failed to get mem resource\n"); + return -EINVAL; + } + + irq = board->irq_pin; + + /* init ata host */ + + host = ata_host_alloc(dev, 1); + + if (!host) + return -ENOMEM; + + ap = host->ports[0]; + ap->ops = &pata_at91_port_ops; + ap->flags |= ATA_FLAG_SLAVE_POSS; + ap->pio_mask = ATA_PIO4; + + if (!irq) { + ap->flags |= ATA_FLAG_PIO_POLLING; + ata_port_desc(ap, "no IRQ, using PIO polling"); + } + + info = kzalloc(sizeof(*info), GFP_KERNEL); + + if (!info) { + dev_err(dev, "failed to allocate memory for private data\n"); + return -ENOMEM; + } + + info->cs = board->chipselect; + info->mode = AT91_SMC_READMODE | AT91_SMC_WRITEMODE | + AT91_SMC_EXNWMODE_READY | AT91_SMC_BAT_SELECT | + AT91_SMC_DBW_8 | AT91_SMC_TDF_(0); + + info->ide_addr = devm_ioremap(dev, + mem_res->start + CF_IDE_OFFSET, CF_IDE_RES_SIZE); + + if (!info->ide_addr) { + dev_err(dev, "failed to map IO base\n"); + ret = -ENOMEM; + goto err_ide_ioremap; + } + + info->alt_addr = devm_ioremap(dev, + mem_res->start + CF_ALT_IDE_OFFSET, CF_IDE_RES_SIZE); + + if (!info->alt_addr) { + dev_err(dev, "failed to map CTL base\n"); + ret = -ENOMEM; + goto err_alt_ioremap; + } + + ap->ioaddr.cmd_addr = info->ide_addr; + ap->ioaddr.ctl_addr = info->alt_addr + 0x06; + ap->ioaddr.altstatus_addr = ap->ioaddr.ctl_addr; + + ata_sff_std_ports(&ap->ioaddr); + + ata_port_desc(ap, "mmio cmd 0x%llx ctl 0x%llx", + (unsigned long long)mem_res->start + CF_IDE_OFFSET, + (unsigned long long)mem_res->start + CF_ALT_IDE_OFFSET); + + host->private_data = info; + + return ata_host_activate(host, irq ? gpio_to_irq(irq) : 0, + irq ? ata_sff_interrupt : NULL, + irq_flags, &pata_at91_sht); + +err_alt_ioremap: + devm_iounmap(dev, info->ide_addr); + +err_ide_ioremap: + kfree(info); + + return ret; +} + +static int __devexit pata_at91_remove(struct platform_device *pdev) +{ + struct ata_host *host = dev_get_drvdata(&pdev->dev); + struct at91_ide_info *info = host->private_data; + struct device *dev = &pdev->dev; + + if (!host) + return 0; + + ata_host_detach(host); + + if (!info) + return 0; + + devm_iounmap(dev, info->ide_addr); + devm_iounmap(dev, info->alt_addr); + + kfree(info); + return 0; +} + +static struct platform_driver pata_at91_driver = { + .probe = pata_at91_probe, + .remove = __devexit_p(pata_at91_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init pata_at91_init(void) +{ + return platform_driver_register(&pata_at91_driver); +} + +static void __exit pata_at91_exit(void) +{ + platform_driver_unregister(&pata_at91_driver); +} + + +module_init(pata_at91_init); +module_exit(pata_at91_exit); + + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Driver for CF in True IDE mode on AT91SAM9260 SoC"); +MODULE_AUTHOR("Matyukevich Sergey"); +MODULE_VERSION(DRV_VERSION); + diff --git a/drivers/ata/sata_fsl.c b/drivers/ata/sata_fsl.c index 36b8629203b..94eaa432c40 100644 --- a/drivers/ata/sata_fsl.c +++ b/drivers/ata/sata_fsl.c @@ -1378,6 +1378,37 @@ static int sata_fsl_remove(struct of_device *ofdev) return 0; } +#ifdef CONFIG_PM +static int sata_fsl_suspend(struct of_device *op, pm_message_t state) +{ + struct ata_host *host = dev_get_drvdata(&op->dev); + return ata_host_suspend(host, state); +} + +static int sata_fsl_resume(struct of_device *op) +{ + struct ata_host *host = dev_get_drvdata(&op->dev); + struct sata_fsl_host_priv *host_priv = host->private_data; + int ret; + void __iomem *hcr_base = host_priv->hcr_base; + struct ata_port *ap = host->ports[0]; + struct sata_fsl_port_priv *pp = ap->private_data; + + ret = sata_fsl_init_controller(host); + if (ret) { + dev_printk(KERN_ERR, &op->dev, + "Error initialize hardware\n"); + return ret; + } + + /* Recovery the CHBA register in host controller cmd register set */ + iowrite32(pp->cmdslot_paddr & 0xffffffff, hcr_base + CHBA); + + ata_host_resume(host); + return 0; +} +#endif + static struct of_device_id fsl_sata_match[] = { { .compatible = "fsl,pq-sata", @@ -1392,6 +1423,10 @@ static struct of_platform_driver fsl_sata_driver = { .match_table = fsl_sata_match, .probe = sata_fsl_probe, .remove = sata_fsl_remove, +#ifdef CONFIG_PM + .suspend = sata_fsl_suspend, + .resume = sata_fsl_resume, +#endif }; static int __init sata_fsl_init(void) diff --git a/drivers/char/bfin_jtag_comm.c b/drivers/char/bfin_jtag_comm.c index 44c113d5604..1d7c34c73b2 100644 --- a/drivers/char/bfin_jtag_comm.c +++ b/drivers/char/bfin_jtag_comm.c @@ -8,6 +8,10 @@ * Licensed under the GPL-2 or later. */ +#define DRV_NAME "bfin-jtag-comm" +#define DEV_NAME "ttyBFJC" +#define pr_fmt(fmt) DRV_NAME ": " fmt + #include <linux/circ_buf.h> #include <linux/console.h> #include <linux/delay.h> @@ -22,18 +26,14 @@ #include <linux/tty_flip.h> #include <asm/atomic.h> +#define pr_init(fmt, args...) ({ static const __initconst char __fmt[] = fmt; printk(__fmt, ## args); }) + /* See the Debug/Emulation chapter in the HRM */ #define EMUDOF 0x00000001 /* EMUDAT_OUT full & valid */ #define EMUDIF 0x00000002 /* EMUDAT_IN full & valid */ #define EMUDOOVF 0x00000004 /* EMUDAT_OUT overflow */ #define EMUDIOVF 0x00000008 /* EMUDAT_IN overflow */ -#define DRV_NAME "bfin-jtag-comm" -#define DEV_NAME "ttyBFJC" - -#define pr_init(fmt, args...) ({ static const __initdata char __fmt[] = fmt; printk(__fmt, ## args); }) -#define debug(fmt, args...) pr_debug(DRV_NAME ": " fmt, ## args) - static inline uint32_t bfin_write_emudat(uint32_t emudat) { __asm__ __volatile__("emudat = %0;" : : "d"(emudat)); @@ -74,7 +74,7 @@ bfin_jc_emudat_manager(void *arg) while (!kthread_should_stop()) { /* no one left to give data to, so sleep */ if (bfin_jc_tty == NULL && circ_empty(&bfin_jc_write_buf)) { - debug("waiting for readers\n"); + pr_debug("waiting for readers\n"); __set_current_state(TASK_UNINTERRUPTIBLE); schedule(); __set_current_state(TASK_RUNNING); @@ -82,7 +82,7 @@ bfin_jc_emudat_manager(void *arg) /* no data available, so just chill */ if (!(bfin_read_DBGSTAT() & EMUDIF) && circ_empty(&bfin_jc_write_buf)) { - debug("waiting for data (in_len = %i) (circ: %i %i)\n", + pr_debug("waiting for data (in_len = %i) (circ: %i %i)\n", inbound_len, bfin_jc_write_buf.tail, bfin_jc_write_buf.head); if (inbound_len) schedule(); @@ -99,11 +99,11 @@ bfin_jc_emudat_manager(void *arg) if (tty != NULL) { uint32_t emudat = bfin_read_emudat(); if (inbound_len == 0) { - debug("incoming length: 0x%08x\n", emudat); + pr_debug("incoming length: 0x%08x\n", emudat); inbound_len = emudat; } else { size_t num_chars = (4 <= inbound_len ? 4 : inbound_len); - debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars); + pr_debug(" incoming data: 0x%08x (pushing %zu)\n", emudat, num_chars); inbound_len -= num_chars; tty_insert_flip_string(tty, (unsigned char *)&emudat, num_chars); tty_flip_buffer_push(tty); @@ -117,7 +117,7 @@ bfin_jc_emudat_manager(void *arg) if (outbound_len == 0) { outbound_len = circ_cnt(&bfin_jc_write_buf); bfin_write_emudat(outbound_len); - debug("outgoing length: 0x%08x\n", outbound_len); + pr_debug("outgoing length: 0x%08x\n", outbound_len); } else { struct tty_struct *tty; int tail = bfin_jc_write_buf.tail; @@ -136,7 +136,7 @@ bfin_jc_emudat_manager(void *arg) if (tty) tty_wakeup(tty); mutex_unlock(&bfin_jc_tty_mutex); - debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate); + pr_debug(" outgoing data: 0x%08x (pushing %zu)\n", emudat, ate); } } } @@ -149,7 +149,7 @@ static int bfin_jc_open(struct tty_struct *tty, struct file *filp) { mutex_lock(&bfin_jc_tty_mutex); - debug("open %lu\n", bfin_jc_count); + pr_debug("open %lu\n", bfin_jc_count); ++bfin_jc_count; bfin_jc_tty = tty; wake_up_process(bfin_jc_kthread); @@ -161,7 +161,7 @@ static void bfin_jc_close(struct tty_struct *tty, struct file *filp) { mutex_lock(&bfin_jc_tty_mutex); - debug("close %lu\n", bfin_jc_count); + pr_debug("close %lu\n", bfin_jc_count); if (--bfin_jc_count == 0) bfin_jc_tty = NULL; wake_up_process(bfin_jc_kthread); @@ -174,7 +174,7 @@ bfin_jc_circ_write(const unsigned char *buf, int count) { int i; count = min(count, circ_free(&bfin_jc_write_buf)); - debug("going to write chunk of %i bytes\n", count); + pr_debug("going to write chunk of %i bytes\n", count); for (i = 0; i < count; ++i) circ_byte(&bfin_jc_write_buf, bfin_jc_write_buf.head + i) = buf[i]; bfin_jc_write_buf.head += i; diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c index 6799588b009..65b6ff2442c 100644 --- a/drivers/char/moxa.c +++ b/drivers/char/moxa.c @@ -1189,11 +1189,6 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) return -ENODEV; } - if (port % MAX_PORTS_PER_BOARD >= brd->numPorts) { - retval = -ENODEV; - goto out_unlock; - } - ch = &brd->ports[port % MAX_PORTS_PER_BOARD]; ch->port.count++; tty->driver_data = ch; @@ -1218,8 +1213,8 @@ static int moxa_open(struct tty_struct *tty, struct file *filp) moxa_close_port(tty); } else ch->port.flags |= ASYNC_NORMAL_ACTIVE; -out_unlock: mutex_unlock(&moxa_openlock); + return retval; } diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c index 9533f43a30b..52d953eb30c 100644 --- a/drivers/char/mxser.c +++ b/drivers/char/mxser.c @@ -1048,8 +1048,6 @@ static int mxser_open(struct tty_struct *tty, struct file *filp) if (retval) return retval; - /* unmark here for very high baud rate (ex. 921600 bps) used */ - tty->low_latency = 1; return 0; } diff --git a/drivers/char/n_hdlc.c b/drivers/char/n_hdlc.c index 461ece591a5..1c43c8cdee2 100644 --- a/drivers/char/n_hdlc.c +++ b/drivers/char/n_hdlc.c @@ -10,7 +10,6 @@ * Paul Mackerras <Paul.Mackerras@cs.anu.edu.au> * * Original release 01/11/99 - * $Id: n_hdlc.c,v 4.8 2003/05/06 21:18:51 paulkf Exp $ * * This code is released under the GNU General Public License (GPL) * @@ -79,7 +78,6 @@ */ #define HDLC_MAGIC 0x239e -#define HDLC_VERSION "$Revision: 4.8 $" #include <linux/module.h> #include <linux/init.h> @@ -114,7 +112,7 @@ #define MAX_HDLC_FRAME_SIZE 65535 #define DEFAULT_RX_BUF_COUNT 10 #define MAX_RX_BUF_COUNT 60 -#define DEFAULT_TX_BUF_COUNT 1 +#define DEFAULT_TX_BUF_COUNT 3 struct n_hdlc_buf { struct n_hdlc_buf *link; @@ -199,6 +197,31 @@ static void n_hdlc_tty_wakeup(struct tty_struct *tty); #define tty2n_hdlc(tty) ((struct n_hdlc *) ((tty)->disc_data)) #define n_hdlc2tty(n_hdlc) ((n_hdlc)->tty) +static void flush_rx_queue(struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc(tty); + struct n_hdlc_buf *buf; + + while ((buf = n_hdlc_buf_get(&n_hdlc->rx_buf_list))) + n_hdlc_buf_put(&n_hdlc->rx_free_buf_list, buf); +} + +static void flush_tx_queue(struct tty_struct *tty) +{ + struct n_hdlc *n_hdlc = tty2n_hdlc(tty); + struct n_hdlc_buf *buf; + unsigned long flags; + + while ((buf = n_hdlc_buf_get(&n_hdlc->tx_buf_list))) + n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, buf); + spin_lock_irqsave(&n_hdlc->tx_buf_list.spinlock, flags); + if (n_hdlc->tbuf) { + n_hdlc_buf_put(&n_hdlc->tx_free_buf_list, n_hdlc->tbuf); + n_hdlc->tbuf = NULL; + } + spin_unlock_irqrestore(&n_hdlc->tx_buf_list.spinlock, flags); +} + static struct tty_ldisc_ops n_hdlc_ldisc = { .owner = THIS_MODULE, .magic = TTY_LDISC_MAGIC, @@ -211,6 +234,7 @@ static struct tty_ldisc_ops n_hdlc_ldisc = { .poll = n_hdlc_tty_poll, .receive_buf = n_hdlc_tty_receive, .write_wakeup = n_hdlc_tty_wakeup, + .flush_buffer = flush_rx_queue, }; /** @@ -341,10 +365,7 @@ static int n_hdlc_tty_open (struct tty_struct *tty) set_bit(TTY_NO_WRITE_SPLIT,&tty->flags); #endif - /* Flush any pending characters in the driver and discipline. */ - if (tty->ldisc->ops->flush_buffer) - tty->ldisc->ops->flush_buffer(tty); - + /* flush receive data from driver */ tty_driver_flush_buffer(tty); if (debuglevel >= DEBUG_LEVEL_INFO) @@ -763,6 +784,14 @@ static int n_hdlc_tty_ioctl(struct tty_struct *tty, struct file *file, error = put_user(count, (int __user *)arg); break; + case TCFLSH: + switch (arg) { + case TCIOFLUSH: + case TCOFLUSH: + flush_tx_queue(tty); + } + /* fall through to default */ + default: error = n_tty_ioctl_helper(tty, file, cmd, arg); break; @@ -919,8 +948,7 @@ static struct n_hdlc_buf* n_hdlc_buf_get(struct n_hdlc_buf_list *list) } /* end of n_hdlc_buf_get() */ static char hdlc_banner[] __initdata = - KERN_INFO "HDLC line discipline: version " HDLC_VERSION - ", maxframe=%u\n"; + KERN_INFO "HDLC line discipline maxframe=%u\n"; static char hdlc_register_ok[] __initdata = KERN_INFO "N_HDLC line discipline registered.\n"; static char hdlc_register_fail[] __initdata = diff --git a/drivers/char/n_r3964.c b/drivers/char/n_r3964.c index d2e93e34322..2e99158ebb8 100644 --- a/drivers/char/n_r3964.c +++ b/drivers/char/n_r3964.c @@ -1062,7 +1062,7 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, struct r3964_client_info *pClient; struct r3964_message *pMsg; struct r3964_client_message theMsg; - int count; + int ret; TRACE_L("read()"); @@ -1074,8 +1074,8 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, if (pMsg == NULL) { /* no messages available. */ if (file->f_flags & O_NONBLOCK) { - unlock_kernel(); - return -EAGAIN; + ret = -EAGAIN; + goto unlock; } /* block until there is a message: */ wait_event_interruptible(pInfo->read_wait, @@ -1085,29 +1085,31 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, /* If we still haven't got a message, we must have been signalled */ if (!pMsg) { - unlock_kernel(); - return -EINTR; + ret = -EINTR; + goto unlock; } /* deliver msg to client process: */ theMsg.msg_id = pMsg->msg_id; theMsg.arg = pMsg->arg; theMsg.error_code = pMsg->error_code; - count = sizeof(struct r3964_client_message); + ret = sizeof(struct r3964_client_message); kfree(pMsg); TRACE_M("r3964_read - msg kfree %p", pMsg); - if (copy_to_user(buf, &theMsg, count)) { - unlock_kernel(); - return -EFAULT; + if (copy_to_user(buf, &theMsg, ret)) { + ret = -EFAULT; + goto unlock; } - TRACE_PS("read - return %d", count); - return count; + TRACE_PS("read - return %d", ret); + goto unlock; } + ret = -EPERM; +unlock: unlock_kernel(); - return -EPERM; + return ret; } static ssize_t r3964_write(struct tty_struct *tty, struct file *file, diff --git a/drivers/char/nozomi.c b/drivers/char/nozomi.c index d6102b644b5..574f1c79b6e 100644 --- a/drivers/char/nozomi.c +++ b/drivers/char/nozomi.c @@ -1591,8 +1591,6 @@ static int ntty_open(struct tty_struct *tty, struct file *file) /* Enable interrupt downlink for channel */ if (port->port.count == 1) { - /* FIXME: is this needed now ? */ - tty->low_latency = 1; tty->driver_data = port; tty_port_tty_set(&port->port, tty); DBG1("open: %d", port->token_dl); diff --git a/drivers/char/pcmcia/cm4000_cs.c b/drivers/char/pcmcia/cm4000_cs.c index dbb91257456..881934c068c 100644 --- a/drivers/char/pcmcia/cm4000_cs.c +++ b/drivers/char/pcmcia/cm4000_cs.c @@ -1575,7 +1575,8 @@ static long cmm_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) clear_bit(LOCK_IO, &dev->flags); wake_up_interruptible(&dev->ioq); - return 0; + rc = 0; + break; case CM_IOCSPTS: { struct ptsreq krnptsreq; diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c index 1386625fc4c..a2e67e6df3a 100644 --- a/drivers/char/synclink_gt.c +++ b/drivers/char/synclink_gt.c @@ -467,7 +467,6 @@ static unsigned int free_tbuf_count(struct slgt_info *info); static unsigned int tbuf_bytes(struct slgt_info *info); static void reset_tbufs(struct slgt_info *info); static void tdma_reset(struct slgt_info *info); -static void tdma_start(struct slgt_info *info); static void tx_load(struct slgt_info *info, const char *buf, unsigned int count); static void get_signals(struct slgt_info *info); @@ -795,6 +794,18 @@ static void set_termios(struct tty_struct *tty, struct ktermios *old_termios) } } +static void update_tx_timer(struct slgt_info *info) +{ + /* + * use worst case speed of 1200bps to calculate transmit timeout + * based on data in buffers (tbuf_bytes) and FIFO (128 bytes) + */ + if (info->params.mode == MGSL_MODE_HDLC) { + int timeout = (tbuf_bytes(info) * 7) + 1000; + mod_timer(&info->tx_timer, jiffies + msecs_to_jiffies(timeout)); + } +} + static int write(struct tty_struct *tty, const unsigned char *buf, int count) { @@ -838,8 +849,18 @@ start: spin_lock_irqsave(&info->lock,flags); if (!info->tx_active) tx_start(info); - else - tdma_start(info); + else if (!(rd_reg32(info, TDCSR) & BIT0)) { + /* transmit still active but transmit DMA stopped */ + unsigned int i = info->tbuf_current; + if (!i) + i = info->tbuf_count; + i--; + /* if DMA buf unsent must try later after tx idle */ + if (desc_count(info->tbufs[i])) + ret = 0; + } + if (ret > 0) + update_tx_timer(info); spin_unlock_irqrestore(&info->lock,flags); } @@ -1502,10 +1523,9 @@ static int hdlcdev_xmit(struct sk_buff *skb, struct net_device *dev) /* save start time for transmit timeout detection */ dev->trans_start = jiffies; - /* start hardware transmitter if necessary */ spin_lock_irqsave(&info->lock,flags); - if (!info->tx_active) - tx_start(info); + tx_start(info); + update_tx_timer(info); spin_unlock_irqrestore(&info->lock,flags); return 0; @@ -3946,50 +3966,19 @@ static void tx_start(struct slgt_info *info) slgt_irq_on(info, IRQ_TXUNDER + IRQ_TXIDLE); /* clear tx idle and underrun status bits */ wr_reg16(info, SSR, (unsigned short)(IRQ_TXIDLE + IRQ_TXUNDER)); - if (info->params.mode == MGSL_MODE_HDLC) - mod_timer(&info->tx_timer, jiffies + - msecs_to_jiffies(5000)); } else { slgt_irq_off(info, IRQ_TXDATA); slgt_irq_on(info, IRQ_TXIDLE); /* clear tx idle status bit */ wr_reg16(info, SSR, IRQ_TXIDLE); } - tdma_start(info); + /* set 1st descriptor address and start DMA */ + wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); + wr_reg32(info, TDCSR, BIT2 + BIT0); info->tx_active = true; } } -/* - * start transmit DMA if inactive and there are unsent buffers - */ -static void tdma_start(struct slgt_info *info) -{ - unsigned int i; - - if (rd_reg32(info, TDCSR) & BIT0) - return; - - /* transmit DMA inactive, check for unsent buffers */ - i = info->tbuf_start; - while (!desc_count(info->tbufs[i])) { - if (++i == info->tbuf_count) - i = 0; - if (i == info->tbuf_current) - return; - } - info->tbuf_start = i; - - /* there are unsent buffers, start transmit DMA */ - - /* reset needed if previous error condition */ - tdma_reset(info); - - /* set 1st descriptor address */ - wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc); - wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */ -} - static void tx_stop(struct slgt_info *info) { unsigned short val; @@ -5004,8 +4993,7 @@ static void tx_timeout(unsigned long context) info->icount.txtimeout++; } spin_lock_irqsave(&info->lock,flags); - info->tx_active = false; - info->tx_count = 0; + tx_stop(info); spin_unlock_irqrestore(&info->lock,flags); #if SYNCLINK_GENERIC_HDLC diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c index 62dadfc95e3..4e862a75f7f 100644 --- a/drivers/char/tty_port.c +++ b/drivers/char/tty_port.c @@ -193,7 +193,7 @@ int tty_port_block_til_ready(struct tty_port *port, { int do_clocal = 0, retval; unsigned long flags; - DECLARE_WAITQUEUE(wait, current); + DEFINE_WAIT(wait); int cd; /* block if port is in the process of being closed */ diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c index e6ce632a393..7539bed0f7e 100644 --- a/drivers/char/vt_ioctl.c +++ b/drivers/char/vt_ioctl.c @@ -396,7 +396,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file, kbd = kbd_table + console; switch (cmd) { case TIOCLINUX: - return tioclinux(tty, arg); + ret = tioclinux(tty, arg); + break; case KIOCSOUND: if (!perm) goto eperm; diff --git a/drivers/firewire/Kconfig b/drivers/firewire/Kconfig index 45090243820..13efcd36207 100644 --- a/drivers/firewire/Kconfig +++ b/drivers/firewire/Kconfig @@ -1,28 +1,29 @@ -comment "A new alternative FireWire stack is available with EXPERIMENTAL=y" - depends on EXPERIMENTAL=n - -comment "Enable only one of the two stacks, unless you know what you are doing" - depends on EXPERIMENTAL +comment "You can enable one or both FireWire driver stacks." +comment "See the help texts for more information." config FIREWIRE - tristate "New FireWire stack, EXPERIMENTAL" - depends on EXPERIMENTAL + tristate "FireWire driver stack" select CRC_ITU_T help - This is the "Juju" FireWire stack, a new alternative implementation - designed for robustness and simplicity. You can build either this - stack, or the old stack (the ieee1394 driver, ohci1394 etc.) or both. - Please read http://ieee1394.wiki.kernel.org/index.php/Juju_Migration - before you enable the new stack. + This is the new-generation IEEE 1394 (FireWire) driver stack + a.k.a. Juju, a new implementation designed for robustness and + simplicity. + See http://ieee1394.wiki.kernel.org/index.php/Juju_Migration + for information about migration from the older Linux 1394 stack + to the new driver stack. To compile this driver as a module, say M here: the module will be called firewire-core. This module functionally replaces ieee1394, raw1394, and video1394. To access it from application programs, you generally need at least - libraw1394 version 2. IIDC/DCAM applications also need libdc1394 - version 2. No libraries are required to access storage devices - through the firewire-sbp2 driver. + libraw1394 v2. IIDC/DCAM applications need libdc1394 v2. + No libraries are required to access storage devices through the + firewire-sbp2 driver. + + NOTE: + FireWire audio devices currently require the old drivers (ieee1394, + ohci1394, raw1394). config FIREWIRE_OHCI tristate "OHCI-1394 controllers" @@ -37,11 +38,9 @@ config FIREWIRE_OHCI stack. NOTE: - - You should only build either firewire-ohci or the old ohci1394 driver, - but not both. If you nevertheless want to install both, you should - configure them only as modules and blacklist the driver(s) which you - don't want to have auto-loaded. Add either + If you want to install firewire-ohci and ohci1394 together, you + should configure them only as modules and blacklist the driver(s) + which you don't want to have auto-loaded. Add either blacklist firewire-ohci or @@ -50,12 +49,7 @@ config FIREWIRE_OHCI blacklist dv1394 to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf - depending on your distribution. The latter two modules should be - blacklisted together with ohci1394 because they depend on ohci1394. - - If you have an old modprobe which doesn't implement the blacklist - directive, use "install modulename /bin/true" for the modules to be - blacklisted. + depending on your distribution. config FIREWIRE_OHCI_DEBUG bool @@ -77,3 +71,17 @@ config FIREWIRE_SBP2 You should also enable support for disks, CD-ROMs, etc. in the SCSI configuration section. + +config FIREWIRE_NET + tristate "IP networking over 1394 (EXPERIMENTAL)" + depends on FIREWIRE && INET && EXPERIMENTAL + help + This enables IPv4 over IEEE 1394, providing IP connectivity with + other implementations of RFC 2734 as found on several operating + systems. Multicast support is currently limited. + + NOTE, this driver is not stable yet! + + To compile this driver as a module, say M here: The module will be + called firewire-net. It replaces eth1394 of the classic IEEE 1394 + stack. diff --git a/drivers/firewire/Makefile b/drivers/firewire/Makefile index bc3b9bf822b..a8f9bb6d9fd 100644 --- a/drivers/firewire/Makefile +++ b/drivers/firewire/Makefile @@ -6,7 +6,9 @@ firewire-core-y += core-card.o core-cdev.o core-device.o \ core-iso.o core-topology.o core-transaction.o firewire-ohci-y += ohci.o firewire-sbp2-y += sbp2.o +firewire-net-y += net.o -obj-$(CONFIG_FIREWIRE) += firewire-core.o +obj-$(CONFIG_FIREWIRE) += firewire-core.o obj-$(CONFIG_FIREWIRE_OHCI) += firewire-ohci.o obj-$(CONFIG_FIREWIRE_SBP2) += firewire-sbp2.o +obj-$(CONFIG_FIREWIRE_NET) += firewire-net.o diff --git a/drivers/firewire/core-card.c b/drivers/firewire/core-card.c index 4c1be64fddd..543fccac81b 100644 --- a/drivers/firewire/core-card.c +++ b/drivers/firewire/core-card.c @@ -176,6 +176,7 @@ int fw_core_add_descriptor(struct fw_descriptor *desc) return 0; } +EXPORT_SYMBOL(fw_core_add_descriptor); void fw_core_remove_descriptor(struct fw_descriptor *desc) { @@ -189,6 +190,7 @@ void fw_core_remove_descriptor(struct fw_descriptor *desc) mutex_unlock(&card_mutex); } +EXPORT_SYMBOL(fw_core_remove_descriptor); static void allocate_broadcast_channel(struct fw_card *card, int generation) { @@ -459,11 +461,11 @@ EXPORT_SYMBOL(fw_card_add); /* - * The next few functions implements a dummy driver that use once a - * card driver shuts down an fw_card. This allows the driver to - * cleanly unload, as all IO to the card will be handled by the dummy - * driver instead of calling into the (possibly) unloaded module. The - * dummy driver just fails all IO. + * The next few functions implement a dummy driver that is used once a card + * driver shuts down an fw_card. This allows the driver to cleanly unload, + * as all IO to the card will be handled (and failed) by the dummy driver + * instead of calling into the module. Only functions for iso context + * shutdown still need to be provided by the card driver. */ static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length) @@ -510,7 +512,7 @@ static int dummy_enable_phys_dma(struct fw_card *card, return -ENODEV; } -static struct fw_card_driver dummy_driver = { +static const struct fw_card_driver dummy_driver_template = { .enable = dummy_enable, .update_phy_reg = dummy_update_phy_reg, .set_config_rom = dummy_set_config_rom, @@ -529,6 +531,8 @@ void fw_card_release(struct kref *kref) void fw_core_remove_card(struct fw_card *card) { + struct fw_card_driver dummy_driver = dummy_driver_template; + card->driver->update_phy_reg(card, 4, PHY_LINK_ACTIVE | PHY_CONTENDER, 0); fw_core_initiate_bus_reset(card, 1); @@ -537,7 +541,9 @@ void fw_core_remove_card(struct fw_card *card) list_del_init(&card->link); mutex_unlock(&card_mutex); - /* Set up the dummy driver. */ + /* Switch off most of the card driver interface. */ + dummy_driver.free_iso_context = card->driver->free_iso_context; + dummy_driver.stop_iso = card->driver->stop_iso; card->driver = &dummy_driver; fw_destroy_nodes(card); diff --git a/drivers/firewire/core-iso.c b/drivers/firewire/core-iso.c index 28076c892d7..166f19c6d38 100644 --- a/drivers/firewire/core-iso.c +++ b/drivers/firewire/core-iso.c @@ -71,7 +71,7 @@ int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, for (j = 0; j < i; j++) { address = page_private(buffer->pages[j]); dma_unmap_page(card->device, address, - PAGE_SIZE, DMA_TO_DEVICE); + PAGE_SIZE, direction); __free_page(buffer->pages[j]); } kfree(buffer->pages); @@ -80,6 +80,7 @@ int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, return -ENOMEM; } +EXPORT_SYMBOL(fw_iso_buffer_init); int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma) { @@ -107,13 +108,14 @@ void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, for (i = 0; i < buffer->page_count; i++) { address = page_private(buffer->pages[i]); dma_unmap_page(card->device, address, - PAGE_SIZE, DMA_TO_DEVICE); + PAGE_SIZE, buffer->direction); __free_page(buffer->pages[i]); } kfree(buffer->pages); buffer->pages = NULL; } +EXPORT_SYMBOL(fw_iso_buffer_destroy); struct fw_iso_context *fw_iso_context_create(struct fw_card *card, int type, int channel, int speed, size_t header_size, @@ -136,6 +138,7 @@ struct fw_iso_context *fw_iso_context_create(struct fw_card *card, return ctx; } +EXPORT_SYMBOL(fw_iso_context_create); void fw_iso_context_destroy(struct fw_iso_context *ctx) { @@ -143,12 +146,14 @@ void fw_iso_context_destroy(struct fw_iso_context *ctx) card->driver->free_iso_context(ctx); } +EXPORT_SYMBOL(fw_iso_context_destroy); int fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags) { return ctx->card->driver->start_iso(ctx, cycle, sync, tags); } +EXPORT_SYMBOL(fw_iso_context_start); int fw_iso_context_queue(struct fw_iso_context *ctx, struct fw_iso_packet *packet, @@ -159,11 +164,13 @@ int fw_iso_context_queue(struct fw_iso_context *ctx, return card->driver->queue_iso(ctx, packet, buffer, payload); } +EXPORT_SYMBOL(fw_iso_context_queue); int fw_iso_context_stop(struct fw_iso_context *ctx) { return ctx->card->driver->stop_iso(ctx); } +EXPORT_SYMBOL(fw_iso_context_stop); /* * Isochronous bus resource management (channels, bandwidth), client side diff --git a/drivers/firewire/core.h b/drivers/firewire/core.h index 0a25a7b38a8..c3cfc647e5e 100644 --- a/drivers/firewire/core.h +++ b/drivers/firewire/core.h @@ -1,7 +1,6 @@ #ifndef _FIREWIRE_CORE_H #define _FIREWIRE_CORE_H -#include <linux/dma-mapping.h> #include <linux/fs.h> #include <linux/list.h> #include <linux/idr.h> @@ -97,17 +96,6 @@ 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); -struct fw_descriptor { - struct list_head link; - size_t length; - u32 immediate; - u32 key; - const u32 *data; -}; - -int fw_core_add_descriptor(struct fw_descriptor *desc); -void fw_core_remove_descriptor(struct fw_descriptor *desc); - /* -cdev */ @@ -130,77 +118,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event); /* -iso */ -/* - * The iso packet format allows for an immediate header/payload part - * stored in 'header' immediately after the packet info plus an - * indirect payload part that is pointer to by the 'payload' field. - * Applications can use one or the other or both to implement simple - * low-bandwidth streaming (e.g. audio) or more advanced - * scatter-gather streaming (e.g. assembling video frame automatically). - */ -struct fw_iso_packet { - u16 payload_length; /* Length of indirect payload. */ - u32 interrupt:1; /* Generate interrupt on this packet */ - u32 skip:1; /* Set to not send packet at all. */ - u32 tag:2; - u32 sy:4; - u32 header_length:8; /* Length of immediate header. */ - u32 header[0]; -}; - -#define FW_ISO_CONTEXT_TRANSMIT 0 -#define FW_ISO_CONTEXT_RECEIVE 1 - -#define FW_ISO_CONTEXT_MATCH_TAG0 1 -#define FW_ISO_CONTEXT_MATCH_TAG1 2 -#define FW_ISO_CONTEXT_MATCH_TAG2 4 -#define FW_ISO_CONTEXT_MATCH_TAG3 8 -#define FW_ISO_CONTEXT_MATCH_ALL_TAGS 15 - -/* - * An iso buffer is just a set of pages mapped for DMA in the - * specified direction. Since the pages are to be used for DMA, they - * are not mapped into the kernel virtual address space. We store the - * DMA address in the page private. The helper function - * fw_iso_buffer_map() will map the pages into a given vma. - */ -struct fw_iso_buffer { - enum dma_data_direction direction; - struct page **pages; - int page_count; -}; - -typedef void (*fw_iso_callback_t)(struct fw_iso_context *context, - u32 cycle, size_t header_length, - void *header, void *data); - -struct fw_iso_context { - struct fw_card *card; - int type; - int channel; - int speed; - size_t header_size; - fw_iso_callback_t callback; - void *callback_data; -}; - -int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card, - int page_count, enum dma_data_direction direction); int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma); -void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card); - -struct fw_iso_context *fw_iso_context_create(struct fw_card *card, - int type, int channel, int speed, size_t header_size, - fw_iso_callback_t callback, void *callback_data); -int fw_iso_context_queue(struct fw_iso_context *ctx, - struct fw_iso_packet *packet, - struct fw_iso_buffer *buffer, - unsigned long payload); -int fw_iso_context_start(struct fw_iso_context *ctx, - int cycle, int sync, int tags); -int fw_iso_context_stop(struct fw_iso_context *ctx); -void fw_iso_context_destroy(struct fw_iso_context *ctx); - void fw_iso_resource_manage(struct fw_card *card, int generation, u64 channels_mask, int *channel, int *bandwidth, bool allocate); @@ -285,9 +203,4 @@ void fw_flush_transactions(struct fw_card *card); void fw_send_phy_config(struct fw_card *card, int node_id, int generation, int gap_count); -static inline int fw_stream_packet_destination_id(int tag, int channel, int sy) -{ - return tag << 14 | channel << 8 | sy; -} - #endif /* _FIREWIRE_CORE_H */ diff --git a/drivers/firewire/net.c b/drivers/firewire/net.c new file mode 100644 index 00000000000..a42209a73ae --- /dev/null +++ b/drivers/firewire/net.c @@ -0,0 +1,1655 @@ +/* + * IPv4 over IEEE 1394, per RFC 2734 + * + * Copyright (C) 2009 Jay Fenlason <fenlason@redhat.com> + * + * based on eth1394 by Ben Collins et al + */ + +#include <linux/bug.h> +#include <linux/device.h> +#include <linux/ethtool.h> +#include <linux/firewire.h> +#include <linux/firewire-constants.h> +#include <linux/highmem.h> +#include <linux/in.h> +#include <linux/ip.h> +#include <linux/jiffies.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/mutex.h> +#include <linux/netdevice.h> +#include <linux/skbuff.h> +#include <linux/spinlock.h> + +#include <asm/unaligned.h> +#include <net/arp.h> + +#define FWNET_MAX_FRAGMENTS 25 /* arbitrary limit */ +#define FWNET_ISO_PAGE_COUNT (PAGE_SIZE < 16 * 1024 ? 4 : 2) + +#define IEEE1394_BROADCAST_CHANNEL 31 +#define IEEE1394_ALL_NODES (0xffc0 | 0x003f) +#define IEEE1394_MAX_PAYLOAD_S100 512 +#define FWNET_NO_FIFO_ADDR (~0ULL) + +#define IANA_SPECIFIER_ID 0x00005eU +#define RFC2734_SW_VERSION 0x000001U + +#define IEEE1394_GASP_HDR_SIZE 8 + +#define RFC2374_UNFRAG_HDR_SIZE 4 +#define RFC2374_FRAG_HDR_SIZE 8 +#define RFC2374_FRAG_OVERHEAD 4 + +#define RFC2374_HDR_UNFRAG 0 /* unfragmented */ +#define RFC2374_HDR_FIRSTFRAG 1 /* first fragment */ +#define RFC2374_HDR_LASTFRAG 2 /* last fragment */ +#define RFC2374_HDR_INTFRAG 3 /* interior fragment */ + +#define RFC2734_HW_ADDR_LEN 16 + +struct rfc2734_arp { + __be16 hw_type; /* 0x0018 */ + __be16 proto_type; /* 0x0806 */ + u8 hw_addr_len; /* 16 */ + u8 ip_addr_len; /* 4 */ + __be16 opcode; /* ARP Opcode */ + /* Above is exactly the same format as struct arphdr */ + + __be64 s_uniq_id; /* Sender's 64bit EUI */ + u8 max_rec; /* Sender's max packet size */ + u8 sspd; /* Sender's max speed */ + __be16 fifo_hi; /* hi 16bits of sender's FIFO addr */ + __be32 fifo_lo; /* lo 32bits of sender's FIFO addr */ + __be32 sip; /* Sender's IP Address */ + __be32 tip; /* IP Address of requested hw addr */ +} __attribute__((packed)); + +/* This header format is specific to this driver implementation. */ +#define FWNET_ALEN 8 +#define FWNET_HLEN 10 +struct fwnet_header { + u8 h_dest[FWNET_ALEN]; /* destination address */ + __be16 h_proto; /* packet type ID field */ +} __attribute__((packed)); + +/* IPv4 and IPv6 encapsulation header */ +struct rfc2734_header { + u32 w0; + u32 w1; +}; + +#define fwnet_get_hdr_lf(h) (((h)->w0 & 0xc0000000) >> 30) +#define fwnet_get_hdr_ether_type(h) (((h)->w0 & 0x0000ffff)) +#define fwnet_get_hdr_dg_size(h) (((h)->w0 & 0x0fff0000) >> 16) +#define fwnet_get_hdr_fg_off(h) (((h)->w0 & 0x00000fff)) +#define fwnet_get_hdr_dgl(h) (((h)->w1 & 0xffff0000) >> 16) + +#define fwnet_set_hdr_lf(lf) ((lf) << 30) +#define fwnet_set_hdr_ether_type(et) (et) +#define fwnet_set_hdr_dg_size(dgs) ((dgs) << 16) +#define fwnet_set_hdr_fg_off(fgo) (fgo) + +#define fwnet_set_hdr_dgl(dgl) ((dgl) << 16) + +static inline void fwnet_make_uf_hdr(struct rfc2734_header *hdr, + unsigned ether_type) +{ + hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_UNFRAG) + | fwnet_set_hdr_ether_type(ether_type); +} + +static inline void fwnet_make_ff_hdr(struct rfc2734_header *hdr, + unsigned ether_type, unsigned dg_size, unsigned dgl) +{ + hdr->w0 = fwnet_set_hdr_lf(RFC2374_HDR_FIRSTFRAG) + | fwnet_set_hdr_dg_size(dg_size) + | fwnet_set_hdr_ether_type(ether_type); + hdr->w1 = fwnet_set_hdr_dgl(dgl); +} + +static inline void fwnet_make_sf_hdr(struct rfc2734_header *hdr, + unsigned lf, unsigned dg_size, unsigned fg_off, unsigned dgl) +{ + hdr->w0 = fwnet_set_hdr_lf(lf) + | fwnet_set_hdr_dg_size(dg_size) + | fwnet_set_hdr_fg_off(fg_off); + hdr->w1 = fwnet_set_hdr_dgl(dgl); +} + +/* This list keeps track of what parts of the datagram have been filled in */ +struct fwnet_fragment_info { + struct list_head fi_link; + u16 offset; + u16 len; +}; + +struct fwnet_partial_datagram { + struct list_head pd_link; + struct list_head fi_list; + struct sk_buff *skb; + /* FIXME Why not use skb->data? */ + char *pbuf; + u16 datagram_label; + u16 ether_type; + u16 datagram_size; +}; + +static DEFINE_MUTEX(fwnet_device_mutex); +static LIST_HEAD(fwnet_device_list); + +struct fwnet_device { + struct list_head dev_link; + spinlock_t lock; + enum { + FWNET_BROADCAST_ERROR, + FWNET_BROADCAST_RUNNING, + FWNET_BROADCAST_STOPPED, + } broadcast_state; + struct fw_iso_context *broadcast_rcv_context; + struct fw_iso_buffer broadcast_rcv_buffer; + void **broadcast_rcv_buffer_ptrs; + unsigned broadcast_rcv_next_ptr; + unsigned num_broadcast_rcv_ptrs; + unsigned rcv_buffer_size; + /* + * This value is the maximum unfragmented datagram size that can be + * sent by the hardware. It already has the GASP overhead and the + * unfragmented datagram header overhead calculated into it. + */ + unsigned broadcast_xmt_max_payload; + u16 broadcast_xmt_datagramlabel; + + /* + * The CSR address that remote nodes must send datagrams to for us to + * receive them. + */ + struct fw_address_handler handler; + u64 local_fifo; + + /* List of packets to be sent */ + struct list_head packet_list; + /* + * List of packets that were broadcasted. When we get an ISO interrupt + * one of them has been sent + */ + struct list_head broadcasted_list; + /* List of packets that have been sent but not yet acked */ + struct list_head sent_list; + + struct list_head peer_list; + struct fw_card *card; + struct net_device *netdev; +}; + +struct fwnet_peer { + struct list_head peer_link; + struct fwnet_device *dev; + u64 guid; + u64 fifo; + + /* guarded by dev->lock */ + struct list_head pd_list; /* received partial datagrams */ + unsigned pdg_size; /* pd_list size */ + + u16 datagram_label; /* outgoing datagram label */ + unsigned max_payload; /* includes RFC2374_FRAG_HDR_SIZE overhead */ + int node_id; + int generation; + unsigned speed; +}; + +/* This is our task struct. It's used for the packet complete callback. */ +struct fwnet_packet_task { + /* + * ptask can actually be on dev->packet_list, dev->broadcasted_list, + * or dev->sent_list depending on its current state. + */ + struct list_head pt_link; + struct fw_transaction transaction; + struct rfc2734_header hdr; + struct sk_buff *skb; + struct fwnet_device *dev; + + int outstanding_pkts; + unsigned max_payload; + u64 fifo_addr; + u16 dest_node; + u8 generation; + u8 speed; +}; + +/* + * saddr == NULL means use device source address. + * daddr == NULL means leave destination address (eg unresolved arp). + */ +static int fwnet_header_create(struct sk_buff *skb, struct net_device *net, + unsigned short type, const void *daddr, + const void *saddr, unsigned len) +{ + struct fwnet_header *h; + + h = (struct fwnet_header *)skb_push(skb, sizeof(*h)); + put_unaligned_be16(type, &h->h_proto); + + if (net->flags & (IFF_LOOPBACK | IFF_NOARP)) { + memset(h->h_dest, 0, net->addr_len); + + return net->hard_header_len; + } + + if (daddr) { + memcpy(h->h_dest, daddr, net->addr_len); + + return net->hard_header_len; + } + + return -net->hard_header_len; +} + +static int fwnet_header_rebuild(struct sk_buff *skb) +{ + struct fwnet_header *h = (struct fwnet_header *)skb->data; + + if (get_unaligned_be16(&h->h_proto) == ETH_P_IP) + return arp_find((unsigned char *)&h->h_dest, skb); + + fw_notify("%s: unable to resolve type %04x addresses\n", + skb->dev->name, be16_to_cpu(h->h_proto)); + return 0; +} + +static int fwnet_header_cache(const struct neighbour *neigh, + struct hh_cache *hh) +{ + struct net_device *net; + struct fwnet_header *h; + + if (hh->hh_type == cpu_to_be16(ETH_P_802_3)) + return -1; + net = neigh->dev; + h = (struct fwnet_header *)((u8 *)hh->hh_data + 16 - sizeof(*h)); + h->h_proto = hh->hh_type; + memcpy(h->h_dest, neigh->ha, net->addr_len); + hh->hh_len = FWNET_HLEN; + + return 0; +} + +/* Called by Address Resolution module to notify changes in address. */ +static void fwnet_header_cache_update(struct hh_cache *hh, + const struct net_device *net, const unsigned char *haddr) +{ + memcpy((u8 *)hh->hh_data + 16 - FWNET_HLEN, haddr, net->addr_len); +} + +static int fwnet_header_parse(const struct sk_buff *skb, unsigned char *haddr) +{ + memcpy(haddr, skb->dev->dev_addr, FWNET_ALEN); + + return FWNET_ALEN; +} + +static const struct header_ops fwnet_header_ops = { + .create = fwnet_header_create, + .rebuild = fwnet_header_rebuild, + .cache = fwnet_header_cache, + .cache_update = fwnet_header_cache_update, + .parse = fwnet_header_parse, +}; + +/* FIXME: is this correct for all cases? */ +static bool fwnet_frag_overlap(struct fwnet_partial_datagram *pd, + unsigned offset, unsigned len) +{ + struct fwnet_fragment_info *fi; + unsigned end = offset + len; + + list_for_each_entry(fi, &pd->fi_list, fi_link) + if (offset < fi->offset + fi->len && end > fi->offset) + return true; + + return false; +} + +/* Assumes that new fragment does not overlap any existing fragments */ +static struct fwnet_fragment_info *fwnet_frag_new( + struct fwnet_partial_datagram *pd, unsigned offset, unsigned len) +{ + struct fwnet_fragment_info *fi, *fi2, *new; + struct list_head *list; + + list = &pd->fi_list; + list_for_each_entry(fi, &pd->fi_list, fi_link) { + if (fi->offset + fi->len == offset) { + /* The new fragment can be tacked on to the end */ + /* Did the new fragment plug a hole? */ + fi2 = list_entry(fi->fi_link.next, + struct fwnet_fragment_info, fi_link); + if (fi->offset + fi->len == fi2->offset) { + /* glue fragments together */ + fi->len += len + fi2->len; + list_del(&fi2->fi_link); + kfree(fi2); + } else { + fi->len += len; + } + + return fi; + } + if (offset + len == fi->offset) { + /* The new fragment can be tacked on to the beginning */ + /* Did the new fragment plug a hole? */ + fi2 = list_entry(fi->fi_link.prev, + struct fwnet_fragment_info, fi_link); + if (fi2->offset + fi2->len == fi->offset) { + /* glue fragments together */ + fi2->len += fi->len + len; + list_del(&fi->fi_link); + kfree(fi); + + return fi2; + } + fi->offset = offset; + fi->len += len; + + return fi; + } + if (offset > fi->offset + fi->len) { + list = &fi->fi_link; + break; + } + if (offset + len < fi->offset) { + list = fi->fi_link.prev; + break; + } + } + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) { + fw_error("out of memory\n"); + return NULL; + } + + new->offset = offset; + new->len = len; + list_add(&new->fi_link, list); + + return new; +} + +static struct fwnet_partial_datagram *fwnet_pd_new(struct net_device *net, + struct fwnet_peer *peer, u16 datagram_label, unsigned dg_size, + void *frag_buf, unsigned frag_off, unsigned frag_len) +{ + struct fwnet_partial_datagram *new; + struct fwnet_fragment_info *fi; + + new = kmalloc(sizeof(*new), GFP_ATOMIC); + if (!new) + goto fail; + + INIT_LIST_HEAD(&new->fi_list); + fi = fwnet_frag_new(new, frag_off, frag_len); + if (fi == NULL) + goto fail_w_new; + + new->datagram_label = datagram_label; + new->datagram_size = dg_size; + new->skb = dev_alloc_skb(dg_size + net->hard_header_len + 15); + if (new->skb == NULL) + goto fail_w_fi; + + skb_reserve(new->skb, (net->hard_header_len + 15) & ~15); + new->pbuf = skb_put(new->skb, dg_size); + memcpy(new->pbuf + frag_off, frag_buf, frag_len); + list_add_tail(&new->pd_link, &peer->pd_list); + + return new; + +fail_w_fi: + kfree(fi); +fail_w_new: + kfree(new); +fail: + fw_error("out of memory\n"); + + return NULL; +} + +static struct fwnet_partial_datagram *fwnet_pd_find(struct fwnet_peer *peer, + u16 datagram_label) +{ + struct fwnet_partial_datagram *pd; + + list_for_each_entry(pd, &peer->pd_list, pd_link) + if (pd->datagram_label == datagram_label) + return pd; + + return NULL; +} + + +static void fwnet_pd_delete(struct fwnet_partial_datagram *old) +{ + struct fwnet_fragment_info *fi, *n; + + list_for_each_entry_safe(fi, n, &old->fi_list, fi_link) + kfree(fi); + + list_del(&old->pd_link); + dev_kfree_skb_any(old->skb); + kfree(old); +} + +static bool fwnet_pd_update(struct fwnet_peer *peer, + struct fwnet_partial_datagram *pd, void *frag_buf, + unsigned frag_off, unsigned frag_len) +{ + if (fwnet_frag_new(pd, frag_off, frag_len) == NULL) + return false; + + memcpy(pd->pbuf + frag_off, frag_buf, frag_len); + + /* + * Move list entry to beginnig of list so that oldest partial + * datagrams percolate to the end of the list + */ + list_move_tail(&pd->pd_link, &peer->pd_list); + + return true; +} + +static bool fwnet_pd_is_complete(struct fwnet_partial_datagram *pd) +{ + struct fwnet_fragment_info *fi; + + fi = list_entry(pd->fi_list.next, struct fwnet_fragment_info, fi_link); + + return fi->len == pd->datagram_size; +} + +/* caller must hold dev->lock */ +static struct fwnet_peer *fwnet_peer_find_by_guid(struct fwnet_device *dev, + u64 guid) +{ + struct fwnet_peer *peer; + + list_for_each_entry(peer, &dev->peer_list, peer_link) + if (peer->guid == guid) + return peer; + + return NULL; +} + +/* caller must hold dev->lock */ +static struct fwnet_peer *fwnet_peer_find_by_node_id(struct fwnet_device *dev, + int node_id, int generation) +{ + struct fwnet_peer *peer; + + list_for_each_entry(peer, &dev->peer_list, peer_link) + if (peer->node_id == node_id && + peer->generation == generation) + return peer; + + return NULL; +} + +/* See IEEE 1394-2008 table 6-4, table 8-8, table 16-18. */ +static unsigned fwnet_max_payload(unsigned max_rec, unsigned speed) +{ + max_rec = min(max_rec, speed + 8); + max_rec = min(max_rec, 0xbU); /* <= 4096 */ + if (max_rec < 8) { + fw_notify("max_rec %x out of range\n", max_rec); + max_rec = 8; + } + + return (1 << (max_rec + 1)) - RFC2374_FRAG_HDR_SIZE; +} + + +static int fwnet_finish_incoming_packet(struct net_device *net, + struct sk_buff *skb, u16 source_node_id, + bool is_broadcast, u16 ether_type) +{ + struct fwnet_device *dev; + static const __be64 broadcast_hw = cpu_to_be64(~0ULL); + int status; + __be64 guid; + + dev = netdev_priv(net); + /* Write metadata, and then pass to the receive level */ + skb->dev = net; + skb->ip_summed = CHECKSUM_UNNECESSARY; /* don't check it */ + + /* + * Parse the encapsulation header. This actually does the job of + * converting to an ethernet frame header, as well as arp + * conversion if needed. ARP conversion is easier in this + * direction, since we are using ethernet as our backend. + */ + /* + * If this is an ARP packet, convert it. First, we want to make + * use of some of the fields, since they tell us a little bit + * about the sending machine. + */ + if (ether_type == ETH_P_ARP) { + struct rfc2734_arp *arp1394; + struct arphdr *arp; + unsigned char *arp_ptr; + u64 fifo_addr; + u64 peer_guid; + unsigned sspd; + u16 max_payload; + struct fwnet_peer *peer; + unsigned long flags; + + arp1394 = (struct rfc2734_arp *)skb->data; + arp = (struct arphdr *)skb->data; + arp_ptr = (unsigned char *)(arp + 1); + peer_guid = get_unaligned_be64(&arp1394->s_uniq_id); + fifo_addr = (u64)get_unaligned_be16(&arp1394->fifo_hi) << 32 + | get_unaligned_be32(&arp1394->fifo_lo); + + sspd = arp1394->sspd; + /* Sanity check. OS X 10.3 PPC reportedly sends 131. */ + if (sspd > SCODE_3200) { + fw_notify("sspd %x out of range\n", sspd); + sspd = SCODE_3200; + } + max_payload = fwnet_max_payload(arp1394->max_rec, sspd); + + spin_lock_irqsave(&dev->lock, flags); + peer = fwnet_peer_find_by_guid(dev, peer_guid); + if (peer) { + peer->fifo = fifo_addr; + + if (peer->speed > sspd) + peer->speed = sspd; + if (peer->max_payload > max_payload) + peer->max_payload = max_payload; + } + spin_unlock_irqrestore(&dev->lock, flags); + + if (!peer) { + fw_notify("No peer for ARP packet from %016llx\n", + (unsigned long long)peer_guid); + goto failed_proto; + } + + /* + * Now that we're done with the 1394 specific stuff, we'll + * need to alter some of the data. Believe it or not, all + * that needs to be done is sender_IP_address needs to be + * moved, the destination hardware address get stuffed + * in and the hardware address length set to 8. + * + * IMPORTANT: The code below overwrites 1394 specific data + * needed above so keep the munging of the data for the + * higher level IP stack last. + */ + + arp->ar_hln = 8; + /* skip over sender unique id */ + arp_ptr += arp->ar_hln; + /* move sender IP addr */ + put_unaligned(arp1394->sip, (u32 *)arp_ptr); + /* skip over sender IP addr */ + arp_ptr += arp->ar_pln; + + if (arp->ar_op == htons(ARPOP_REQUEST)) + memset(arp_ptr, 0, sizeof(u64)); + else + memcpy(arp_ptr, net->dev_addr, sizeof(u64)); + } + + /* Now add the ethernet header. */ + guid = cpu_to_be64(dev->card->guid); + if (dev_hard_header(skb, net, ether_type, + is_broadcast ? &broadcast_hw : &guid, + NULL, skb->len) >= 0) { + struct fwnet_header *eth; + u16 *rawp; + __be16 protocol; + + skb_reset_mac_header(skb); + skb_pull(skb, sizeof(*eth)); + eth = (struct fwnet_header *)skb_mac_header(skb); + if (*eth->h_dest & 1) { + if (memcmp(eth->h_dest, net->broadcast, + net->addr_len) == 0) + skb->pkt_type = PACKET_BROADCAST; +#if 0 + else + skb->pkt_type = PACKET_MULTICAST; +#endif + } else { + if (memcmp(eth->h_dest, net->dev_addr, net->addr_len)) + skb->pkt_type = PACKET_OTHERHOST; + } + if (ntohs(eth->h_proto) >= 1536) { + protocol = eth->h_proto; + } else { + rawp = (u16 *)skb->data; + if (*rawp == 0xffff) + protocol = htons(ETH_P_802_3); + else + protocol = htons(ETH_P_802_2); + } + skb->protocol = protocol; + } + status = netif_rx(skb); + if (status == NET_RX_DROP) { + net->stats.rx_errors++; + net->stats.rx_dropped++; + } else { + net->stats.rx_packets++; + net->stats.rx_bytes += skb->len; + } + if (netif_queue_stopped(net)) + netif_wake_queue(net); + + return 0; + + failed_proto: + net->stats.rx_errors++; + net->stats.rx_dropped++; + + dev_kfree_skb_any(skb); + if (netif_queue_stopped(net)) + netif_wake_queue(net); + + net->last_rx = jiffies; + + return 0; +} + +static int fwnet_incoming_packet(struct fwnet_device *dev, __be32 *buf, int len, + int source_node_id, int generation, + bool is_broadcast) +{ + struct sk_buff *skb; + struct net_device *net = dev->netdev; + struct rfc2734_header hdr; + unsigned lf; + unsigned long flags; + struct fwnet_peer *peer; + struct fwnet_partial_datagram *pd; + int fg_off; + int dg_size; + u16 datagram_label; + int retval; + u16 ether_type; + + hdr.w0 = be32_to_cpu(buf[0]); + lf = fwnet_get_hdr_lf(&hdr); + if (lf == RFC2374_HDR_UNFRAG) { + /* + * An unfragmented datagram has been received by the ieee1394 + * bus. Build an skbuff around it so we can pass it to the + * high level network layer. + */ + ether_type = fwnet_get_hdr_ether_type(&hdr); + buf++; + len -= RFC2374_UNFRAG_HDR_SIZE; + + skb = dev_alloc_skb(len + net->hard_header_len + 15); + if (unlikely(!skb)) { + fw_error("out of memory\n"); + net->stats.rx_dropped++; + + return -1; + } + skb_reserve(skb, (net->hard_header_len + 15) & ~15); + memcpy(skb_put(skb, len), buf, len); + + return fwnet_finish_incoming_packet(net, skb, source_node_id, + is_broadcast, ether_type); + } + /* A datagram fragment has been received, now the fun begins. */ + hdr.w1 = ntohl(buf[1]); + buf += 2; + len -= RFC2374_FRAG_HDR_SIZE; + if (lf == RFC2374_HDR_FIRSTFRAG) { + ether_type = fwnet_get_hdr_ether_type(&hdr); + fg_off = 0; + } else { + ether_type = 0; + fg_off = fwnet_get_hdr_fg_off(&hdr); + } + datagram_label = fwnet_get_hdr_dgl(&hdr); + dg_size = fwnet_get_hdr_dg_size(&hdr); /* ??? + 1 */ + + spin_lock_irqsave(&dev->lock, flags); + + peer = fwnet_peer_find_by_node_id(dev, source_node_id, generation); + if (!peer) + goto bad_proto; + + pd = fwnet_pd_find(peer, datagram_label); + if (pd == NULL) { + while (peer->pdg_size >= FWNET_MAX_FRAGMENTS) { + /* remove the oldest */ + fwnet_pd_delete(list_first_entry(&peer->pd_list, + struct fwnet_partial_datagram, pd_link)); + peer->pdg_size--; + } + pd = fwnet_pd_new(net, peer, datagram_label, + dg_size, buf, fg_off, len); + if (pd == NULL) { + retval = -ENOMEM; + goto bad_proto; + } + peer->pdg_size++; + } else { + if (fwnet_frag_overlap(pd, fg_off, len) || + pd->datagram_size != dg_size) { + /* + * Differing datagram sizes or overlapping fragments, + * discard old datagram and start a new one. + */ + fwnet_pd_delete(pd); + pd = fwnet_pd_new(net, peer, datagram_label, + dg_size, buf, fg_off, len); + if (pd == NULL) { + retval = -ENOMEM; + peer->pdg_size--; + goto bad_proto; + } + } else { + if (!fwnet_pd_update(peer, pd, buf, fg_off, len)) { + /* + * Couldn't save off fragment anyway + * so might as well obliterate the + * datagram now. + */ + fwnet_pd_delete(pd); + peer->pdg_size--; + goto bad_proto; + } + } + } /* new datagram or add to existing one */ + + if (lf == RFC2374_HDR_FIRSTFRAG) + pd->ether_type = ether_type; + + if (fwnet_pd_is_complete(pd)) { + ether_type = pd->ether_type; + peer->pdg_size--; + skb = skb_get(pd->skb); + fwnet_pd_delete(pd); + + spin_unlock_irqrestore(&dev->lock, flags); + + return fwnet_finish_incoming_packet(net, skb, source_node_id, + false, ether_type); + } + /* + * Datagram is not complete, we're done for the + * moment. + */ + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; + + bad_proto: + spin_unlock_irqrestore(&dev->lock, flags); + + if (netif_queue_stopped(net)) + netif_wake_queue(net); + + return 0; +} + +static void fwnet_receive_packet(struct fw_card *card, struct fw_request *r, + int tcode, int destination, int source, int generation, + int speed, unsigned long long offset, void *payload, + size_t length, void *callback_data) +{ + struct fwnet_device *dev = callback_data; + int rcode; + + if (destination == IEEE1394_ALL_NODES) { + kfree(r); + + return; + } + + if (offset != dev->handler.offset) + rcode = RCODE_ADDRESS_ERROR; + else if (tcode != TCODE_WRITE_BLOCK_REQUEST) + rcode = RCODE_TYPE_ERROR; + else if (fwnet_incoming_packet(dev, payload, length, + source, generation, false) != 0) { + fw_error("Incoming packet failure\n"); + rcode = RCODE_CONFLICT_ERROR; + } else + rcode = RCODE_COMPLETE; + + fw_send_response(card, r, rcode); +} + +static void fwnet_receive_broadcast(struct fw_iso_context *context, + u32 cycle, size_t header_length, void *header, void *data) +{ + struct fwnet_device *dev; + struct fw_iso_packet packet; + struct fw_card *card; + __be16 *hdr_ptr; + __be32 *buf_ptr; + int retval; + u32 length; + u16 source_node_id; + u32 specifier_id; + u32 ver; + unsigned long offset; + unsigned long flags; + + dev = data; + card = dev->card; + hdr_ptr = header; + length = be16_to_cpup(hdr_ptr); + + spin_lock_irqsave(&dev->lock, flags); + + offset = dev->rcv_buffer_size * dev->broadcast_rcv_next_ptr; + buf_ptr = dev->broadcast_rcv_buffer_ptrs[dev->broadcast_rcv_next_ptr++]; + if (dev->broadcast_rcv_next_ptr == dev->num_broadcast_rcv_ptrs) + dev->broadcast_rcv_next_ptr = 0; + + spin_unlock_irqrestore(&dev->lock, flags); + + specifier_id = (be32_to_cpu(buf_ptr[0]) & 0xffff) << 8 + | (be32_to_cpu(buf_ptr[1]) & 0xff000000) >> 24; + ver = be32_to_cpu(buf_ptr[1]) & 0xffffff; + source_node_id = be32_to_cpu(buf_ptr[0]) >> 16; + + if (specifier_id == IANA_SPECIFIER_ID && ver == RFC2734_SW_VERSION) { + buf_ptr += 2; + length -= IEEE1394_GASP_HDR_SIZE; + fwnet_incoming_packet(dev, buf_ptr, length, + source_node_id, -1, true); + } + + packet.payload_length = dev->rcv_buffer_size; + packet.interrupt = 1; + packet.skip = 0; + packet.tag = 3; + packet.sy = 0; + packet.header_length = IEEE1394_GASP_HDR_SIZE; + + spin_lock_irqsave(&dev->lock, flags); + + retval = fw_iso_context_queue(dev->broadcast_rcv_context, &packet, + &dev->broadcast_rcv_buffer, offset); + + spin_unlock_irqrestore(&dev->lock, flags); + + if (retval < 0) + fw_error("requeue failed\n"); +} + +static struct kmem_cache *fwnet_packet_task_cache; + +static int fwnet_send_packet(struct fwnet_packet_task *ptask); + +static void fwnet_transmit_packet_done(struct fwnet_packet_task *ptask) +{ + struct fwnet_device *dev; + unsigned long flags; + + dev = ptask->dev; + + spin_lock_irqsave(&dev->lock, flags); + list_del(&ptask->pt_link); + spin_unlock_irqrestore(&dev->lock, flags); + + ptask->outstanding_pkts--; /* FIXME access inside lock */ + + if (ptask->outstanding_pkts > 0) { + u16 dg_size; + u16 fg_off; + u16 datagram_label; + u16 lf; + struct sk_buff *skb; + + /* Update the ptask to point to the next fragment and send it */ + lf = fwnet_get_hdr_lf(&ptask->hdr); + switch (lf) { + case RFC2374_HDR_LASTFRAG: + case RFC2374_HDR_UNFRAG: + default: + fw_error("Outstanding packet %x lf %x, header %x,%x\n", + ptask->outstanding_pkts, lf, ptask->hdr.w0, + ptask->hdr.w1); + BUG(); + + case RFC2374_HDR_FIRSTFRAG: + /* Set frag type here for future interior fragments */ + dg_size = fwnet_get_hdr_dg_size(&ptask->hdr); + fg_off = ptask->max_payload - RFC2374_FRAG_HDR_SIZE; + datagram_label = fwnet_get_hdr_dgl(&ptask->hdr); + break; + + case RFC2374_HDR_INTFRAG: + dg_size = fwnet_get_hdr_dg_size(&ptask->hdr); + fg_off = fwnet_get_hdr_fg_off(&ptask->hdr) + + ptask->max_payload - RFC2374_FRAG_HDR_SIZE; + datagram_label = fwnet_get_hdr_dgl(&ptask->hdr); + break; + } + skb = ptask->skb; + skb_pull(skb, ptask->max_payload); + if (ptask->outstanding_pkts > 1) { + fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_INTFRAG, + dg_size, fg_off, datagram_label); + } else { + fwnet_make_sf_hdr(&ptask->hdr, RFC2374_HDR_LASTFRAG, + dg_size, fg_off, datagram_label); + ptask->max_payload = skb->len + RFC2374_FRAG_HDR_SIZE; + } + fwnet_send_packet(ptask); + } else { + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); + } +} + +static void fwnet_write_complete(struct fw_card *card, int rcode, + void *payload, size_t length, void *data) +{ + struct fwnet_packet_task *ptask; + + ptask = data; + + if (rcode == RCODE_COMPLETE) + fwnet_transmit_packet_done(ptask); + else + fw_error("fwnet_write_complete: failed: %x\n", rcode); + /* ??? error recovery */ +} + +static int fwnet_send_packet(struct fwnet_packet_task *ptask) +{ + struct fwnet_device *dev; + unsigned tx_len; + struct rfc2734_header *bufhdr; + unsigned long flags; + + dev = ptask->dev; + tx_len = ptask->max_payload; + switch (fwnet_get_hdr_lf(&ptask->hdr)) { + case RFC2374_HDR_UNFRAG: + bufhdr = (struct rfc2734_header *) + skb_push(ptask->skb, RFC2374_UNFRAG_HDR_SIZE); + put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0); + break; + + case RFC2374_HDR_FIRSTFRAG: + case RFC2374_HDR_INTFRAG: + case RFC2374_HDR_LASTFRAG: + bufhdr = (struct rfc2734_header *) + skb_push(ptask->skb, RFC2374_FRAG_HDR_SIZE); + put_unaligned_be32(ptask->hdr.w0, &bufhdr->w0); + put_unaligned_be32(ptask->hdr.w1, &bufhdr->w1); + break; + + default: + BUG(); + } + if (ptask->dest_node == IEEE1394_ALL_NODES) { + u8 *p; + int generation; + int node_id; + + /* ptask->generation may not have been set yet */ + generation = dev->card->generation; + smp_rmb(); + node_id = dev->card->node_id; + + p = skb_push(ptask->skb, 8); + put_unaligned_be32(node_id << 16 | IANA_SPECIFIER_ID >> 8, p); + put_unaligned_be32((IANA_SPECIFIER_ID & 0xff) << 24 + | RFC2734_SW_VERSION, &p[4]); + + /* We should not transmit if broadcast_channel.valid == 0. */ + fw_send_request(dev->card, &ptask->transaction, + TCODE_STREAM_DATA, + fw_stream_packet_destination_id(3, + IEEE1394_BROADCAST_CHANNEL, 0), + generation, SCODE_100, 0ULL, ptask->skb->data, + tx_len + 8, fwnet_write_complete, ptask); + + /* FIXME race? */ + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&ptask->pt_link, &dev->broadcasted_list); + spin_unlock_irqrestore(&dev->lock, flags); + + return 0; + } + + fw_send_request(dev->card, &ptask->transaction, + TCODE_WRITE_BLOCK_REQUEST, ptask->dest_node, + ptask->generation, ptask->speed, ptask->fifo_addr, + ptask->skb->data, tx_len, fwnet_write_complete, ptask); + + /* FIXME race? */ + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&ptask->pt_link, &dev->sent_list); + spin_unlock_irqrestore(&dev->lock, flags); + + dev->netdev->trans_start = jiffies; + + return 0; +} + +static int fwnet_broadcast_start(struct fwnet_device *dev) +{ + struct fw_iso_context *context; + int retval; + unsigned num_packets; + unsigned max_receive; + struct fw_iso_packet packet; + unsigned long offset; + unsigned u; + + if (dev->local_fifo == FWNET_NO_FIFO_ADDR) { + /* outside OHCI posted write area? */ + static const struct fw_address_region region = { + .start = 0xffff00000000ULL, + .end = CSR_REGISTER_BASE, + }; + + dev->handler.length = 4096; + dev->handler.address_callback = fwnet_receive_packet; + dev->handler.callback_data = dev; + + retval = fw_core_add_address_handler(&dev->handler, ®ion); + if (retval < 0) + goto failed_initial; + + dev->local_fifo = dev->handler.offset; + } + + max_receive = 1U << (dev->card->max_receive + 1); + num_packets = (FWNET_ISO_PAGE_COUNT * PAGE_SIZE) / max_receive; + + if (!dev->broadcast_rcv_context) { + void **ptrptr; + + context = fw_iso_context_create(dev->card, + FW_ISO_CONTEXT_RECEIVE, IEEE1394_BROADCAST_CHANNEL, + dev->card->link_speed, 8, fwnet_receive_broadcast, dev); + if (IS_ERR(context)) { + retval = PTR_ERR(context); + goto failed_context_create; + } + + retval = fw_iso_buffer_init(&dev->broadcast_rcv_buffer, + dev->card, FWNET_ISO_PAGE_COUNT, DMA_FROM_DEVICE); + if (retval < 0) + goto failed_buffer_init; + + ptrptr = kmalloc(sizeof(void *) * num_packets, GFP_KERNEL); + if (!ptrptr) { + retval = -ENOMEM; + goto failed_ptrs_alloc; + } + + dev->broadcast_rcv_buffer_ptrs = ptrptr; + for (u = 0; u < FWNET_ISO_PAGE_COUNT; u++) { + void *ptr; + unsigned v; + + ptr = kmap(dev->broadcast_rcv_buffer.pages[u]); + for (v = 0; v < num_packets / FWNET_ISO_PAGE_COUNT; v++) + *ptrptr++ = (void *) + ((char *)ptr + v * max_receive); + } + dev->broadcast_rcv_context = context; + } else { + context = dev->broadcast_rcv_context; + } + + packet.payload_length = max_receive; + packet.interrupt = 1; + packet.skip = 0; + packet.tag = 3; + packet.sy = 0; + packet.header_length = IEEE1394_GASP_HDR_SIZE; + offset = 0; + + for (u = 0; u < num_packets; u++) { + retval = fw_iso_context_queue(context, &packet, + &dev->broadcast_rcv_buffer, offset); + if (retval < 0) + goto failed_rcv_queue; + + offset += max_receive; + } + dev->num_broadcast_rcv_ptrs = num_packets; + dev->rcv_buffer_size = max_receive; + dev->broadcast_rcv_next_ptr = 0U; + retval = fw_iso_context_start(context, -1, 0, + FW_ISO_CONTEXT_MATCH_ALL_TAGS); /* ??? sync */ + if (retval < 0) + goto failed_rcv_queue; + + /* FIXME: adjust it according to the min. speed of all known peers? */ + dev->broadcast_xmt_max_payload = IEEE1394_MAX_PAYLOAD_S100 + - IEEE1394_GASP_HDR_SIZE - RFC2374_UNFRAG_HDR_SIZE; + dev->broadcast_state = FWNET_BROADCAST_RUNNING; + + return 0; + + failed_rcv_queue: + kfree(dev->broadcast_rcv_buffer_ptrs); + dev->broadcast_rcv_buffer_ptrs = NULL; + failed_ptrs_alloc: + fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, dev->card); + failed_buffer_init: + fw_iso_context_destroy(context); + dev->broadcast_rcv_context = NULL; + failed_context_create: + fw_core_remove_address_handler(&dev->handler); + failed_initial: + dev->local_fifo = FWNET_NO_FIFO_ADDR; + + return retval; +} + +/* ifup */ +static int fwnet_open(struct net_device *net) +{ + struct fwnet_device *dev = netdev_priv(net); + int ret; + + if (dev->broadcast_state == FWNET_BROADCAST_ERROR) { + ret = fwnet_broadcast_start(dev); + if (ret) + return ret; + } + netif_start_queue(net); + + return 0; +} + +/* ifdown */ +static int fwnet_stop(struct net_device *net) +{ + netif_stop_queue(net); + + /* Deallocate iso context for use by other applications? */ + + return 0; +} + +static int fwnet_tx(struct sk_buff *skb, struct net_device *net) +{ + struct fwnet_header hdr_buf; + struct fwnet_device *dev = netdev_priv(net); + __be16 proto; + u16 dest_node; + unsigned max_payload; + u16 dg_size; + u16 *datagram_label_ptr; + struct fwnet_packet_task *ptask; + struct fwnet_peer *peer; + unsigned long flags; + + ptask = kmem_cache_alloc(fwnet_packet_task_cache, GFP_ATOMIC); + if (ptask == NULL) + goto fail; + + skb = skb_share_check(skb, GFP_ATOMIC); + if (!skb) + goto fail; + + /* + * Make a copy of the driver-specific header. + * We might need to rebuild the header on tx failure. + */ + memcpy(&hdr_buf, skb->data, sizeof(hdr_buf)); + skb_pull(skb, sizeof(hdr_buf)); + + proto = hdr_buf.h_proto; + dg_size = skb->len; + + /* serialize access to peer, including peer->datagram_label */ + spin_lock_irqsave(&dev->lock, flags); + + /* + * Set the transmission type for the packet. ARP packets and IP + * broadcast packets are sent via GASP. + */ + if (memcmp(hdr_buf.h_dest, net->broadcast, FWNET_ALEN) == 0 + || proto == htons(ETH_P_ARP) + || (proto == htons(ETH_P_IP) + && IN_MULTICAST(ntohl(ip_hdr(skb)->daddr)))) { + max_payload = dev->broadcast_xmt_max_payload; + datagram_label_ptr = &dev->broadcast_xmt_datagramlabel; + + ptask->fifo_addr = FWNET_NO_FIFO_ADDR; + ptask->generation = 0; + ptask->dest_node = IEEE1394_ALL_NODES; + ptask->speed = SCODE_100; + } else { + __be64 guid = get_unaligned((__be64 *)hdr_buf.h_dest); + u8 generation; + + peer = fwnet_peer_find_by_guid(dev, be64_to_cpu(guid)); + if (!peer || peer->fifo == FWNET_NO_FIFO_ADDR) + goto fail_unlock; + + generation = peer->generation; + dest_node = peer->node_id; + max_payload = peer->max_payload; + datagram_label_ptr = &peer->datagram_label; + + ptask->fifo_addr = peer->fifo; + ptask->generation = generation; + ptask->dest_node = dest_node; + ptask->speed = peer->speed; + } + + /* If this is an ARP packet, convert it */ + if (proto == htons(ETH_P_ARP)) { + struct arphdr *arp = (struct arphdr *)skb->data; + unsigned char *arp_ptr = (unsigned char *)(arp + 1); + struct rfc2734_arp *arp1394 = (struct rfc2734_arp *)skb->data; + __be32 ipaddr; + + ipaddr = get_unaligned((__be32 *)(arp_ptr + FWNET_ALEN)); + + arp1394->hw_addr_len = RFC2734_HW_ADDR_LEN; + arp1394->max_rec = dev->card->max_receive; + arp1394->sspd = dev->card->link_speed; + + put_unaligned_be16(dev->local_fifo >> 32, + &arp1394->fifo_hi); + put_unaligned_be32(dev->local_fifo & 0xffffffff, + &arp1394->fifo_lo); + put_unaligned(ipaddr, &arp1394->sip); + } + + ptask->hdr.w0 = 0; + ptask->hdr.w1 = 0; + ptask->skb = skb; + ptask->dev = dev; + + /* Does it all fit in one packet? */ + if (dg_size <= max_payload) { + fwnet_make_uf_hdr(&ptask->hdr, ntohs(proto)); + ptask->outstanding_pkts = 1; + max_payload = dg_size + RFC2374_UNFRAG_HDR_SIZE; + } else { + u16 datagram_label; + + max_payload -= RFC2374_FRAG_OVERHEAD; + datagram_label = (*datagram_label_ptr)++; + fwnet_make_ff_hdr(&ptask->hdr, ntohs(proto), dg_size, + datagram_label); + ptask->outstanding_pkts = DIV_ROUND_UP(dg_size, max_payload); + max_payload += RFC2374_FRAG_HDR_SIZE; + } + + spin_unlock_irqrestore(&dev->lock, flags); + + ptask->max_payload = max_payload; + fwnet_send_packet(ptask); + + return NETDEV_TX_OK; + + fail_unlock: + spin_unlock_irqrestore(&dev->lock, flags); + fail: + if (ptask) + kmem_cache_free(fwnet_packet_task_cache, ptask); + + if (skb != NULL) + dev_kfree_skb(skb); + + net->stats.tx_dropped++; + net->stats.tx_errors++; + + /* + * FIXME: According to a patch from 2003-02-26, "returning non-zero + * causes serious problems" here, allegedly. Before that patch, + * -ERRNO was returned which is not appropriate under Linux 2.6. + * Perhaps more needs to be done? Stop the queue in serious + * conditions and restart it elsewhere? + */ + return NETDEV_TX_OK; +} + +static int fwnet_change_mtu(struct net_device *net, int new_mtu) +{ + if (new_mtu < 68) + return -EINVAL; + + net->mtu = new_mtu; + return 0; +} + +static void fwnet_get_drvinfo(struct net_device *net, + struct ethtool_drvinfo *info) +{ + strcpy(info->driver, KBUILD_MODNAME); + strcpy(info->bus_info, "ieee1394"); +} + +static struct ethtool_ops fwnet_ethtool_ops = { + .get_drvinfo = fwnet_get_drvinfo, +}; + +static const struct net_device_ops fwnet_netdev_ops = { + .ndo_open = fwnet_open, + .ndo_stop = fwnet_stop, + .ndo_start_xmit = fwnet_tx, + .ndo_change_mtu = fwnet_change_mtu, +}; + +static void fwnet_init_dev(struct net_device *net) +{ + net->header_ops = &fwnet_header_ops; + net->netdev_ops = &fwnet_netdev_ops; + net->watchdog_timeo = 2 * HZ; + net->flags = IFF_BROADCAST | IFF_MULTICAST; + net->features = NETIF_F_HIGHDMA; + net->addr_len = FWNET_ALEN; + net->hard_header_len = FWNET_HLEN; + net->type = ARPHRD_IEEE1394; + net->tx_queue_len = 10; + SET_ETHTOOL_OPS(net, &fwnet_ethtool_ops); +} + +/* caller must hold fwnet_device_mutex */ +static struct fwnet_device *fwnet_dev_find(struct fw_card *card) +{ + struct fwnet_device *dev; + + list_for_each_entry(dev, &fwnet_device_list, dev_link) + if (dev->card == card) + return dev; + + return NULL; +} + +static int fwnet_add_peer(struct fwnet_device *dev, + struct fw_unit *unit, struct fw_device *device) +{ + struct fwnet_peer *peer; + + peer = kmalloc(sizeof(*peer), GFP_KERNEL); + if (!peer) + return -ENOMEM; + + dev_set_drvdata(&unit->device, peer); + + peer->dev = dev; + peer->guid = (u64)device->config_rom[3] << 32 | device->config_rom[4]; + peer->fifo = FWNET_NO_FIFO_ADDR; + INIT_LIST_HEAD(&peer->pd_list); + peer->pdg_size = 0; + peer->datagram_label = 0; + peer->speed = device->max_speed; + peer->max_payload = fwnet_max_payload(device->max_rec, peer->speed); + + peer->generation = device->generation; + smp_rmb(); + peer->node_id = device->node_id; + + spin_lock_irq(&dev->lock); + list_add_tail(&peer->peer_link, &dev->peer_list); + spin_unlock_irq(&dev->lock); + + return 0; +} + +static int fwnet_probe(struct device *_dev) +{ + struct fw_unit *unit = fw_unit(_dev); + struct fw_device *device = fw_parent_device(unit); + struct fw_card *card = device->card; + struct net_device *net; + bool allocated_netdev = false; + struct fwnet_device *dev; + unsigned max_mtu; + int ret; + + mutex_lock(&fwnet_device_mutex); + + dev = fwnet_dev_find(card); + if (dev) { + net = dev->netdev; + goto have_dev; + } + + net = alloc_netdev(sizeof(*dev), "firewire%d", fwnet_init_dev); + if (net == NULL) { + ret = -ENOMEM; + goto out; + } + + allocated_netdev = true; + SET_NETDEV_DEV(net, card->device); + dev = netdev_priv(net); + + spin_lock_init(&dev->lock); + dev->broadcast_state = FWNET_BROADCAST_ERROR; + dev->broadcast_rcv_context = NULL; + dev->broadcast_xmt_max_payload = 0; + dev->broadcast_xmt_datagramlabel = 0; + + dev->local_fifo = FWNET_NO_FIFO_ADDR; + + INIT_LIST_HEAD(&dev->packet_list); + INIT_LIST_HEAD(&dev->broadcasted_list); + INIT_LIST_HEAD(&dev->sent_list); + INIT_LIST_HEAD(&dev->peer_list); + + dev->card = card; + dev->netdev = net; + + /* + * Use the RFC 2734 default 1500 octets or the maximum payload + * as initial MTU + */ + max_mtu = (1 << (card->max_receive + 1)) + - sizeof(struct rfc2734_header) - IEEE1394_GASP_HDR_SIZE; + net->mtu = min(1500U, max_mtu); + + /* Set our hardware address while we're at it */ + put_unaligned_be64(card->guid, net->dev_addr); + put_unaligned_be64(~0ULL, net->broadcast); + ret = register_netdev(net); + if (ret) { + fw_error("Cannot register the driver\n"); + goto out; + } + + list_add_tail(&dev->dev_link, &fwnet_device_list); + fw_notify("%s: IPv4 over FireWire on device %016llx\n", + net->name, (unsigned long long)card->guid); + have_dev: + ret = fwnet_add_peer(dev, unit, device); + if (ret && allocated_netdev) { + unregister_netdev(net); + list_del(&dev->dev_link); + } + out: + if (ret && allocated_netdev) + free_netdev(net); + + mutex_unlock(&fwnet_device_mutex); + + return ret; +} + +static void fwnet_remove_peer(struct fwnet_peer *peer) +{ + struct fwnet_partial_datagram *pd, *pd_next; + + spin_lock_irq(&peer->dev->lock); + list_del(&peer->peer_link); + spin_unlock_irq(&peer->dev->lock); + + list_for_each_entry_safe(pd, pd_next, &peer->pd_list, pd_link) + fwnet_pd_delete(pd); + + kfree(peer); +} + +static int fwnet_remove(struct device *_dev) +{ + struct fwnet_peer *peer = dev_get_drvdata(_dev); + struct fwnet_device *dev = peer->dev; + struct net_device *net; + struct fwnet_packet_task *ptask, *pt_next; + + mutex_lock(&fwnet_device_mutex); + + fwnet_remove_peer(peer); + + if (list_empty(&dev->peer_list)) { + net = dev->netdev; + unregister_netdev(net); + + if (dev->local_fifo != FWNET_NO_FIFO_ADDR) + fw_core_remove_address_handler(&dev->handler); + if (dev->broadcast_rcv_context) { + fw_iso_context_stop(dev->broadcast_rcv_context); + fw_iso_buffer_destroy(&dev->broadcast_rcv_buffer, + dev->card); + fw_iso_context_destroy(dev->broadcast_rcv_context); + } + list_for_each_entry_safe(ptask, pt_next, + &dev->packet_list, pt_link) { + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); + } + list_for_each_entry_safe(ptask, pt_next, + &dev->broadcasted_list, pt_link) { + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); + } + list_for_each_entry_safe(ptask, pt_next, + &dev->sent_list, pt_link) { + dev_kfree_skb_any(ptask->skb); + kmem_cache_free(fwnet_packet_task_cache, ptask); + } + list_del(&dev->dev_link); + + free_netdev(net); + } + + mutex_unlock(&fwnet_device_mutex); + + return 0; +} + +/* + * FIXME abort partially sent fragmented datagrams, + * discard partially received fragmented datagrams + */ +static void fwnet_update(struct fw_unit *unit) +{ + struct fw_device *device = fw_parent_device(unit); + struct fwnet_peer *peer = dev_get_drvdata(&unit->device); + int generation; + + generation = device->generation; + + spin_lock_irq(&peer->dev->lock); + peer->node_id = device->node_id; + peer->generation = generation; + spin_unlock_irq(&peer->dev->lock); +} + +static const struct ieee1394_device_id fwnet_id_table[] = { + { + .match_flags = IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION, + .specifier_id = IANA_SPECIFIER_ID, + .version = RFC2734_SW_VERSION, + }, + { } +}; + +static struct fw_driver fwnet_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "net", + .bus = &fw_bus_type, + .probe = fwnet_probe, + .remove = fwnet_remove, + }, + .update = fwnet_update, + .id_table = fwnet_id_table, +}; + +static const u32 rfc2374_unit_directory_data[] = { + 0x00040000, /* directory_length */ + 0x1200005e, /* unit_specifier_id: IANA */ + 0x81000003, /* textual descriptor offset */ + 0x13000001, /* unit_sw_version: RFC 2734 */ + 0x81000005, /* textual descriptor offset */ + 0x00030000, /* descriptor_length */ + 0x00000000, /* text */ + 0x00000000, /* minimal ASCII, en */ + 0x49414e41, /* I A N A */ + 0x00030000, /* descriptor_length */ + 0x00000000, /* text */ + 0x00000000, /* minimal ASCII, en */ + 0x49507634, /* I P v 4 */ +}; + +static struct fw_descriptor rfc2374_unit_directory = { + .length = ARRAY_SIZE(rfc2374_unit_directory_data), + .key = (CSR_DIRECTORY | CSR_UNIT) << 24, + .data = rfc2374_unit_directory_data +}; + +static int __init fwnet_init(void) +{ + int err; + + err = fw_core_add_descriptor(&rfc2374_unit_directory); + if (err) + return err; + + fwnet_packet_task_cache = kmem_cache_create("packet_task", + sizeof(struct fwnet_packet_task), 0, 0, NULL); + if (!fwnet_packet_task_cache) { + err = -ENOMEM; + goto out; + } + + err = driver_register(&fwnet_driver.driver); + if (!err) + return 0; + + kmem_cache_destroy(fwnet_packet_task_cache); +out: + fw_core_remove_descriptor(&rfc2374_unit_directory); + + return err; +} +module_init(fwnet_init); + +static void __exit fwnet_cleanup(void) +{ + driver_unregister(&fwnet_driver.driver); + kmem_cache_destroy(fwnet_packet_task_cache); + fw_core_remove_descriptor(&rfc2374_unit_directory); +} +module_exit(fwnet_cleanup); + +MODULE_AUTHOR("Jay Fenlason <fenlason@redhat.com>"); +MODULE_DESCRIPTION("IPv4 over IEEE1394 as per RFC 2734"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(ieee1394, fwnet_id_table); diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c index dc425e74a26..e4b4e8898e3 100644 --- a/drivers/gpu/drm/i915/i915_opregion.c +++ b/drivers/gpu/drm/i915/i915_opregion.c @@ -419,7 +419,7 @@ void intel_opregion_free(struct drm_device *dev, int suspend) return; if (!suspend) - acpi_video_exit(); + acpi_video_unregister(); opregion->acpi->drdy = 0; diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 3c259ee7ddd..aa87b6a3bbe 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -326,6 +326,15 @@ config I2C_DAVINCI devices such as DaVinci NIC. For details please see http://www.ti.com/davinci +config I2C_DESIGNWARE + tristate "Synopsys DesignWare" + help + If you say yes to this option, support will be included for the + Synopsys DesignWare I2C adapter. Only master mode is supported. + + This driver can also be built as a module. If so, the module + will be called i2c-designware. + config I2C_GPIO tristate "GPIO-based bitbanging I2C" depends on GENERIC_GPIO diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index edeabf00310..e654263bfc0 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -30,6 +30,7 @@ obj-$(CONFIG_I2C_AU1550) += i2c-au1550.o obj-$(CONFIG_I2C_BLACKFIN_TWI) += i2c-bfin-twi.o obj-$(CONFIG_I2C_CPM) += i2c-cpm.o obj-$(CONFIG_I2C_DAVINCI) += i2c-davinci.o +obj-$(CONFIG_I2C_DESIGNWARE) += i2c-designware.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index b5db8b88361..9c2e10082b7 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -140,7 +140,7 @@ static irqreturn_t cpm_i2c_interrupt(int irq, void *dev_id) dev_dbg(&adap->dev, "Interrupt: %x\n", i); - wake_up_interruptible(&cpm->i2c_wait); + wake_up(&cpm->i2c_wait); return i ? IRQ_HANDLED : IRQ_NONE; } @@ -364,12 +364,12 @@ static int cpm_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) dev_dbg(&adap->dev, "test ready.\n"); pmsg = &msgs[tptr]; if (pmsg->flags & I2C_M_RD) - ret = wait_event_interruptible_timeout(cpm->i2c_wait, + ret = wait_event_timeout(cpm->i2c_wait, (in_be16(&tbdf[tptr].cbd_sc) & BD_SC_NAK) || !(in_be16(&rbdf[rptr].cbd_sc) & BD_SC_EMPTY), 1 * HZ); else - ret = wait_event_interruptible_timeout(cpm->i2c_wait, + ret = wait_event_timeout(cpm->i2c_wait, !(in_be16(&tbdf[tptr].cbd_sc) & BD_SC_READY), 1 * HZ); if (ret == 0) { diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c new file mode 100644 index 00000000000..b444762e9b9 --- /dev/null +++ b/drivers/i2c/busses/i2c-designware.c @@ -0,0 +1,624 @@ +/* + * Synopsys Designware I2C adapter driver (master only). + * + * Based on the TI DAVINCI I2C adapter driver. + * + * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2007 MontaVista Software Inc. + * Copyright (C) 2009 Provigent Ltd. + * + * ---------------------------------------------------------------------------- + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * ---------------------------------------------------------------------------- + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/clk.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/io.h> + +/* + * Registers offset + */ +#define DW_IC_CON 0x0 +#define DW_IC_TAR 0x4 +#define DW_IC_DATA_CMD 0x10 +#define DW_IC_SS_SCL_HCNT 0x14 +#define DW_IC_SS_SCL_LCNT 0x18 +#define DW_IC_FS_SCL_HCNT 0x1c +#define DW_IC_FS_SCL_LCNT 0x20 +#define DW_IC_INTR_STAT 0x2c +#define DW_IC_INTR_MASK 0x30 +#define DW_IC_CLR_INTR 0x40 +#define DW_IC_ENABLE 0x6c +#define DW_IC_STATUS 0x70 +#define DW_IC_TXFLR 0x74 +#define DW_IC_RXFLR 0x78 +#define DW_IC_COMP_PARAM_1 0xf4 +#define DW_IC_TX_ABRT_SOURCE 0x80 + +#define DW_IC_CON_MASTER 0x1 +#define DW_IC_CON_SPEED_STD 0x2 +#define DW_IC_CON_SPEED_FAST 0x4 +#define DW_IC_CON_10BITADDR_MASTER 0x10 +#define DW_IC_CON_RESTART_EN 0x20 +#define DW_IC_CON_SLAVE_DISABLE 0x40 + +#define DW_IC_INTR_TX_EMPTY 0x10 +#define DW_IC_INTR_TX_ABRT 0x40 +#define DW_IC_INTR_STOP_DET 0x200 + +#define DW_IC_STATUS_ACTIVITY 0x1 + +#define DW_IC_ERR_TX_ABRT 0x1 + +/* + * status codes + */ +#define STATUS_IDLE 0x0 +#define STATUS_WRITE_IN_PROGRESS 0x1 +#define STATUS_READ_IN_PROGRESS 0x2 + +#define TIMEOUT 20 /* ms */ + +/* + * hardware abort codes from the DW_IC_TX_ABRT_SOURCE register + * + * only expected abort codes are listed here + * refer to the datasheet for the full list + */ +#define ABRT_7B_ADDR_NOACK 0 +#define ABRT_10ADDR1_NOACK 1 +#define ABRT_10ADDR2_NOACK 2 +#define ABRT_TXDATA_NOACK 3 +#define ABRT_GCALL_NOACK 4 +#define ABRT_GCALL_READ 5 +#define ABRT_SBYTE_ACKDET 7 +#define ABRT_SBYTE_NORSTRT 9 +#define ABRT_10B_RD_NORSTRT 10 +#define ARB_MASTER_DIS 11 +#define ARB_LOST 12 + +static char *abort_sources[] = { + [ABRT_7B_ADDR_NOACK] = + "slave address not acknowledged (7bit mode)", + [ABRT_10ADDR1_NOACK] = + "first address byte not acknowledged (10bit mode)", + [ABRT_10ADDR2_NOACK] = + "second address byte not acknowledged (10bit mode)", + [ABRT_TXDATA_NOACK] = + "data not acknowledged", + [ABRT_GCALL_NOACK] = + "no acknowledgement for a general call", + [ABRT_GCALL_READ] = + "read after general call", + [ABRT_SBYTE_ACKDET] = + "start byte acknowledged", + [ABRT_SBYTE_NORSTRT] = + "trying to send start byte when restart is disabled", + [ABRT_10B_RD_NORSTRT] = + "trying to read when restart is disabled (10bit mode)", + [ARB_MASTER_DIS] = + "trying to use disabled adapter", + [ARB_LOST] = + "lost arbitration", +}; + +/** + * struct dw_i2c_dev - private i2c-designware data + * @dev: driver model device node + * @base: IO registers pointer + * @cmd_complete: tx completion indicator + * @pump_msg: continue in progress transfers + * @lock: protect this struct and IO registers + * @clk: input reference clock + * @cmd_err: run time hadware error code + * @msgs: points to an array of messages currently being transfered + * @msgs_num: the number of elements in msgs + * @msg_write_idx: the element index of the current tx message in the msgs + * array + * @tx_buf_len: the length of the current tx buffer + * @tx_buf: the current tx buffer + * @msg_read_idx: the element index of the current rx message in the msgs + * array + * @rx_buf_len: the length of the current rx buffer + * @rx_buf: the current rx buffer + * @msg_err: error status of the current transfer + * @status: i2c master status, one of STATUS_* + * @abort_source: copy of the TX_ABRT_SOURCE register + * @irq: interrupt number for the i2c master + * @adapter: i2c subsystem adapter node + * @tx_fifo_depth: depth of the hardware tx fifo + * @rx_fifo_depth: depth of the hardware rx fifo + */ +struct dw_i2c_dev { + struct device *dev; + void __iomem *base; + struct completion cmd_complete; + struct tasklet_struct pump_msg; + struct mutex lock; + struct clk *clk; + int cmd_err; + struct i2c_msg *msgs; + int msgs_num; + int msg_write_idx; + u16 tx_buf_len; + u8 *tx_buf; + int msg_read_idx; + u16 rx_buf_len; + u8 *rx_buf; + int msg_err; + unsigned int status; + u16 abort_source; + int irq; + struct i2c_adapter adapter; + unsigned int tx_fifo_depth; + unsigned int rx_fifo_depth; +}; + +/** + * i2c_dw_init() - initialize the designware i2c master hardware + * @dev: device private data + * + * This functions configures and enables the I2C master. + * This function is called during I2C init function, and in case of timeout at + * run time. + */ +static void i2c_dw_init(struct dw_i2c_dev *dev) +{ + u32 input_clock_khz = clk_get_rate(dev->clk) / 1000; + u16 ic_con; + + /* Disable the adapter */ + writeb(0, dev->base + DW_IC_ENABLE); + + /* set standard and fast speed deviders for high/low periods */ + writew((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */ + dev->base + DW_IC_SS_SCL_HCNT); + writew((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */ + dev->base + DW_IC_SS_SCL_LCNT); + writew((input_clock_khz * 6 / 10000)+1, /* fast speed high, 0.6us */ + dev->base + DW_IC_FS_SCL_HCNT); + writew((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */ + dev->base + DW_IC_FS_SCL_LCNT); + + /* configure the i2c master */ + ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | + DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; + writew(ic_con, dev->base + DW_IC_CON); +} + +/* + * Waiting for bus not busy + */ +static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) +{ + int timeout = TIMEOUT; + + while (readb(dev->base + DW_IC_STATUS) & DW_IC_STATUS_ACTIVITY) { + if (timeout <= 0) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + timeout--; + mdelay(1); + } + + return 0; +} + +/* + * Initiate low level master read/write transaction. + * This function is called from i2c_dw_xfer when starting a transfer. + * This function is also called from dw_i2c_pump_msg to continue a transfer + * that is longer than the size of the TX FIFO. + */ +static void +i2c_dw_xfer_msg(struct i2c_adapter *adap) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + struct i2c_msg *msgs = dev->msgs; + int num = dev->msgs_num; + u16 ic_con, intr_mask; + int tx_limit = dev->tx_fifo_depth - readb(dev->base + DW_IC_TXFLR); + int rx_limit = dev->rx_fifo_depth - readb(dev->base + DW_IC_RXFLR); + u16 addr = msgs[dev->msg_write_idx].addr; + u16 buf_len = dev->tx_buf_len; + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* Disable the adapter */ + writeb(0, dev->base + DW_IC_ENABLE); + + /* set the slave (target) address */ + writew(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR); + + /* if the slave address is ten bit address, enable 10BITADDR */ + ic_con = readw(dev->base + DW_IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_con |= DW_IC_CON_10BITADDR_MASTER; + else + ic_con &= ~DW_IC_CON_10BITADDR_MASTER; + writew(ic_con, dev->base + DW_IC_CON); + + /* Enable the adapter */ + writeb(1, dev->base + DW_IC_ENABLE); + } + + for (; dev->msg_write_idx < num; dev->msg_write_idx++) { + /* if target address has changed, we need to + * reprogram the target address in the i2c + * adapter when we are done with this transfer + */ + if (msgs[dev->msg_write_idx].addr != addr) + return; + + if (msgs[dev->msg_write_idx].len == 0) { + dev_err(dev->dev, + "%s: invalid message length\n", __func__); + dev->msg_err = -EINVAL; + return; + } + + if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) { + /* new i2c_msg */ + dev->tx_buf = msgs[dev->msg_write_idx].buf; + buf_len = msgs[dev->msg_write_idx].len; + } + + while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) { + if (msgs[dev->msg_write_idx].flags & I2C_M_RD) { + writew(0x100, dev->base + DW_IC_DATA_CMD); + rx_limit--; + } else + writew(*(dev->tx_buf++), + dev->base + DW_IC_DATA_CMD); + tx_limit--; buf_len--; + } + } + + intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT; + if (buf_len > 0) { /* more bytes to be written */ + intr_mask |= DW_IC_INTR_TX_EMPTY; + dev->status |= STATUS_WRITE_IN_PROGRESS; + } else + dev->status &= ~STATUS_WRITE_IN_PROGRESS; + writew(intr_mask, dev->base + DW_IC_INTR_MASK); + + dev->tx_buf_len = buf_len; +} + +static void +i2c_dw_read(struct i2c_adapter *adap) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + struct i2c_msg *msgs = dev->msgs; + int num = dev->msgs_num; + u16 addr = msgs[dev->msg_read_idx].addr; + int rx_valid = readw(dev->base + DW_IC_RXFLR); + + for (; dev->msg_read_idx < num; dev->msg_read_idx++) { + u16 len; + u8 *buf; + + if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD)) + continue; + + /* different i2c client, reprogram the i2c adapter */ + if (msgs[dev->msg_read_idx].addr != addr) + return; + + if (!(dev->status & STATUS_READ_IN_PROGRESS)) { + len = msgs[dev->msg_read_idx].len; + buf = msgs[dev->msg_read_idx].buf; + } else { + len = dev->rx_buf_len; + buf = dev->rx_buf; + } + + for (; len > 0 && rx_valid > 0; len--, rx_valid--) + *buf++ = readb(dev->base + DW_IC_DATA_CMD); + + if (len > 0) { + dev->status |= STATUS_READ_IN_PROGRESS; + dev->rx_buf_len = len; + dev->rx_buf = buf; + return; + } else + dev->status &= ~STATUS_READ_IN_PROGRESS; + } +} + +/* + * Prepare controller for a transaction and call i2c_dw_xfer_msg + */ +static int +i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct dw_i2c_dev *dev = i2c_get_adapdata(adap); + int ret; + + dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + + mutex_lock(&dev->lock); + + INIT_COMPLETION(dev->cmd_complete); + dev->msgs = msgs; + dev->msgs_num = num; + dev->cmd_err = 0; + dev->msg_write_idx = 0; + dev->msg_read_idx = 0; + dev->msg_err = 0; + dev->status = STATUS_IDLE; + + ret = i2c_dw_wait_bus_not_busy(dev); + if (ret < 0) + goto done; + + /* start the transfers */ + i2c_dw_xfer_msg(adap); + + /* wait for tx to complete */ + ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ); + if (ret == 0) { + dev_err(dev->dev, "controller timed out\n"); + i2c_dw_init(dev); + ret = -ETIMEDOUT; + goto done; + } else if (ret < 0) + goto done; + + if (dev->msg_err) { + ret = dev->msg_err; + goto done; + } + + /* no error */ + if (likely(!dev->cmd_err)) { + /* read rx fifo, and disable the adapter */ + do { + i2c_dw_read(adap); + } while (dev->status & STATUS_READ_IN_PROGRESS); + writeb(0, dev->base + DW_IC_ENABLE); + ret = num; + goto done; + } + + /* We have an error */ + if (dev->cmd_err == DW_IC_ERR_TX_ABRT) { + unsigned long abort_source = dev->abort_source; + int i; + + for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) { + dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]); + } + } + ret = -EIO; + +done: + mutex_unlock(&dev->lock); + + return ret; +} + +static u32 i2c_dw_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR; +} + +static void dw_i2c_pump_msg(unsigned long data) +{ + struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data; + u16 intr_mask; + + i2c_dw_read(&dev->adapter); + i2c_dw_xfer_msg(&dev->adapter); + + intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT; + if (dev->status & STATUS_WRITE_IN_PROGRESS) + intr_mask |= DW_IC_INTR_TX_EMPTY; + writew(intr_mask, dev->base + DW_IC_INTR_MASK); +} + +/* + * Interrupt service routine. This gets called whenever an I2C interrupt + * occurs. + */ +static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) +{ + struct dw_i2c_dev *dev = dev_id; + u16 stat; + + stat = readw(dev->base + DW_IC_INTR_STAT); + dev_dbg(dev->dev, "%s: stat=0x%x\n", __func__, stat); + if (stat & DW_IC_INTR_TX_ABRT) { + dev->abort_source = readw(dev->base + DW_IC_TX_ABRT_SOURCE); + dev->cmd_err |= DW_IC_ERR_TX_ABRT; + dev->status = STATUS_IDLE; + } else if (stat & DW_IC_INTR_TX_EMPTY) + tasklet_schedule(&dev->pump_msg); + + readb(dev->base + DW_IC_CLR_INTR); /* clear interrupts */ + writew(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */ + if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) + complete(&dev->cmd_complete); + + return IRQ_HANDLED; +} + +static struct i2c_algorithm i2c_dw_algo = { + .master_xfer = i2c_dw_xfer, + .functionality = i2c_dw_func, +}; + +static int __devinit dw_i2c_probe(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *irq, *ioarea; + int r; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -EINVAL; + } + + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -EINVAL; + } + + ioarea = request_mem_region(mem->start, resource_size(mem), + pdev->name); + if (!ioarea) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + dev = kzalloc(sizeof(struct dw_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto err_release_region; + } + + init_completion(&dev->cmd_complete); + tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev); + mutex_init(&dev->lock); + dev->dev = get_device(&pdev->dev); + dev->irq = irq->start; + platform_set_drvdata(pdev, dev); + + dev->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(dev->clk)) { + r = -ENODEV; + goto err_free_mem; + } + clk_enable(dev->clk); + + dev->base = ioremap(mem->start, resource_size(mem)); + if (dev->base == NULL) { + dev_err(&pdev->dev, "failure mapping io resources\n"); + r = -EBUSY; + goto err_unuse_clocks; + } + { + u32 param1 = readl(dev->base + DW_IC_COMP_PARAM_1); + + dev->tx_fifo_depth = ((param1 >> 16) & 0xff) + 1; + dev->rx_fifo_depth = ((param1 >> 8) & 0xff) + 1; + } + i2c_dw_init(dev); + + writew(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */ + r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, dev); + if (r) { + dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq); + goto err_iounmap; + } + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strlcpy(adap->name, "Synopsys DesignWare I2C adapter", + sizeof(adap->name)); + adap->algo = &i2c_dw_algo; + adap->dev.parent = &pdev->dev; + + adap->nr = pdev->id; + r = i2c_add_numbered_adapter(adap); + if (r) { + dev_err(&pdev->dev, "failure adding adapter\n"); + goto err_free_irq; + } + + return 0; + +err_free_irq: + free_irq(dev->irq, dev); +err_iounmap: + iounmap(dev->base); +err_unuse_clocks: + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; +err_free_mem: + platform_set_drvdata(pdev, NULL); + put_device(&pdev->dev); + kfree(dev); +err_release_region: + release_mem_region(mem->start, resource_size(mem)); + + return r; +} + +static int __devexit dw_i2c_remove(struct platform_device *pdev) +{ + struct dw_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + platform_set_drvdata(pdev, NULL); + i2c_del_adapter(&dev->adapter); + put_device(&pdev->dev); + + clk_disable(dev->clk); + clk_put(dev->clk); + dev->clk = NULL; + + writeb(0, dev->base + DW_IC_ENABLE); + free_irq(dev->irq, dev); + kfree(dev); + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, resource_size(mem)); + return 0; +} + +/* work with hotplug and coldplug */ +MODULE_ALIAS("platform:i2c_designware"); + +static struct platform_driver dw_i2c_driver = { + .remove = __devexit_p(dw_i2c_remove), + .driver = { + .name = "i2c_designware", + .owner = THIS_MODULE, + }, +}; + +static int __init dw_i2c_init_driver(void) +{ + return platform_driver_probe(&dw_i2c_driver, dw_i2c_probe); +} +module_init(dw_i2c_init_driver); + +static void __exit dw_i2c_exit_driver(void) +{ + platform_driver_unregister(&dw_i2c_driver); +} +module_exit(dw_i2c_exit_driver); + +MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); +MODULE_DESCRIPTION("Synopsys DesignWare I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index b606db85525..ad8d2010c92 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -339,7 +339,7 @@ static int omap_i2c_init(struct omap_i2c_dev *dev) * to get longer filter period for better noise suppression. * The filter is iclk (fclk for HS) period. */ - if (dev->speed > 400 || cpu_is_omap_2430()) + if (dev->speed > 400 || cpu_is_omap2430()) internal_clk = 19200; else if (dev->speed > 100) internal_clk = 9600; diff --git a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c index 03c86209446..680e5975217 100644 --- a/drivers/ide/cmd64x.c +++ b/drivers/ide/cmd64x.c @@ -389,8 +389,7 @@ static const struct ide_port_info cmd64x_chipsets[] __devinitdata = { .init_chipset = init_chipset_cmd64x, .enablebits = {{0x51,0x04,0x04}, {0x51,0x08,0x08}}, .port_ops = &cmd648_port_ops, - .host_flags = IDE_HFLAG_SERIALIZE | - IDE_HFLAG_ABUSE_PREFETCH, + .host_flags = IDE_HFLAG_ABUSE_PREFETCH, .pio_mask = ATA_PIO5, .mwdma_mask = ATA_MWDMA2, .udma_mask = ATA_UDMA2, diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig index 95f45f9b8e5..f102fcc7e52 100644 --- a/drivers/ieee1394/Kconfig +++ b/drivers/ieee1394/Kconfig @@ -4,7 +4,7 @@ menu "IEEE 1394 (FireWire) support" source "drivers/firewire/Kconfig" config IEEE1394 - tristate "Stable FireWire stack" + tristate "Legacy alternative FireWire driver stack" depends on PCI || BROKEN help IEEE 1394 describes a high performance serial bus, which is also @@ -33,11 +33,9 @@ config IEEE1394_OHCI1394 module will be called ohci1394. NOTE: - - You should only build either ohci1394 or the new firewire-ohci driver, - but not both. If you nevertheless want to install both, you should - configure them only as modules and blacklist the driver(s) which you - don't want to have auto-loaded. Add either + If you want to install firewire-ohci and ohci1394 together, you + should configure them only as modules and blacklist the driver(s) + which you don't want to have auto-loaded. Add either blacklist firewire-ohci or @@ -46,12 +44,7 @@ config IEEE1394_OHCI1394 blacklist dv1394 to /etc/modprobe.conf or /etc/modprobe.d/* and update modprobe.conf - depending on your distribution. The latter two modules should be - blacklisted together with ohci1394 because they depend on ohci1394. - - If you have an old modprobe which doesn't implement the blacklist - directive, use "install modulename /bin/true" for the modules to be - blacklisted. + depending on your distribution. comment "PCILynx controller requires I2C" depends on IEEE1394 && I2C=n @@ -105,7 +98,7 @@ config IEEE1394_ETH1394_ROM_ENTRY default n config IEEE1394_ETH1394 - tristate "IP over 1394" + tristate "IP networking over 1394 (experimental)" depends on IEEE1394 && EXPERIMENTAL && INET select IEEE1394_ETH1394_ROM_ENTRY help diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c index ce511d8748c..5be1bd4fc7e 100644 --- a/drivers/infiniband/core/addr.c +++ b/drivers/infiniband/core/addr.c @@ -514,7 +514,7 @@ static struct notifier_block nb = { .notifier_call = netevent_callback }; -static int addr_init(void) +static int __init addr_init(void) { addr_wq = create_singlethread_workqueue("ib_addr"); if (!addr_wq) @@ -524,7 +524,7 @@ static int addr_init(void) return 0; } -static void addr_cleanup(void) +static void __exit addr_cleanup(void) { unregister_netevent_notifier(&nb); destroy_workqueue(addr_wq); diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 851de83ff45..075317884b5 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -2960,7 +2960,7 @@ static void cma_remove_one(struct ib_device *device) kfree(cma_dev); } -static int cma_init(void) +static int __init cma_init(void) { int ret, low, high, remaining; @@ -2990,7 +2990,7 @@ err: return ret; } -static void cma_cleanup(void) +static void __exit cma_cleanup(void) { ib_unregister_client(&cma_client); unregister_netdevice_notifier(&cma_nb); diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c index 9209c5332df..8b92f85d4dd 100644 --- a/drivers/infiniband/hw/ehca/ehca_hca.c +++ b/drivers/infiniband/hw/ehca/ehca_hca.c @@ -319,7 +319,7 @@ int ehca_query_gid(struct ib_device *ibdev, u8 port, ib_device); struct hipz_query_port *rblock; - if (index > 255) { + if (index < 0 || index > 255) { ehca_err(&shca->ib_device, "Invalid index: %x.", index); return -EINVAL; } diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index ce4e6eff479..fab18a2c74a 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -52,7 +52,7 @@ #include "ehca_tools.h" #include "hcp_if.h" -#define HCAD_VERSION "0027" +#define HCAD_VERSION "0028" MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>"); @@ -506,6 +506,7 @@ static int ehca_init_device(struct ehca_shca *shca) shca->ib_device.detach_mcast = ehca_detach_mcast; shca->ib_device.process_mad = ehca_process_mad; shca->ib_device.mmap = ehca_mmap; + shca->ib_device.dma_ops = &ehca_dma_mapping_ops; if (EHCA_BMASK_GET(HCA_CAP_SRQ, shca->hca_cap)) { shca->ib_device.uverbs_cmd_mask |= @@ -1028,17 +1029,23 @@ static int __init ehca_module_init(void) goto module_init1; } + ret = ehca_create_busmap(); + if (ret) { + ehca_gen_err("Cannot create busmap."); + goto module_init2; + } + ret = ibmebus_register_driver(&ehca_driver); if (ret) { ehca_gen_err("Cannot register eHCA device driver"); ret = -EINVAL; - goto module_init2; + goto module_init3; } ret = register_memory_notifier(&ehca_mem_nb); if (ret) { ehca_gen_err("Failed registering memory add/remove notifier"); - goto module_init3; + goto module_init4; } if (ehca_poll_all_eqs != 1) { @@ -1053,9 +1060,12 @@ static int __init ehca_module_init(void) return 0; -module_init3: +module_init4: ibmebus_unregister_driver(&ehca_driver); +module_init3: + ehca_destroy_busmap(); + module_init2: ehca_destroy_slab_caches(); @@ -1073,6 +1083,8 @@ static void __exit ehca_module_exit(void) unregister_memory_notifier(&ehca_mem_nb); + ehca_destroy_busmap(); + ehca_destroy_slab_caches(); ehca_destroy_comp_pool(); diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c index 72f83f7df61..7663a2a9f13 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.c +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c @@ -53,6 +53,38 @@ /* max number of rpages (per hcall register_rpages) */ #define MAX_RPAGES 512 +/* DMEM toleration management */ +#define EHCA_SECTSHIFT SECTION_SIZE_BITS +#define EHCA_SECTSIZE (1UL << EHCA_SECTSHIFT) +#define EHCA_HUGEPAGESHIFT 34 +#define EHCA_HUGEPAGE_SIZE (1UL << EHCA_HUGEPAGESHIFT) +#define EHCA_HUGEPAGE_PFN_MASK ((EHCA_HUGEPAGE_SIZE - 1) >> PAGE_SHIFT) +#define EHCA_INVAL_ADDR 0xFFFFFFFFFFFFFFFFULL +#define EHCA_DIR_INDEX_SHIFT 13 /* 8k Entries in 64k block */ +#define EHCA_TOP_INDEX_SHIFT (EHCA_DIR_INDEX_SHIFT * 2) +#define EHCA_MAP_ENTRIES (1 << EHCA_DIR_INDEX_SHIFT) +#define EHCA_TOP_MAP_SIZE (0x10000) /* currently fixed map size */ +#define EHCA_DIR_MAP_SIZE (0x10000) +#define EHCA_ENT_MAP_SIZE (0x10000) +#define EHCA_INDEX_MASK (EHCA_MAP_ENTRIES - 1) + +static unsigned long ehca_mr_len; + +/* + * Memory map data structures + */ +struct ehca_dir_bmap { + u64 ent[EHCA_MAP_ENTRIES]; +}; +struct ehca_top_bmap { + struct ehca_dir_bmap *dir[EHCA_MAP_ENTRIES]; +}; +struct ehca_bmap { + struct ehca_top_bmap *top[EHCA_MAP_ENTRIES]; +}; + +static struct ehca_bmap *ehca_bmap; + static struct kmem_cache *mr_cache; static struct kmem_cache *mw_cache; @@ -68,6 +100,8 @@ enum ehca_mr_pgsize { #define EHCA_MR_PGSHIFT1M 20 #define EHCA_MR_PGSHIFT16M 24 +static u64 ehca_map_vaddr(void *caddr); + static u32 ehca_encode_hwpage_size(u32 pgsize) { int log = ilog2(pgsize); @@ -135,7 +169,8 @@ struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags) goto get_dma_mr_exit0; } - ret = ehca_reg_maxmr(shca, e_maxmr, (u64 *)KERNELBASE, + ret = ehca_reg_maxmr(shca, e_maxmr, + (void *)ehca_map_vaddr((void *)KERNELBASE), mr_access_flags, e_pd, &e_maxmr->ib.ib_mr.lkey, &e_maxmr->ib.ib_mr.rkey); @@ -251,7 +286,7 @@ struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd, ret = ehca_reg_mr(shca, e_mr, iova_start, size, mr_access_flags, e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, - &e_mr->ib.ib_mr.rkey); + &e_mr->ib.ib_mr.rkey, EHCA_REG_MR); if (ret) { ib_mr = ERR_PTR(ret); goto reg_phys_mr_exit1; @@ -370,7 +405,7 @@ reg_user_mr_fallback: ret = ehca_reg_mr(shca, e_mr, (u64 *)virt, length, mr_access_flags, e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, - &e_mr->ib.ib_mr.rkey); + &e_mr->ib.ib_mr.rkey, EHCA_REG_MR); if (ret == -EINVAL && pginfo.hwpage_size > PAGE_SIZE) { ehca_warn(pd->device, "failed to register mr " "with hwpage_size=%llx", hwpage_size); @@ -794,7 +829,7 @@ struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd, ret = ehca_reg_mr(shca, e_fmr, NULL, fmr_attr->max_pages * (1 << fmr_attr->page_shift), mr_access_flags, e_pd, &pginfo, - &tmp_lkey, &tmp_rkey); + &tmp_lkey, &tmp_rkey, EHCA_REG_MR); if (ret) { ib_fmr = ERR_PTR(ret); goto alloc_fmr_exit1; @@ -983,6 +1018,10 @@ free_fmr_exit0: /*----------------------------------------------------------------------*/ +static int ehca_reg_bmap_mr_rpages(struct ehca_shca *shca, + struct ehca_mr *e_mr, + struct ehca_mr_pginfo *pginfo); + int ehca_reg_mr(struct ehca_shca *shca, struct ehca_mr *e_mr, u64 *iova_start, @@ -991,7 +1030,8 @@ int ehca_reg_mr(struct ehca_shca *shca, struct ehca_pd *e_pd, struct ehca_mr_pginfo *pginfo, u32 *lkey, /*OUT*/ - u32 *rkey) /*OUT*/ + u32 *rkey, /*OUT*/ + enum ehca_reg_type reg_type) { int ret; u64 h_ret; @@ -1015,7 +1055,13 @@ int ehca_reg_mr(struct ehca_shca *shca, e_mr->ipz_mr_handle = hipzout.handle; - ret = ehca_reg_mr_rpages(shca, e_mr, pginfo); + if (reg_type == EHCA_REG_BUSMAP_MR) + ret = ehca_reg_bmap_mr_rpages(shca, e_mr, pginfo); + else if (reg_type == EHCA_REG_MR) + ret = ehca_reg_mr_rpages(shca, e_mr, pginfo); + else + ret = -EINVAL; + if (ret) goto ehca_reg_mr_exit1; @@ -1316,7 +1362,7 @@ int ehca_rereg_mr(struct ehca_shca *shca, e_mr->fmr_map_cnt = save_mr.fmr_map_cnt; ret = ehca_reg_mr(shca, e_mr, iova_start, size, acl, - e_pd, pginfo, lkey, rkey); + e_pd, pginfo, lkey, rkey, EHCA_REG_MR); if (ret) { u32 offset = (u64)(&e_mr->flags) - (u64)e_mr; memcpy(&e_mr->flags, &(save_mr.flags), @@ -1409,7 +1455,7 @@ int ehca_unmap_one_fmr(struct ehca_shca *shca, ret = ehca_reg_mr(shca, e_fmr, NULL, (e_fmr->fmr_max_pages * e_fmr->fmr_page_size), e_fmr->acl, e_pd, &pginfo, &tmp_lkey, - &tmp_rkey); + &tmp_rkey, EHCA_REG_MR); if (ret) { u32 offset = (u64)(&e_fmr->flags) - (u64)e_fmr; memcpy(&e_fmr->flags, &(save_mr.flags), @@ -1478,6 +1524,90 @@ ehca_reg_smr_exit0: } /* end ehca_reg_smr() */ /*----------------------------------------------------------------------*/ +static inline void *ehca_calc_sectbase(int top, int dir, int idx) +{ + unsigned long ret = idx; + ret |= dir << EHCA_DIR_INDEX_SHIFT; + ret |= top << EHCA_TOP_INDEX_SHIFT; + return abs_to_virt(ret << SECTION_SIZE_BITS); +} + +#define ehca_bmap_valid(entry) \ + ((u64)entry != (u64)EHCA_INVAL_ADDR) + +static u64 ehca_reg_mr_section(int top, int dir, int idx, u64 *kpage, + struct ehca_shca *shca, struct ehca_mr *mr, + struct ehca_mr_pginfo *pginfo) +{ + u64 h_ret = 0; + unsigned long page = 0; + u64 rpage = virt_to_abs(kpage); + int page_count; + + void *sectbase = ehca_calc_sectbase(top, dir, idx); + if ((unsigned long)sectbase & (pginfo->hwpage_size - 1)) { + ehca_err(&shca->ib_device, "reg_mr_section will probably fail:" + "hwpage_size does not fit to " + "section start address"); + } + page_count = EHCA_SECTSIZE / pginfo->hwpage_size; + + while (page < page_count) { + u64 rnum; + for (rnum = 0; (rnum < MAX_RPAGES) && (page < page_count); + rnum++) { + void *pg = sectbase + ((page++) * pginfo->hwpage_size); + kpage[rnum] = virt_to_abs(pg); + } + + h_ret = hipz_h_register_rpage_mr(shca->ipz_hca_handle, mr, + ehca_encode_hwpage_size(pginfo->hwpage_size), + 0, rpage, rnum); + + if ((h_ret != H_SUCCESS) && (h_ret != H_PAGE_REGISTERED)) { + ehca_err(&shca->ib_device, "register_rpage_mr failed"); + return h_ret; + } + } + return h_ret; +} + +static u64 ehca_reg_mr_sections(int top, int dir, u64 *kpage, + struct ehca_shca *shca, struct ehca_mr *mr, + struct ehca_mr_pginfo *pginfo) +{ + u64 hret = H_SUCCESS; + int idx; + + for (idx = 0; idx < EHCA_MAP_ENTRIES; idx++) { + if (!ehca_bmap_valid(ehca_bmap->top[top]->dir[dir]->ent[idx])) + continue; + + hret = ehca_reg_mr_section(top, dir, idx, kpage, shca, mr, + pginfo); + if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED)) + return hret; + } + return hret; +} + +static u64 ehca_reg_mr_dir_sections(int top, u64 *kpage, struct ehca_shca *shca, + struct ehca_mr *mr, + struct ehca_mr_pginfo *pginfo) +{ + u64 hret = H_SUCCESS; + int dir; + + for (dir = 0; dir < EHCA_MAP_ENTRIES; dir++) { + if (!ehca_bmap_valid(ehca_bmap->top[top]->dir[dir])) + continue; + + hret = ehca_reg_mr_sections(top, dir, kpage, shca, mr, pginfo); + if ((hret != H_SUCCESS) && (hret != H_PAGE_REGISTERED)) + return hret; + } + return hret; +} /* register internal max-MR to internal SHCA */ int ehca_reg_internal_maxmr( @@ -1495,6 +1625,11 @@ int ehca_reg_internal_maxmr( u32 num_hwpages; u64 hw_pgsize; + if (!ehca_bmap) { + ret = -EFAULT; + goto ehca_reg_internal_maxmr_exit0; + } + e_mr = ehca_mr_new(); if (!e_mr) { ehca_err(&shca->ib_device, "out of memory"); @@ -1504,8 +1639,8 @@ int ehca_reg_internal_maxmr( e_mr->flags |= EHCA_MR_FLAG_MAXMR; /* register internal max-MR on HCA */ - size_maxmr = (u64)high_memory - PAGE_OFFSET; - iova_start = (u64 *)KERNELBASE; + size_maxmr = ehca_mr_len; + iova_start = (u64 *)ehca_map_vaddr((void *)KERNELBASE); ib_pbuf.addr = 0; ib_pbuf.size = size_maxmr; num_kpages = NUM_CHUNKS(((u64)iova_start % PAGE_SIZE) + size_maxmr, @@ -1524,7 +1659,7 @@ int ehca_reg_internal_maxmr( ret = ehca_reg_mr(shca, e_mr, iova_start, size_maxmr, 0, e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, - &e_mr->ib.ib_mr.rkey); + &e_mr->ib.ib_mr.rkey, EHCA_REG_BUSMAP_MR); if (ret) { ehca_err(&shca->ib_device, "reg of internal max MR failed, " "e_mr=%p iova_start=%p size_maxmr=%llx num_kpages=%x " @@ -2077,8 +2212,8 @@ int ehca_mr_is_maxmr(u64 size, u64 *iova_start) { /* a MR is treated as max-MR only if it fits following: */ - if ((size == ((u64)high_memory - PAGE_OFFSET)) && - (iova_start == (void *)KERNELBASE)) { + if ((size == ehca_mr_len) && + (iova_start == (void *)ehca_map_vaddr((void *)KERNELBASE))) { ehca_gen_dbg("this is a max-MR"); return 1; } else @@ -2184,3 +2319,350 @@ void ehca_cleanup_mrmw_cache(void) if (mw_cache) kmem_cache_destroy(mw_cache); } + +static inline int ehca_init_top_bmap(struct ehca_top_bmap *ehca_top_bmap, + int dir) +{ + if (!ehca_bmap_valid(ehca_top_bmap->dir[dir])) { + ehca_top_bmap->dir[dir] = + kmalloc(sizeof(struct ehca_dir_bmap), GFP_KERNEL); + if (!ehca_top_bmap->dir[dir]) + return -ENOMEM; + /* Set map block to 0xFF according to EHCA_INVAL_ADDR */ + memset(ehca_top_bmap->dir[dir], 0xFF, EHCA_ENT_MAP_SIZE); + } + return 0; +} + +static inline int ehca_init_bmap(struct ehca_bmap *ehca_bmap, int top, int dir) +{ + if (!ehca_bmap_valid(ehca_bmap->top[top])) { + ehca_bmap->top[top] = + kmalloc(sizeof(struct ehca_top_bmap), GFP_KERNEL); + if (!ehca_bmap->top[top]) + return -ENOMEM; + /* Set map block to 0xFF according to EHCA_INVAL_ADDR */ + memset(ehca_bmap->top[top], 0xFF, EHCA_DIR_MAP_SIZE); + } + return ehca_init_top_bmap(ehca_bmap->top[top], dir); +} + +static inline int ehca_calc_index(unsigned long i, unsigned long s) +{ + return (i >> s) & EHCA_INDEX_MASK; +} + +void ehca_destroy_busmap(void) +{ + int top, dir; + + if (!ehca_bmap) + return; + + for (top = 0; top < EHCA_MAP_ENTRIES; top++) { + if (!ehca_bmap_valid(ehca_bmap->top[top])) + continue; + for (dir = 0; dir < EHCA_MAP_ENTRIES; dir++) { + if (!ehca_bmap_valid(ehca_bmap->top[top]->dir[dir])) + continue; + + kfree(ehca_bmap->top[top]->dir[dir]); + } + + kfree(ehca_bmap->top[top]); + } + + kfree(ehca_bmap); + ehca_bmap = NULL; +} + +static int ehca_update_busmap(unsigned long pfn, unsigned long nr_pages) +{ + unsigned long i, start_section, end_section; + int top, dir, idx; + + if (!nr_pages) + return 0; + + if (!ehca_bmap) { + ehca_bmap = kmalloc(sizeof(struct ehca_bmap), GFP_KERNEL); + if (!ehca_bmap) + return -ENOMEM; + /* Set map block to 0xFF according to EHCA_INVAL_ADDR */ + memset(ehca_bmap, 0xFF, EHCA_TOP_MAP_SIZE); + } + + start_section = phys_to_abs(pfn * PAGE_SIZE) / EHCA_SECTSIZE; + end_section = phys_to_abs((pfn + nr_pages) * PAGE_SIZE) / EHCA_SECTSIZE; + for (i = start_section; i < end_section; i++) { + int ret; + top = ehca_calc_index(i, EHCA_TOP_INDEX_SHIFT); + dir = ehca_calc_index(i, EHCA_DIR_INDEX_SHIFT); + idx = i & EHCA_INDEX_MASK; + + ret = ehca_init_bmap(ehca_bmap, top, dir); + if (ret) { + ehca_destroy_busmap(); + return ret; + } + ehca_bmap->top[top]->dir[dir]->ent[idx] = ehca_mr_len; + ehca_mr_len += EHCA_SECTSIZE; + } + return 0; +} + +static int ehca_is_hugepage(unsigned long pfn) +{ + int page_order; + + if (pfn & EHCA_HUGEPAGE_PFN_MASK) + return 0; + + page_order = compound_order(pfn_to_page(pfn)); + if (page_order + PAGE_SHIFT != EHCA_HUGEPAGESHIFT) + return 0; + + return 1; +} + +static int ehca_create_busmap_callback(unsigned long initial_pfn, + unsigned long total_nr_pages, void *arg) +{ + int ret; + unsigned long pfn, start_pfn, end_pfn, nr_pages; + + if ((total_nr_pages * PAGE_SIZE) < EHCA_HUGEPAGE_SIZE) + return ehca_update_busmap(initial_pfn, total_nr_pages); + + /* Given chunk is >= 16GB -> check for hugepages */ + start_pfn = initial_pfn; + end_pfn = initial_pfn + total_nr_pages; + pfn = start_pfn; + + while (pfn < end_pfn) { + if (ehca_is_hugepage(pfn)) { + /* Add mem found in front of the hugepage */ + nr_pages = pfn - start_pfn; + ret = ehca_update_busmap(start_pfn, nr_pages); + if (ret) + return ret; + /* Skip the hugepage */ + pfn += (EHCA_HUGEPAGE_SIZE / PAGE_SIZE); + start_pfn = pfn; + } else + pfn += (EHCA_SECTSIZE / PAGE_SIZE); + } + + /* Add mem found behind the hugepage(s) */ + nr_pages = pfn - start_pfn; + return ehca_update_busmap(start_pfn, nr_pages); +} + +int ehca_create_busmap(void) +{ + int ret; + + ehca_mr_len = 0; + ret = walk_memory_resource(0, 1ULL << MAX_PHYSMEM_BITS, NULL, + ehca_create_busmap_callback); + return ret; +} + +static int ehca_reg_bmap_mr_rpages(struct ehca_shca *shca, + struct ehca_mr *e_mr, + struct ehca_mr_pginfo *pginfo) +{ + int top; + u64 hret, *kpage; + + kpage = ehca_alloc_fw_ctrlblock(GFP_KERNEL); + if (!kpage) { + ehca_err(&shca->ib_device, "kpage alloc failed"); + return -ENOMEM; + } + for (top = 0; top < EHCA_MAP_ENTRIES; top++) { + if (!ehca_bmap_valid(ehca_bmap->top[top])) + continue; + hret = ehca_reg_mr_dir_sections(top, kpage, shca, e_mr, pginfo); + if ((hret != H_PAGE_REGISTERED) && (hret != H_SUCCESS)) + break; + } + + ehca_free_fw_ctrlblock(kpage); + + if (hret == H_SUCCESS) + return 0; /* Everything is fine */ + else { + ehca_err(&shca->ib_device, "ehca_reg_bmap_mr_rpages failed, " + "h_ret=%lli e_mr=%p top=%x lkey=%x " + "hca_hndl=%llx mr_hndl=%llx", hret, e_mr, top, + e_mr->ib.ib_mr.lkey, + shca->ipz_hca_handle.handle, + e_mr->ipz_mr_handle.handle); + return ehca2ib_return_code(hret); + } +} + +static u64 ehca_map_vaddr(void *caddr) +{ + int top, dir, idx; + unsigned long abs_addr, offset; + u64 entry; + + if (!ehca_bmap) + return EHCA_INVAL_ADDR; + + abs_addr = virt_to_abs(caddr); + top = ehca_calc_index(abs_addr, EHCA_TOP_INDEX_SHIFT + EHCA_SECTSHIFT); + if (!ehca_bmap_valid(ehca_bmap->top[top])) + return EHCA_INVAL_ADDR; + + dir = ehca_calc_index(abs_addr, EHCA_DIR_INDEX_SHIFT + EHCA_SECTSHIFT); + if (!ehca_bmap_valid(ehca_bmap->top[top]->dir[dir])) + return EHCA_INVAL_ADDR; + + idx = ehca_calc_index(abs_addr, EHCA_SECTSHIFT); + + entry = ehca_bmap->top[top]->dir[dir]->ent[idx]; + if (ehca_bmap_valid(entry)) { + offset = (unsigned long)caddr & (EHCA_SECTSIZE - 1); + return entry | offset; + } else + return EHCA_INVAL_ADDR; +} + +static int ehca_dma_mapping_error(struct ib_device *dev, u64 dma_addr) +{ + return dma_addr == EHCA_INVAL_ADDR; +} + +static u64 ehca_dma_map_single(struct ib_device *dev, void *cpu_addr, + size_t size, enum dma_data_direction direction) +{ + if (cpu_addr) + return ehca_map_vaddr(cpu_addr); + else + return EHCA_INVAL_ADDR; +} + +static void ehca_dma_unmap_single(struct ib_device *dev, u64 addr, size_t size, + enum dma_data_direction direction) +{ + /* This is only a stub; nothing to be done here */ +} + +static u64 ehca_dma_map_page(struct ib_device *dev, struct page *page, + unsigned long offset, size_t size, + enum dma_data_direction direction) +{ + u64 addr; + + if (offset + size > PAGE_SIZE) + return EHCA_INVAL_ADDR; + + addr = ehca_map_vaddr(page_address(page)); + if (!ehca_dma_mapping_error(dev, addr)) + addr += offset; + + return addr; +} + +static void ehca_dma_unmap_page(struct ib_device *dev, u64 addr, size_t size, + enum dma_data_direction direction) +{ + /* This is only a stub; nothing to be done here */ +} + +static int ehca_dma_map_sg(struct ib_device *dev, struct scatterlist *sgl, + int nents, enum dma_data_direction direction) +{ + struct scatterlist *sg; + int i; + + for_each_sg(sgl, sg, nents, i) { + u64 addr; + addr = ehca_map_vaddr(sg_virt(sg)); + if (ehca_dma_mapping_error(dev, addr)) + return 0; + + sg->dma_address = addr; + sg->dma_length = sg->length; + } + return nents; +} + +static void ehca_dma_unmap_sg(struct ib_device *dev, struct scatterlist *sg, + int nents, enum dma_data_direction direction) +{ + /* This is only a stub; nothing to be done here */ +} + +static u64 ehca_dma_address(struct ib_device *dev, struct scatterlist *sg) +{ + return sg->dma_address; +} + +static unsigned int ehca_dma_len(struct ib_device *dev, struct scatterlist *sg) +{ + return sg->length; +} + +static void ehca_dma_sync_single_for_cpu(struct ib_device *dev, u64 addr, + size_t size, + enum dma_data_direction dir) +{ + dma_sync_single_for_cpu(dev->dma_device, addr, size, dir); +} + +static void ehca_dma_sync_single_for_device(struct ib_device *dev, u64 addr, + size_t size, + enum dma_data_direction dir) +{ + dma_sync_single_for_device(dev->dma_device, addr, size, dir); +} + +static void *ehca_dma_alloc_coherent(struct ib_device *dev, size_t size, + u64 *dma_handle, gfp_t flag) +{ + struct page *p; + void *addr = NULL; + u64 dma_addr; + + p = alloc_pages(flag, get_order(size)); + if (p) { + addr = page_address(p); + dma_addr = ehca_map_vaddr(addr); + if (ehca_dma_mapping_error(dev, dma_addr)) { + free_pages((unsigned long)addr, get_order(size)); + return NULL; + } + if (dma_handle) + *dma_handle = dma_addr; + return addr; + } + return NULL; +} + +static void ehca_dma_free_coherent(struct ib_device *dev, size_t size, + void *cpu_addr, u64 dma_handle) +{ + if (cpu_addr && size) + free_pages((unsigned long)cpu_addr, get_order(size)); +} + + +struct ib_dma_mapping_ops ehca_dma_mapping_ops = { + .mapping_error = ehca_dma_mapping_error, + .map_single = ehca_dma_map_single, + .unmap_single = ehca_dma_unmap_single, + .map_page = ehca_dma_map_page, + .unmap_page = ehca_dma_unmap_page, + .map_sg = ehca_dma_map_sg, + .unmap_sg = ehca_dma_unmap_sg, + .dma_address = ehca_dma_address, + .dma_len = ehca_dma_len, + .sync_single_for_cpu = ehca_dma_sync_single_for_cpu, + .sync_single_for_device = ehca_dma_sync_single_for_device, + .alloc_coherent = ehca_dma_alloc_coherent, + .free_coherent = ehca_dma_free_coherent, +}; diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.h b/drivers/infiniband/hw/ehca/ehca_mrmw.h index bc8f4e31c12..50d8b51306d 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.h +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.h @@ -42,6 +42,11 @@ #ifndef _EHCA_MRMW_H_ #define _EHCA_MRMW_H_ +enum ehca_reg_type { + EHCA_REG_MR, + EHCA_REG_BUSMAP_MR +}; + int ehca_reg_mr(struct ehca_shca *shca, struct ehca_mr *e_mr, u64 *iova_start, @@ -50,7 +55,8 @@ int ehca_reg_mr(struct ehca_shca *shca, struct ehca_pd *e_pd, struct ehca_mr_pginfo *pginfo, u32 *lkey, - u32 *rkey); + u32 *rkey, + enum ehca_reg_type reg_type); int ehca_reg_mr_rpages(struct ehca_shca *shca, struct ehca_mr *e_mr, @@ -118,4 +124,9 @@ void ehca_mrmw_reverse_map_acl(const u32 *hipz_acl, void ehca_mr_deletenew(struct ehca_mr *mr); +int ehca_create_busmap(void); + +void ehca_destroy_busmap(void); + +extern struct ib_dma_mapping_ops ehca_dma_mapping_ops; #endif /*_EHCA_MRMW_H_*/ diff --git a/drivers/infiniband/hw/mthca/mthca_mr.c b/drivers/infiniband/hw/mthca/mthca_mr.c index d606edf1085..065b2089987 100644 --- a/drivers/infiniband/hw/mthca/mthca_mr.c +++ b/drivers/infiniband/hw/mthca/mthca_mr.c @@ -352,10 +352,14 @@ static void mthca_arbel_write_mtt_seg(struct mthca_dev *dev, BUG_ON(!mtts); + dma_sync_single_for_cpu(&dev->pdev->dev, dma_handle, + list_len * sizeof (u64), DMA_TO_DEVICE); + for (i = 0; i < list_len; ++i) mtts[i] = cpu_to_be64(buffer_list[i] | MTHCA_MTT_FLAG_PRESENT); - dma_sync_single(&dev->pdev->dev, dma_handle, list_len * sizeof (u64), DMA_TO_DEVICE); + dma_sync_single_for_device(&dev->pdev->dev, dma_handle, + list_len * sizeof (u64), DMA_TO_DEVICE); } int mthca_write_mtt(struct mthca_dev *dev, struct mthca_mtt *mtt, @@ -803,12 +807,15 @@ int mthca_arbel_map_phys_fmr(struct ib_fmr *ibfmr, u64 *page_list, wmb(); + dma_sync_single_for_cpu(&dev->pdev->dev, fmr->mem.arbel.dma_handle, + list_len * sizeof(u64), DMA_TO_DEVICE); + for (i = 0; i < list_len; ++i) fmr->mem.arbel.mtts[i] = cpu_to_be64(page_list[i] | MTHCA_MTT_FLAG_PRESENT); - dma_sync_single(&dev->pdev->dev, fmr->mem.arbel.dma_handle, - list_len * sizeof(u64), DMA_TO_DEVICE); + dma_sync_single_for_device(&dev->pdev->dev, fmr->mem.arbel.dma_handle, + list_len * sizeof(u64), DMA_TO_DEVICE); fmr->mem.arbel.mpt->key = cpu_to_be32(key); fmr->mem.arbel.mpt->lkey = cpu_to_be32(key); diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 11c7d664201..114b802771a 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -472,6 +472,7 @@ int schedule_nes_timer(struct nes_cm_node *cm_node, struct sk_buff *skb, static void nes_retrans_expired(struct nes_cm_node *cm_node) { + struct iw_cm_id *cm_id = cm_node->cm_id; switch (cm_node->state) { case NES_CM_STATE_SYN_RCVD: case NES_CM_STATE_CLOSING: @@ -479,7 +480,9 @@ static void nes_retrans_expired(struct nes_cm_node *cm_node) break; case NES_CM_STATE_LAST_ACK: case NES_CM_STATE_FIN_WAIT1: - case NES_CM_STATE_MPAREJ_RCVD: + if (cm_node->cm_id) + cm_id->rem_ref(cm_id); + cm_node->state = NES_CM_STATE_CLOSED; send_reset(cm_node, NULL); break; default: @@ -1406,6 +1409,7 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, case NES_CM_STATE_CLOSED: drop_packet(skb); break; + case NES_CM_STATE_FIN_WAIT1: case NES_CM_STATE_LAST_ACK: cm_node->cm_id->rem_ref(cm_node->cm_id); case NES_CM_STATE_TIME_WAIT: @@ -1413,8 +1417,6 @@ static void handle_rst_pkt(struct nes_cm_node *cm_node, struct sk_buff *skb, rem_ref_cm_node(cm_node->cm_core, cm_node); drop_packet(skb); break; - case NES_CM_STATE_FIN_WAIT1: - nes_debug(NES_DBG_CM, "Bad state %s[%u]\n", __func__, __LINE__); default: drop_packet(skb); break; diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 64d5cfd8f38..21e0fd336cf 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -654,7 +654,7 @@ static int nes_query_device(struct ib_device *ibdev, struct ib_device_attr *prop default: props->max_qp_rd_atom = 0; } - props->max_qp_init_rd_atom = props->max_qp_wr; + props->max_qp_init_rd_atom = props->max_qp_rd_atom; props->atomic_cap = IB_ATOMIC_NONE; props->max_map_per_fmr = 1; diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index 3fe158ac7bb..4216328552f 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -2750,3 +2750,26 @@ IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE] = { [0x1b] = KEY_B, /*recall*/ }; EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec); + +/* EVGA inDtube + Devin Heitmueller <devin.heitmueller@gmail.com> + */ +IR_KEYTAB_TYPE ir_codes_evga_indtube[IR_KEYTAB_SIZE] = { + [0x12] = KEY_POWER, + [0x02] = KEY_MODE, /* TV */ + [0x14] = KEY_MUTE, + [0x1a] = KEY_CHANNELUP, + [0x16] = KEY_TV2, /* PIP */ + [0x1d] = KEY_VOLUMEUP, + [0x05] = KEY_CHANNELDOWN, + [0x0f] = KEY_PLAYPAUSE, + [0x19] = KEY_VOLUMEDOWN, + [0x1c] = KEY_REWIND, + [0x0d] = KEY_RECORD, + [0x18] = KEY_FORWARD, + [0x1e] = KEY_PREVIOUS, + [0x1b] = KEY_STOP, + [0x1f] = KEY_NEXT, + [0x13] = KEY_CAMERA, +}; +EXPORT_SYMBOL_GPL(ir_codes_evga_indtube); diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h index 8a1332c2031..bf4e9b63304 100644 --- a/drivers/media/dvb/frontends/stv0900.h +++ b/drivers/media/dvb/frontends/stv0900.h @@ -29,6 +29,11 @@ #include <linux/dvb/frontend.h> #include "dvb_frontend.h" +struct stv0900_reg { + u16 addr; + u8 val; +}; + struct stv0900_config { u8 demod_address; u32 xtal; @@ -38,7 +43,7 @@ struct stv0900_config { u8 path1_mode; u8 path2_mode; - + struct stv0900_reg *ts_config_regs; u8 tun1_maddress;/* 0, 1, 2, 3 for 0xc0, 0xc2, 0xc4, 0xc6 */ u8 tun2_maddress; u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 8499bcf7f25..1da045fbb4e 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -149,31 +149,31 @@ void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr, dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); } -u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg_addr) +u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg) { - u8 data[2]; int ret; - struct i2c_msg i2cmsg = { - .addr = i_params->i2c_addr, - .flags = 0, - .len = 2, - .buf = data, + u8 b0[] = { MSB(reg), LSB(reg) }; + u8 buf = 0; + struct i2c_msg msg[] = { + { + .addr = i_params->i2c_addr, + .flags = 0, + .buf = b0, + .len = 2, + }, { + .addr = i_params->i2c_addr, + .flags = I2C_M_RD, + .buf = &buf, + .len = 1, + }, }; - data[0] = MSB(reg_addr); - data[1] = LSB(reg_addr); - - ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1); - if (ret != 1) - dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); - - i2cmsg.flags = I2C_M_RD; - i2cmsg.len = 1; - ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1); - if (ret != 1) - dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); + ret = i2c_transfer(i_params->i2c_adap, msg, 2); + if (ret != 2) + dprintk(KERN_ERR "%s: i2c error %d, reg[0x%02x]\n", + __func__, ret, reg); - return data[0]; + return buf; } void extract_mask_pos(u32 label, u8 *mask, u8 *pos) @@ -712,6 +712,44 @@ static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, return c_n; } +static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *i_params = state->internal; + enum fe_stv0900_demod_num demod = state->demod; + u8 err_val1, err_val0; + s32 err_field1, err_field0; + u32 header_err_val = 0; + + *ucblocks = 0x0; + if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { + /* DVB-S2 delineator errors count */ + + /* retreiving number for errnous headers */ + dmd_reg(err_field0, R0900_P1_BBFCRCKO0, + R0900_P2_BBFCRCKO0); + dmd_reg(err_field1, R0900_P1_BBFCRCKO1, + R0900_P2_BBFCRCKO1); + + err_val1 = stv0900_read_reg(i_params, err_field1); + err_val0 = stv0900_read_reg(i_params, err_field0); + header_err_val = (err_val1<<8) | err_val0; + + /* retreiving number for errnous packets */ + dmd_reg(err_field0, R0900_P1_UPCRCKO0, + R0900_P2_UPCRCKO0); + dmd_reg(err_field1, R0900_P1_UPCRCKO1, + R0900_P2_UPCRCKO1); + + err_val1 = stv0900_read_reg(i_params, err_field1); + err_val0 = stv0900_read_reg(i_params, err_field0); + *ucblocks = (err_val1<<8) | err_val0; + *ucblocks += header_err_val; + } + + return 0; +} + static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr) { *snr = stv0900_carr_get_quality(fe, @@ -1355,7 +1393,7 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, struct stv0900_state *state = fe->demodulator_priv; enum fe_stv0900_error error = STV0900_NO_ERROR; enum fe_stv0900_error demodError = STV0900_NO_ERROR; - int selosci; + int selosci, i; struct stv0900_inode *temp_int = find_inode(state->i2c_adap, state->config->demod_address); @@ -1402,7 +1440,23 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, stv0900_write_bits(state->internal, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); stv0900_write_bits(state->internal, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); - stv0900_set_ts_parallel_serial(state->internal, p_init->path1_ts_clock, p_init->path2_ts_clock); + state->internal->ts_config = p_init->ts_config; + if (state->internal->ts_config == NULL) + stv0900_set_ts_parallel_serial(state->internal, + p_init->path1_ts_clock, + p_init->path2_ts_clock); + else { + for (i = 0; state->internal->ts_config[i].addr != 0xffff; i++) + stv0900_write_reg(state->internal, + state->internal->ts_config[i].addr, + state->internal->ts_config[i].val); + + stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 0); + } + stv0900_write_bits(state->internal, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); switch (p_init->tuner1_adc) { case 1: @@ -1882,6 +1936,7 @@ static struct dvb_frontend_ops stv0900_ops = { .read_ber = stv0900_read_ber, .read_signal_strength = stv0900_read_signal_strength, .read_snr = stv0900_read_snr, + .read_ucblocks = stv0900_read_ucblocks, }; struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, @@ -1915,6 +1970,7 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, init_params.tun1_iq_inversion = STV0900_IQ_NORMAL; init_params.tuner1_adc = config->tun1_adc; init_params.path2_ts_clock = config->path2_mode; + init_params.ts_config = config->ts_config_regs; init_params.tun2_maddress = config->tun2_maddress; init_params.tuner2_adc = config->tun2_adc; init_params.tun2_iq_inversion = STV0900_IQ_SWAPPED; diff --git a/drivers/media/dvb/frontends/stv0900_priv.h b/drivers/media/dvb/frontends/stv0900_priv.h index 67dc8ec634e..5ed7a145c7d 100644 --- a/drivers/media/dvb/frontends/stv0900_priv.h +++ b/drivers/media/dvb/frontends/stv0900_priv.h @@ -271,6 +271,7 @@ struct stv0900_init_params{ /* IQ from the tuner2 to the demod */ enum stv0900_iq_inversion tun2_iq_inversion; + struct stv0900_reg *ts_config; }; struct stv0900_search_params { @@ -363,6 +364,7 @@ struct stv0900_internal{ u8 i2c_addr; u8 clkmode;/* 0 for CLKI, 2 for XTALI */ u8 chip_id; + struct stv0900_reg *ts_config; enum fe_stv0900_error errs; int dmds_used; }; diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 96ef745a2e4..488bdfb34fb 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -2674,7 +2674,7 @@ static u8 stv090x_optimize_carloop(struct stv090x_state *state, enum stv090x_mod static u8 stv090x_optimize_carloop_short(struct stv090x_state *state) { - struct stv090x_short_frame_crloop *short_crl; + struct stv090x_short_frame_crloop *short_crl = NULL; s32 index = 0; u8 aclc = 0x0b; @@ -2694,10 +2694,13 @@ static u8 stv090x_optimize_carloop_short(struct stv090x_state *state) break; } - if (state->dev_ver >= 0x30) - short_crl = stv090x_s2_short_crl_cut20; - else if (state->dev_ver >= 0x20) + if (state->dev_ver >= 0x30) { + /* Cut 3.0 and up */ short_crl = stv090x_s2_short_crl_cut30; + } else { + /* Cut 2.0 and up: we don't support cuts older than 2.0 */ + short_crl = stv090x_s2_short_crl_cut20; + } if (state->srate <= 3000000) aclc = short_crl[index].crl_2; diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c index 4302c563a6b..cc8862ce4aa 100644 --- a/drivers/media/dvb/frontends/tda10048.c +++ b/drivers/media/dvb/frontends/tda10048.c @@ -210,6 +210,7 @@ static struct pll_tab { { TDA10048_CLK_4000, TDA10048_IF_36130, 10, 0, 0 }, { TDA10048_CLK_16000, TDA10048_IF_3300, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_3500, 10, 3, 0 }, + { TDA10048_CLK_16000, TDA10048_IF_3800, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_4000, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_4300, 10, 3, 0 }, { TDA10048_CLK_16000, TDA10048_IF_36130, 10, 3, 0 }, diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index 32be382f0e9..a246903c334 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -1422,8 +1422,8 @@ int smscore_gpio_configure(struct smscore_device_t *coredev, u8 PinNum, struct smscore_gpio_config *pGpioConfig) { u32 totalLen; - u32 TranslatedPinNum; - u32 GroupNum; + u32 TranslatedPinNum = 0; + u32 GroupNum = 0; u32 ElectricChar; u32 groupCfg; void *buffer; diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c index 393623818ad..3cd76dddb6a 100644 --- a/drivers/media/radio/radio-tea5764.c +++ b/drivers/media/radio/radio-tea5764.c @@ -322,7 +322,9 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->rangehigh = FREQ_MAX * FREQ_MUL; v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; if (r->tunchk & TEA5764_TUNCHK_STEREO) - v->rxsubchans = V4L2_TUNER_SUB_STEREO; + v->rxsubchans = V4L2_TUNER_SUB_STEREO; + else + v->rxsubchans = V4L2_TUNER_SUB_MONO; v->audmode = tea5764_get_audout_mode(radio); v->signal = TEA5764_TUNCHK_LEVEL(r->tunchk) * 0xffff / 0xf; v->afc = TEA5764_TUNCHK_IFCNT(r->tunchk); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 94f440535c6..061e147f6f2 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -866,9 +866,13 @@ config USB_W9968CF module will be called w9968cf. config USB_OV511 - tristate "USB OV511 Camera support" + tristate "USB OV511 Camera support (DEPRECATED)" depends on VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the ov511 / ov518 support of the gspca module + you need atleast version 0.6.0 of libv4l. + Say Y here if you want to connect this type of camera to your computer's USB port. See <file:Documentation/video4linux/ov511.txt> for more information and for a list of supported cameras. diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 8e35c3aed54..5136df19833 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -61,6 +61,8 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { /* Standard V4L2 controls */ + case V4L2_CID_USER_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_BRIGHTNESS: case V4L2_CID_HUE: case V4L2_CID_SATURATION: diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 6a9464079b4..28f48f41f21 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -1052,22 +1052,13 @@ int cx231xx_set_audio_decoder_input(struct cx231xx *dev, /* Set resolution of the video */ int cx231xx_resolution_set(struct cx231xx *dev) { - int width, height; - u32 hscale, vscale; - int status = 0; - - width = dev->width; - height = dev->height; - - get_scale(dev, width, height, &hscale, &vscale); - /* set horzontal scale */ - status = vid_blk_write_word(dev, HSCALE_CTRL, hscale); + int status = vid_blk_write_word(dev, HSCALE_CTRL, dev->hscale); + if (status) + return status; /* set vertical scale */ - status = vid_blk_write_word(dev, VSCALE_CTRL, vscale); - - return status; + return vid_blk_write_word(dev, VSCALE_CTRL, dev->vscale); } /****************************************************************************** @@ -2055,7 +2046,7 @@ int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type) int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type) { - int rc; + int rc = -1; u32 ep_mask = -1; struct pcb_config *pcb_config; diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index a23ae73fe63..609bae6098d 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -893,9 +893,9 @@ static int check_dev(struct cx231xx *dev) return 0; } -void get_scale(struct cx231xx *dev, - unsigned int width, unsigned int height, - unsigned int *hscale, unsigned int *vscale) +static void get_scale(struct cx231xx *dev, + unsigned int width, unsigned int height, + unsigned int *hscale, unsigned int *vscale) { unsigned int maxw = norm_maxw(dev); unsigned int maxh = norm_maxh(dev); @@ -907,10 +907,6 @@ void get_scale(struct cx231xx *dev, *vscale = (((unsigned long)maxh) << 12) / height - 4096L; if (*vscale >= 0x4000) *vscale = 0x3fff; - - dev->hscale = *hscale; - dev->vscale = *vscale; - } /* ------------------------------------------------------------------ @@ -955,8 +951,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct cx231xx_fh *fh = priv; struct cx231xx *dev = fh->dev; - int width = f->fmt.pix.width; - int height = f->fmt.pix.height; + unsigned int width = f->fmt.pix.width; + unsigned int height = f->fmt.pix.height; unsigned int maxw = norm_maxw(dev); unsigned int maxh = norm_maxh(dev); unsigned int hscale, vscale; @@ -971,17 +967,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* width must even because of the YUYV format height must be even because of interlacing */ - height &= 0xfffe; - width &= 0xfffe; - - if (unlikely(height < 32)) - height = 32; - if (unlikely(height > maxh)) - height = maxh; - if (unlikely(width < 48)) - width = 48; - if (unlikely(width > maxw)) - width = maxw; + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); get_scale(dev, width, height, &hscale, &vscale); diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index e38eb2d425f..a0f823ac6b8 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -722,9 +722,6 @@ int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input); int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input); int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev); int cx231xx_set_audio_input(struct cx231xx *dev, u8 input); -void get_scale(struct cx231xx *dev, - unsigned int width, unsigned int height, - unsigned int *hscale, unsigned int *vscale); /* Provided by cx231xx-video.c */ int cx231xx_register_extension(struct cx231xx_ops *dev); diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c index 8ded5294633..4c8e95853fa 100644 --- a/drivers/media/video/cx2341x.c +++ b/drivers/media/video/cx2341x.c @@ -500,6 +500,8 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params, int err; switch (qctrl->id) { + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_MPEG_STREAM_TYPE: return v4l2_ctrl_query_fill(qctrl, V4L2_MPEG_STREAM_TYPE_MPEG2_PS, diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index e236df23370..48a975134ac 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -45,6 +45,7 @@ #include "dibx000_common.h" #include "zl10353.h" #include "stv0900.h" +#include "stv0900_reg.h" #include "stv6110.h" #include "lnbh24.h" #include "cx24116.h" @@ -242,12 +243,22 @@ static struct tda18271_std_map hauppauge_tda18271_std_map = { .if_lvl = 6, .rfagc_top = 0x37 }, }; +static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, }; static struct tda18271_config hauppauge_hvr1200_tuner_config = { + .std_map = &hauppauge_hvr1200_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, }; @@ -370,13 +381,25 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = { .disable_i2c_gate_ctrl = 1, }; +static struct stv0900_reg stv0900_ts_regs[] = { + { R0900_TSGENERAL, 0x00 }, + { R0900_P1_TSSPEED, 0x40 }, + { R0900_P2_TSSPEED, 0x40 }, + { R0900_P1_TSCFGM, 0xc0 }, + { R0900_P2_TSCFGM, 0xc0 }, + { R0900_P1_TSCFGH, 0xe0 }, + { R0900_P2_TSCFGH, 0xe0 }, + { R0900_P1_TSCFGL, 0x20 }, + { R0900_P2_TSCFGL, 0x20 }, + { 0xffff, 0xff }, /* terminate */ +}; + static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, .xtal = 27000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ - .path1_mode = 2,/*Serial continues clock */ - .path2_mode = 2,/*Serial continues clock */ + .ts_config_regs = stv0900_ts_regs, .tun1_maddress = 0,/* 0x60 */ .tun2_maddress = 3,/* 0x63 */ .tun1_adc = 1,/* 1 Vpp */ @@ -736,7 +759,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, 0, 0x09)) + LNBH24_PCL, + LNBH24_TTX, 0x09)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -756,7 +780,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, 0, 0x0a)) + LNBH24_PCL, + LNBH24_TTX, 0x0a)) printk(KERN_ERR "No LNBH24 found!\n"); diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 66bbd2e7110..70836af3ab4 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -963,15 +963,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 94b7a52629d..a5cc1c1fc2d 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -1524,33 +1524,45 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + /* Terry Wu <terrywu2009@gmail.com> */ + /* TV Audio : set GPIO 2, 18, 19 value to 0, 1, 0 */ + /* FM Audio : set GPIO 2, 18, 19 value to 0, 0, 0 */ + /* Line-in Audio : set GPIO 2, 18, 19 value to 0, 1, 1 */ + /* Mute Audio : set GPIO 2 value to 1 */ [CX88_BOARD_WINFAST_TV2000_XP_GLOBAL] = { - .name = "Winfast TV2000 XP Global", + .name = "Leadtek TV2000 XP Global", .tuner_type = TUNER_XC2028, .tuner_addr = 0x61, + .radio_type = TUNER_XC2028, + .radio_addr = 0x61, .input = { { .type = CX88_VMUX_TELEVISION, .vmux = 0, - .gpio0 = 0x0400, /* pin 2:mute = 0 (off?) */ + .gpio0 = 0x0400, /* pin 2 = 0 */ .gpio1 = 0x0000, - .gpio2 = 0x0800, /* pin 19:audio = 0 (tv) */ - + .gpio2 = 0x0C04, /* pin 18 = 1, pin 19 = 0 */ + .gpio3 = 0x0000, }, { .type = CX88_VMUX_COMPOSITE1, .vmux = 1, - .gpio0 = 0x0400, /* probably? or 0x0404 to turn mute on */ + .gpio0 = 0x0400, /* pin 2 = 0 */ .gpio1 = 0x0000, - .gpio2 = 0x0808, /* pin 19:audio = 1 (line) */ - + .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ + .gpio3 = 0x0000, }, { .type = CX88_VMUX_SVIDEO, .vmux = 2, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x0000, + .gpio2 = 0x0C0C, /* pin 18 = 1, pin 19 = 1 */ + .gpio3 = 0x0000, } }, .radio = { .type = CX88_RADIO, - .gpio0 = 0x004ff, - .gpio1 = 0x010ff, - .gpio2 = 0x0ff, + .gpio0 = 0x0400, /* pin 2 = 0 */ + .gpio1 = 0x0000, + .gpio2 = 0x0C00, /* pin 18 = 0, pin 19 = 0 */ + .gpio3 = 0x0000, }, }, [CX88_BOARD_POWERCOLOR_REAL_ANGEL] = { @@ -2438,6 +2450,41 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x107d, .subdevice = 0x6654, .card = CX88_BOARD_WINFAST_DTV1800H, + }, { + /* PVR2000 PAL Model [107d:6630] */ + .subvendor = 0x107d, + .subdevice = 0x6630, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 PAL Model [107d:6638] */ + .subvendor = 0x107d, + .subdevice = 0x6638, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 NTSC Model [107d:6631] */ + .subvendor = 0x107d, + .subdevice = 0x6631, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 NTSC Model [107d:6637] */ + .subvendor = 0x107d, + .subdevice = 0x6637, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* PVR2000 NTSC Model [107d:663d] */ + .subvendor = 0x107d, + .subdevice = 0x663d, + .card = CX88_BOARD_LEADTEK_PVR2000, + }, { + /* DV2000 NTSC Model [107d:6621] */ + .subvendor = 0x107d, + .subdevice = 0x6621, + .card = CX88_BOARD_WINFAST_DV2000, + }, { + /* TV2000 XP Global [107d:6618] */ + .subvendor = 0x107d, + .subdevice = 0x6618, + .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, }, }; @@ -2446,12 +2493,6 @@ static const struct cx88_subid cx88_subids[] = { static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) { - /* This is just for the "Winfast 2000XP Expert" board ATM; I don't have data on - * any others. - * - * Byte 0 is 1 on the NTSC board. - */ - if (eeprom_data[4] != 0x7d || eeprom_data[5] != 0x10 || eeprom_data[7] != 0x66) { @@ -2459,8 +2500,19 @@ static void leadtek_eeprom(struct cx88_core *core, u8 *eeprom_data) return; } - core->board.tuner_type = (eeprom_data[6] == 0x13) ? - TUNER_PHILIPS_FM1236_MK3 : TUNER_PHILIPS_FM1216ME_MK3; + /* Terry Wu <terrywu2009@gmail.com> */ + switch (eeprom_data[6]) { + case 0x13: /* SSID 6613 for TV2000 XP Expert NTSC Model */ + case 0x21: /* SSID 6621 for DV2000 NTSC Model */ + case 0x31: /* SSID 6631 for PVR2000 NTSC Model */ + case 0x37: /* SSID 6637 for PVR2000 NTSC Model */ + case 0x3d: /* SSID 6637 for PVR2000 NTSC Model */ + core->board.tuner_type = TUNER_PHILIPS_FM1236_MK3; + break; + default: + core->board.tuner_type = TUNER_PHILIPS_FM1216ME_MK3; + break; + } info_printk(core, "Leadtek Winfast 2000XP Expert config: " "tuner=%d, eeprom[0]=0x%02x\n", @@ -2713,7 +2765,6 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, { /* Board-specific callbacks */ switch (core->boardnr) { - case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_POWERCOLOR_REAL_ANGEL: case CX88_BOARD_GENIATECH_X8000_MT: case CX88_BOARD_KWORLD_ATSC_120: @@ -2725,6 +2776,7 @@ static int cx88_xc2028_tuner_callback(struct cx88_core *core, case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PRO: case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: return cx88_dvico_xc2028_callback(core, command, arg); + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_WINFAST_DTV1800H: return cx88_xc3028_winfast1800h_callback(core, command, arg); } @@ -2914,6 +2966,7 @@ static void cx88_card_setup_pre_i2c(struct cx88_core *core) udelay(1000); break; + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_WINFAST_DTV1800H: /* GPIO 12 (xc3028 tuner reset) */ cx_set(MO_GP1_IO, 0x1010); @@ -2950,6 +3003,7 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl) case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO: ctl->demod = XC3028_FE_OREN538; break; + case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: /* @@ -2993,6 +3047,8 @@ static void cx88_card_setup(struct cx88_core *core) if (0 == core->i2c_rc) gdi_eeprom(core, eeprom); break; + case CX88_BOARD_LEADTEK_PVR2000: + case CX88_BOARD_WINFAST_DV2000: case CX88_BOARD_WINFAST2000XP_EXPERT: if (0 == core->i2c_rc) leadtek_eeprom(core, eeprom); diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 0ccac702bea..b12770848c0 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -1111,15 +1111,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 00cc791a9e4..c43fdb9bc88 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -139,6 +139,24 @@ static struct em28xx_reg_seq kworld_330u_digital[] = { { -1, -1, -1, -1}, }; +/* Evga inDtube + GPIO0 - Enable digital power (s5h1409) - low to enable + GPIO1 - Enable analog power (tvp5150/emp202) - low to enable + GPIO4 - xc3028 reset + GOP3 - s5h1409 reset + */ +static struct em28xx_reg_seq evga_indtube_analog[] = { + {EM28XX_R08_GPIO, 0x79, 0xff, 60}, + { -1, -1, -1, -1}, +}; + +static struct em28xx_reg_seq evga_indtube_digital[] = { + {EM28XX_R08_GPIO, 0x7a, 0xff, 1}, + {EM2880_R04_GPO, 0x04, 0xff, 10}, + {EM2880_R04_GPO, 0x0c, 0xff, 1}, + { -1, -1, -1, -1}, +}; + /* Callback for the most boards */ static struct em28xx_reg_seq default_tuner_gpio[] = { {EM28XX_R08_GPIO, EM_GPIO_4, EM_GPIO_4, 10}, @@ -1449,6 +1467,33 @@ struct em28xx_board em28xx_boards[] = { .gpio = terratec_av350_unmute_gpio, } }, }, + [EM2882_BOARD_EVGA_INDTUBE] = { + .name = "Evga inDtube", + .tuner_type = TUNER_XC2028, + .tuner_gpio = default_tuner_gpio, + .decoder = EM28XX_TVP5150, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, /* NEC IR */ + .mts_firmware = 1, + .has_dvb = 1, + .dvb_gpio = evga_indtube_digital, + .ir_codes = ir_codes_evga_indtube, + .input = { { + .type = EM28XX_VMUX_TELEVISION, + .vmux = TVP5150_COMPOSITE0, + .amux = EM28XX_AMUX_VIDEO, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = TVP5150_COMPOSITE1, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = TVP5150_SVIDEO, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = evga_indtube_analog, + } }, + }, }; const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards); @@ -1571,6 +1616,7 @@ static struct em28xx_hash_table em28xx_eeprom_hash[] = { {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF}, {0x966a0441, EM2880_BOARD_KWORLD_DVB_310U, TUNER_XC2028}, {0x9567eb1a, EM2880_BOARD_EMPIRE_DUAL_TV, TUNER_XC2028}, + {0xcee44a99, EM2882_BOARD_EVGA_INDTUBE, TUNER_XC2028}, }; /* I2C devicelist hash table for devices with generic USB IDs */ @@ -1834,6 +1880,10 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) ctl->demod = XC3028_FE_CHINA; ctl->fname = XC2028_DEFAULT_FIRMWARE; break; + case EM2882_BOARD_EVGA_INDTUBE: + ctl->demod = XC3028_FE_CHINA; + ctl->fname = XC3028L_DEFAULT_FIRMWARE; + break; default: ctl->demod = XC3028_FE_OREN538; } @@ -2101,6 +2151,12 @@ void em28xx_card_setup(struct em28xx *dev) case EM2880_BOARD_MSI_DIGIVOX_AD: if (!em28xx_hint_board(dev)) em28xx_set_model(dev); + + /* In cases where we had to use a board hint, the call to + em28xx_set_mode() in em28xx_pre_card_setup() was a no-op, + so make the call now so the analog GPIOs are set properly + before probing the i2c bus. */ + em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; } diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 563dd2b1c8e..e7b47c8da8f 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -445,6 +445,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2883_BOARD_KWORLD_HYBRID_330U: + case EM2882_BOARD_EVGA_INDTUBE: dvb->frontend = dvb_attach(s5h1409_attach, &em28xx_s5h1409_with_xc3028, &dev->i2c_adap); diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 882796e84db..8fe1beecfff 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -687,8 +687,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int width = f->fmt.pix.width; - int height = f->fmt.pix.height; + unsigned int width = f->fmt.pix.width; + unsigned int height = f->fmt.pix.height; unsigned int maxw = norm_maxw(dev); unsigned int maxh = norm_maxh(dev); unsigned int hscale, vscale; @@ -701,34 +701,20 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, return -EINVAL; } - /* width must even because of the YUYV format - height must be even because of interlacing */ - height &= 0xfffe; - width &= 0xfffe; - - if (unlikely(height < 32)) - height = 32; - if (unlikely(height > maxh)) - height = maxh; - if (unlikely(width < 48)) - width = 48; - if (unlikely(width > maxw)) - width = maxw; - if (dev->board.is_em2800) { /* the em2800 can only scale down to 50% */ - if (height % (maxh / 2)) - height = maxh; - if (width % (maxw / 2)) - width = maxw; - /* according to empiatech support */ - /* the MaxPacketSize is to small to support */ - /* framesizes larger than 640x480 @ 30 fps */ - /* or 640x576 @ 25 fps. As this would cut */ - /* of a part of the image we prefer */ - /* 360x576 or 360x480 for now */ + height = height > (3 * maxh / 4) ? maxh : maxh / 2; + width = width > (3 * maxw / 4) ? maxw : maxw / 2; + /* According to empiatech support the MaxPacketSize is too small + * to support framesizes larger than 640x480 @ 30 fps or 640x576 + * @ 25 fps. As this would cut of a part of the image we prefer + * 360x576 or 360x480 for now */ if (width == maxw && height == maxh) width /= 2; + } else { + /* width must even because of the YUYV format + height must be even because of interlacing */ + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); } get_scale(dev, width, height, &hscale, &vscale); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 8bf81be1da6..813ce45c2f9 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -106,6 +106,7 @@ #define EM2860_BOARD_TERRATEC_GRABBY 67 #define EM2860_BOARD_TERRATEC_AV350 68 #define EM2882_BOARD_KWORLD_ATSC_315U 69 +#define EM2882_BOARD_EVGA_INDTUBE 70 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index f7e0355ad64..1e89600986c 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -1042,13 +1042,11 @@ static int vidioc_queryctrl(struct file *file, void *priv, for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { if (gspca_dev->ctrl_dis & (1 << i)) continue; - if (ctrls->qctrl.id < id) + if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) continue; - if (ctrls != NULL) { - if (gspca_dev->sd_desc->ctrls[i].qctrl.id + if (ctrls && gspca_dev->sd_desc->ctrls[i].qctrl.id > ctrls->qctrl.id) - continue; - } + continue; ctrls = &gspca_dev->sd_desc->ctrls[i]; } } else { diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 188866ac6ce..2f6e135d94b 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -50,12 +50,18 @@ static int i2c_detect_tries = 10; struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + __u8 packet_nr; + char bridge; #define BRIDGE_OV511 0 #define BRIDGE_OV511PLUS 1 #define BRIDGE_OV518 2 #define BRIDGE_OV518PLUS 3 #define BRIDGE_OV519 4 +#define BRIDGE_MASK 7 + + char invert_led; +#define BRIDGE_INVERT_LED 8 /* Determined by sensor type */ __u8 sif; @@ -65,22 +71,25 @@ struct sd { __u8 colors; __u8 hflip; __u8 vflip; + __u8 autobrightness; + __u8 freq; __u8 stopped; /* Streaming is temporarily paused */ - __u8 frame_rate; /* current Framerate (OV519 only) */ - __u8 clockdiv; /* clockdiv override for OV519 only */ + __u8 frame_rate; /* current Framerate */ + __u8 clockdiv; /* clockdiv override */ char sensor; /* Type of image sensor chip (SEN_*) */ #define SEN_UNKNOWN 0 #define SEN_OV6620 1 #define SEN_OV6630 2 -#define SEN_OV7610 3 -#define SEN_OV7620 4 -#define SEN_OV7640 5 -#define SEN_OV7670 6 -#define SEN_OV76BE 7 -#define SEN_OV8610 8 +#define SEN_OV66308AF 3 +#define SEN_OV7610 4 +#define SEN_OV7620 5 +#define SEN_OV7640 6 +#define SEN_OV7670 7 +#define SEN_OV76BE 8 +#define SEN_OV8610 9 }; /* V4L2 controls supported by the driver */ @@ -94,11 +103,17 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static void setbrightness(struct gspca_dev *gspca_dev); static void setcontrast(struct gspca_dev *gspca_dev); static void setcolors(struct gspca_dev *gspca_dev); +static void setautobrightness(struct sd *sd); +static void setfreq(struct sd *sd); -static struct ctrl sd_ctrls[] = { +static const struct ctrl sd_ctrls[] = { { { .id = V4L2_CID_BRIGHTNESS, @@ -141,7 +156,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcolors, .get = sd_getcolors, }, -/* next controls work with ov7670 only */ +/* The flip controls work with ov7670 only */ #define HFLIP_IDX 3 { { @@ -172,6 +187,51 @@ static struct ctrl sd_ctrls[] = { .set = sd_setvflip, .get = sd_getvflip, }, +#define AUTOBRIGHT_IDX 5 + { + { + .id = V4L2_CID_AUTOBRIGHTNESS, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Brightness", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOBRIGHT_DEF 1 + .default_value = AUTOBRIGHT_DEF, + }, + .set = sd_setautobrightness, + .get = sd_getautobrightness, + }, +#define FREQ_IDX 6 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 0 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, +#define OV7670_FREQ_IDX 7 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 3, /* 0: 0, 1: 50Hz, 2:60Hz 3: Auto Hz */ + .step = 1, +#define OV7670_FREQ_DEF 3 + .default_value = OV7670_FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; static const struct v4l2_pix_format ov519_vga_mode[] = { @@ -187,11 +247,21 @@ static const struct v4l2_pix_format ov519_vga_mode[] = { .priv = 0}, }; static const struct v4l2_pix_format ov519_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 176, .sizeimage = 176 * 144 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 352, .sizeimage = 352 * 288 * 3 / 8 + 590, @@ -199,42 +269,118 @@ static const struct v4l2_pix_format ov519_sif_mode[] = { .priv = 0}, }; +/* Note some of the sizeimage values for the ov511 / ov518 may seem + larger then necessary, however they need to be this big as the ov511 / + ov518 always fills the entire isoc frame, using 0 padding bytes when + it doesn't have any data. So with low framerates the amount of data + transfered can become quite large (libv4l will remove all the 0 padding + in userspace). */ static const struct v4l2_pix_format ov518_vga_mode[] = { {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 320, - .sizeimage = 320 * 240 * 3 / 8 + 590, + .sizeimage = 320 * 240 * 3, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, {640, 480, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, + .sizeimage = 640 * 480 * 2, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; static const struct v4l2_pix_format ov518_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, {176, 144, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 176, - .sizeimage = 40000, + .sizeimage = 70000, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, {352, 288, V4L2_PIX_FMT_OV518, V4L2_FIELD_NONE, .bytesperline = 352, - .sizeimage = 352 * 288 * 3 / 8 + 590, + .sizeimage = 352 * 288 * 3, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; +static const struct v4l2_pix_format ov511_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; +static const struct v4l2_pix_format ov511_sif_mode[] = { + {160, 120, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 70000, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_OV511, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288 * 3, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; /* Registers common to OV511 / OV518 */ +#define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ #define R51x_SYS_RESET 0x50 + /* Reset type flags */ + #define OV511_RESET_OMNICE 0x08 #define R51x_SYS_INIT 0x53 #define R51x_SYS_SNAP 0x52 #define R51x_SYS_CUST_ID 0x5F #define R51x_COMP_LUT_BEGIN 0x80 /* OV511 Camera interface register numbers */ +#define R511_CAM_DELAY 0x10 +#define R511_CAM_EDGE 0x11 +#define R511_CAM_PXCNT 0x12 +#define R511_CAM_LNCNT 0x13 +#define R511_CAM_PXDIV 0x14 +#define R511_CAM_LNDIV 0x15 +#define R511_CAM_UV_EN 0x16 +#define R511_CAM_LINE_MODE 0x17 +#define R511_CAM_OPTS 0x18 + +#define R511_SNAP_FRAME 0x19 +#define R511_SNAP_PXCNT 0x1A +#define R511_SNAP_LNCNT 0x1B +#define R511_SNAP_PXDIV 0x1C +#define R511_SNAP_LNDIV 0x1D +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_UV_EN 0x1E +#define R511_SNAP_OPTS 0x1F + +#define R511_DRAM_FLOW_CTL 0x20 +#define R511_FIFO_OPTS 0x31 +#define R511_I2C_CTL 0x40 #define R511_SYS_LED_CTL 0x55 /* OV511+ only */ -#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ +#define R511_COMP_EN 0x78 +#define R511_COMP_LUT_EN 0x79 /* OV518 Camera interface register numbers */ #define R518_GPIO_OUT 0x56 /* OV518(+) only */ @@ -383,7 +529,7 @@ static const struct ov_i2c_regvals norm_6x20[] = { { 0x28, 0x05 }, { 0x2a, 0x04 }, /* Disable framerate adjust */ /* { 0x2b, 0xac }, * Framerate; Set 2a[7] first */ - { 0x2d, 0x99 }, + { 0x2d, 0x85 }, { 0x33, 0xa0 }, /* Color Processing Parameter */ { 0x34, 0xd2 }, /* Max A/D range */ { 0x38, 0x8b }, @@ -416,7 +562,7 @@ static const struct ov_i2c_regvals norm_6x30[] = { { 0x07, 0x2d }, /* Sharpness */ { 0x0c, 0x20 }, { 0x0d, 0x20 }, - { 0x0e, 0x20 }, + { 0x0e, 0xa0 }, /* Was 0x20, bit7 enables a 2x gain which we need */ { 0x0f, 0x05 }, { 0x10, 0x9a }, { 0x11, 0x00 }, /* Pixel clock = fastest */ @@ -558,7 +704,7 @@ static const struct ov_i2c_regvals norm_7620[] = { { 0x23, 0x00 }, { 0x26, 0xa2 }, { 0x27, 0xea }, - { 0x28, 0x20 }, + { 0x28, 0x22 }, /* Was 0x20, bit1 enables a 2x gain which we need */ { 0x29, 0x00 }, { 0x2a, 0x10 }, { 0x2b, 0x00 }, @@ -999,13 +1145,128 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n) return ret; } +static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + int rc, retries; + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + + /* Three byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_3, reg); + if (rc < 0) + return rc; + + /* Write "value" to I2C data port of OV511 */ + rc = reg_w(sd, R51x_I2C_DATA, value); + if (rc < 0) + return rc; + + /* Initiate 3-byte write cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x01); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + if (--retries < 0) { + PDEBUG(D_USBO, "i2c write retries exhausted"); + return -1; + } + } + + return 0; +} + +static int ov511_i2c_r(struct sd *sd, __u8 reg) +{ + int rc, value, retries; + + /* Two byte write cycle */ + for (retries = 6; ; ) { + /* Select camera register */ + rc = reg_w(sd, R51x_I2C_SADDR_2, reg); + if (rc < 0) + return rc; + + /* Initiate 2-byte write cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x03); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + reg_w(sd, R511_I2C_CTL, 0x10); + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c write retries exhausted"); + return -1; + } + } + + /* Two byte read cycle */ + for (retries = 6; ; ) { + /* Initiate 2-byte read cycle */ + rc = reg_w(sd, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + do + rc = reg_r(sd, R511_I2C_CTL); + while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */ + + if (rc < 0) + return rc; + + if ((rc & 2) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = reg_w(sd, R511_I2C_CTL, 0x10); + if (rc < 0) + return rc; + + if (--retries < 0) { + PDEBUG(D_USBI, "i2c read retries exhausted"); + return -1; + } + } + + value = reg_r(sd, R51x_I2C_DATA); + + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + + /* This is needed to make i2c_w() work */ + rc = reg_w(sd, R511_I2C_CTL, 0x05); + if (rc < 0) + return rc; + + return value; +} /* * The OV518 I2C I/O procedure is different, hence, this function. * This is normally only called from i2c_w(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int i2c_w(struct sd *sd, +static int ov518_i2c_w(struct sd *sd, __u8 reg, __u8 value) { @@ -1040,7 +1301,7 @@ static int i2c_w(struct sd *sd, * This is normally only called from i2c_r(). Note that this function * always succeeds regardless of whether the sensor is present and working. */ -static int i2c_r(struct sd *sd, __u8 reg) +static int ov518_i2c_r(struct sd *sd, __u8 reg) { int rc, value; @@ -1063,6 +1324,34 @@ static int i2c_r(struct sd *sd, __u8 reg) return value; } +static int i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return ov511_i2c_w(sd, reg, value); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + return ov518_i2c_w(sd, reg, value); + } + return -1; /* Should never happen */ +} + +static int i2c_r(struct sd *sd, __u8 reg) +{ + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + return ov511_i2c_r(sd, reg); + case BRIDGE_OV518: + case BRIDGE_OV518PLUS: + case BRIDGE_OV519: + return ov518_i2c_r(sd, reg); + } + return -1; /* Should never happen */ +} + /* Writes bits at positions specified by mask to an I2C reg. Bits that are in * the same position as 1's in "mask" are cleared and set to "value". Bits * that are in the same position as 0's in "mask" are preserved, regardless @@ -1242,7 +1531,6 @@ static int ov8xx0_configure(struct sd *sd) } /* Set sensor-specific vars */ -/* sd->sif = 0; already done */ return 0; } @@ -1279,15 +1567,13 @@ static int ov7xx0_configure(struct sd *sd) } } else if ((rc & 3) == 1) { /* I don't know what's different about the 76BE yet. */ - if (i2c_r(sd, 0x15) & 1) + if (i2c_r(sd, 0x15) & 1) { PDEBUG(D_PROBE, "Sensor is an OV7620AE"); - else + sd->sensor = SEN_OV7620; + } else { PDEBUG(D_PROBE, "Sensor is an OV76BE"); - - /* OV511+ will return all zero isoc data unless we - * configure the sensor as a 7620. Someone needs to - * find the exact reg. setting that causes this. */ - sd->sensor = SEN_OV76BE; + sd->sensor = SEN_OV76BE; + } } else if ((rc & 3) == 0) { /* try to read product id registers */ high = i2c_r(sd, 0x0a); @@ -1333,7 +1619,6 @@ static int ov7xx0_configure(struct sd *sd) } /* Set sensor-specific vars */ -/* sd->sif = 0; already done */ return 0; } @@ -1362,13 +1647,14 @@ static int ov6xx0_configure(struct sd *sd) break; case 0x01: sd->sensor = SEN_OV6620; + PDEBUG(D_PROBE, "Sensor is an OV6620"); break; case 0x02: sd->sensor = SEN_OV6630; PDEBUG(D_PROBE, "Sensor is an OV66308AE"); break; case 0x03: - sd->sensor = SEN_OV6630; + sd->sensor = SEN_OV66308AF; PDEBUG(D_PROBE, "Sensor is an OV66308AF"); break; case 0x90: @@ -1391,6 +1677,9 @@ static int ov6xx0_configure(struct sd *sd) /* Turns on or off the LED. Only has an effect with OV511+/OV518(+)/OV519 */ static void ov51x_led_control(struct sd *sd, int on) { + if (sd->invert_led) + on = !on; + switch (sd->bridge) { /* OV511 has no LED control */ case BRIDGE_OV511PLUS: @@ -1406,9 +1695,31 @@ static void ov51x_led_control(struct sd *sd, int on) } } -/* OV518 quantization tables are 8x4 (instead of 8x8) */ -static int ov518_upload_quan_tables(struct sd *sd) +static int ov51x_upload_quan_tables(struct sd *sd) { + const unsigned char yQuanTable511[] = { + 0, 1, 1, 2, 2, 3, 3, 4, + 1, 1, 1, 2, 2, 3, 4, 4, + 1, 1, 2, 2, 3, 4, 4, 4, + 2, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 5, 5, 5, + 3, 3, 4, 4, 5, 5, 5, 5, + 3, 4, 4, 4, 5, 5, 5, 5, + 4, 4, 4, 4, 5, 5, 5, 5 + }; + + const unsigned char uvQuanTable511[] = { + 0, 2, 2, 3, 4, 4, 4, 4, + 2, 2, 2, 4, 4, 4, 4, 4, + 2, 2, 3, 4, 4, 4, 4, 4, + 3, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4 + }; + + /* OV518 quantization tables are 8x4 (instead of 8x8) */ const unsigned char yQuanTable518[] = { 5, 4, 5, 6, 6, 7, 7, 7, 5, 5, 5, 5, 6, 7, 7, 7, @@ -1423,14 +1734,23 @@ static int ov518_upload_quan_tables(struct sd *sd) 7, 7, 7, 7, 7, 7, 8, 8 }; - const unsigned char *pYTable = yQuanTable518; - const unsigned char *pUVTable = uvQuanTable518; + const unsigned char *pYTable, *pUVTable; unsigned char val0, val1; - int i, rc, reg = R51x_COMP_LUT_BEGIN; + int i, size, rc, reg = R51x_COMP_LUT_BEGIN; PDEBUG(D_PROBE, "Uploading quantization tables"); - for (i = 0; i < 16; i++) { + if (sd->bridge == BRIDGE_OV511 || sd->bridge == BRIDGE_OV511PLUS) { + pYTable = yQuanTable511; + pUVTable = uvQuanTable511; + size = 32; + } else { + pYTable = yQuanTable518; + pUVTable = uvQuanTable518; + size = 16; + } + + for (i = 0; i < size; i++) { val0 = *pYTable++; val1 = *pYTable++; val0 &= 0x0f; @@ -1445,7 +1765,7 @@ static int ov518_upload_quan_tables(struct sd *sd) val0 &= 0x0f; val1 &= 0x0f; val0 |= val1 << 4; - rc = reg_w(sd, reg + 16, val0); + rc = reg_w(sd, reg + size, val0); if (rc < 0) return rc; @@ -1455,6 +1775,87 @@ static int ov518_upload_quan_tables(struct sd *sd) return 0; } +/* This initializes the OV511/OV511+ and the sensor */ +static int ov511_configure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int rc; + + /* For 511 and 511+ */ + const struct ov_regvals init_511[] = { + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x7f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3f }, + { R51x_SYS_INIT, 0x01 }, + { R51x_SYS_RESET, 0x3d }, + }; + + const struct ov_regvals norm_511[] = { + { R511_DRAM_FLOW_CTL, 0x01 }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0x1f }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + + const struct ov_regvals norm_511_p[] = { + { R511_DRAM_FLOW_CTL, 0xff }, + { R51x_SYS_SNAP, 0x00 }, + { R51x_SYS_SNAP, 0x02 }, + { R51x_SYS_SNAP, 0x00 }, + { R511_FIFO_OPTS, 0xff }, + { R511_COMP_EN, 0x00 }, + { R511_COMP_LUT_EN, 0x03 }, + }; + + const struct ov_regvals compress_511[] = { + { 0x70, 0x1f }, + { 0x71, 0x05 }, + { 0x72, 0x06 }, + { 0x73, 0x06 }, + { 0x74, 0x14 }, + { 0x75, 0x03 }, + { 0x76, 0x04 }, + { 0x77, 0x04 }, + }; + + PDEBUG(D_PROBE, "Device custom id %x", reg_r(sd, R51x_SYS_CUST_ID)); + + rc = write_regvals(sd, init_511, ARRAY_SIZE(init_511)); + if (rc < 0) + return rc; + + switch (sd->bridge) { + case BRIDGE_OV511: + rc = write_regvals(sd, norm_511, ARRAY_SIZE(norm_511)); + if (rc < 0) + return rc; + break; + case BRIDGE_OV511PLUS: + rc = write_regvals(sd, norm_511_p, ARRAY_SIZE(norm_511_p)); + if (rc < 0) + return rc; + break; + } + + /* Init compression */ + rc = write_regvals(sd, compress_511, ARRAY_SIZE(compress_511)); + if (rc < 0) + return rc; + + rc = ov51x_upload_quan_tables(sd); + if (rc < 0) { + PDEBUG(D_ERR, "Error uploading quantization tables"); + return rc; + } + + return 0; +} + /* This initializes the OV518/OV518+ and the sensor */ static int ov518_configure(struct gspca_dev *gspca_dev) { @@ -1462,7 +1863,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) int rc; /* For 518 and 518+ */ - static struct ov_regvals init_518[] = { + const struct ov_regvals init_518[] = { { R51x_SYS_RESET, 0x40 }, { R51x_SYS_INIT, 0xe1 }, { R51x_SYS_RESET, 0x3e }, @@ -1473,7 +1874,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) { 0x5d, 0x03 }, }; - static struct ov_regvals norm_518[] = { + const struct ov_regvals norm_518[] = { { R51x_SYS_SNAP, 0x02 }, /* Reset */ { R51x_SYS_SNAP, 0x01 }, /* Enable */ { 0x31, 0x0f }, @@ -1486,7 +1887,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) { 0x2f, 0x80 }, }; - static struct ov_regvals norm_518_p[] = { + const struct ov_regvals norm_518_p[] = { { R51x_SYS_SNAP, 0x02 }, /* Reset */ { R51x_SYS_SNAP, 0x01 }, /* Enable */ { 0x31, 0x0f }, @@ -1531,7 +1932,7 @@ static int ov518_configure(struct gspca_dev *gspca_dev) break; } - rc = ov518_upload_quan_tables(sd); + rc = ov51x_upload_quan_tables(sd); if (rc < 0) { PDEBUG(D_ERR, "Error uploading quantization tables"); return rc; @@ -1573,9 +1974,14 @@ static int sd_config(struct gspca_dev *gspca_dev, struct cam *cam; int ret = 0; - sd->bridge = id->driver_info; + sd->bridge = id->driver_info & BRIDGE_MASK; + sd->invert_led = id->driver_info & BRIDGE_INVERT_LED; switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ret = ov511_configure(gspca_dev); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: ret = ov518_configure(gspca_dev); @@ -1634,6 +2040,16 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + if (!sd->sif) { + cam->cam_mode = ov511_vga_mode; + cam->nmodes = ARRAY_SIZE(ov511_vga_mode); + } else { + cam->cam_mode = ov511_sif_mode; + cam->nmodes = ARRAY_SIZE(ov511_sif_mode); + } + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: if (!sd->sif) { @@ -1655,13 +2071,28 @@ static int sd_config(struct gspca_dev *gspca_dev, break; } sd->brightness = BRIGHTNESS_DEF; - sd->contrast = CONTRAST_DEF; + if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF) + sd->contrast = 200; /* The default is too low for the ov6630 */ + else + sd->contrast = CONTRAST_DEF; sd->colors = COLOR_DEF; sd->hflip = HFLIP_DEF; sd->vflip = VFLIP_DEF; - if (sd->sensor != SEN_OV7670) - gspca_dev->ctrl_dis = (1 << HFLIP_IDX) - | (1 << VFLIP_IDX); + sd->autobrightness = AUTOBRIGHT_DEF; + if (sd->sensor == SEN_OV7670) { + sd->freq = OV7670_FREQ_DEF; + gspca_dev->ctrl_dis = 1 << FREQ_IDX; + } else { + sd->freq = FREQ_DEF; + gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | + (1 << OV7670_FREQ_IDX); + } + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX; + /* OV8610 Frequency filter control should work but needs testing */ + if (sd->sensor == SEN_OV8610) + gspca_dev->ctrl_dis |= 1 << FREQ_IDX; + return 0; error: PDEBUG(D_ERR, "OV519 Config failed"); @@ -1680,6 +2111,7 @@ static int sd_init(struct gspca_dev *gspca_dev) return -EIO; break; case SEN_OV6630: + case SEN_OV66308AF: if (write_i2c_regvals(sd, norm_6x30, ARRAY_SIZE(norm_6x30))) return -EIO; break; @@ -1688,6 +2120,8 @@ static int sd_init(struct gspca_dev *gspca_dev) /* case SEN_OV76BE: */ if (write_i2c_regvals(sd, norm_7610, ARRAY_SIZE(norm_7610))) return -EIO; + if (i2c_w_mask(sd, 0x0e, 0x00, 0x40)) + return -EIO; break; case SEN_OV7620: if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620))) @@ -1709,6 +2143,126 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } +/* Set up the OV511/OV511+ with the given image parameters. + * + * Do not put any sensor-specific code in here (including I2C I/O functions) + */ +static int ov511_mode_init_regs(struct sd *sd) +{ + int hsegs, vsegs, packet_size, fps, needed; + int interlaced = 0; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + reg_w(sd, R51x_FIFO_PSIZE, packet_size >> 5); + + reg_w(sd, R511_CAM_UV_EN, 0x01); + reg_w(sd, R511_SNAP_UV_EN, 0x01); + reg_w(sd, R511_SNAP_OPTS, 0x03); + + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + hsegs = (sd->gspca_dev.width >> 3) - 1; + vsegs = (sd->gspca_dev.height >> 3) - 1; + + reg_w(sd, R511_CAM_PXCNT, hsegs); + reg_w(sd, R511_CAM_LNCNT, vsegs); + reg_w(sd, R511_CAM_PXDIV, 0x00); + reg_w(sd, R511_CAM_LNDIV, 0x00); + + /* YUV420, low pass filter on */ + reg_w(sd, R511_CAM_OPTS, 0x03); + + /* Snapshot additions */ + reg_w(sd, R511_SNAP_PXCNT, hsegs); + reg_w(sd, R511_SNAP_LNCNT, vsegs); + reg_w(sd, R511_SNAP_PXDIV, 0x00); + reg_w(sd, R511_SNAP_LNDIV, 0x00); + + /******** Set the framerate ********/ + if (frame_rate > 0) + sd->frame_rate = frame_rate; + + switch (sd->sensor) { + case SEN_OV6620: + /* No framerate control, doesn't like higher rates yet */ + sd->clockdiv = 3; + break; + + /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed + for more sensors we need to do this for them too */ + case SEN_OV7620: + case SEN_OV7640: + case SEN_OV76BE: + if (sd->gspca_dev.width == 320) + interlaced = 1; + /* Fall through */ + case SEN_OV6630: + case SEN_OV7610: + case SEN_OV7670: + switch (sd->frame_rate) { + case 30: + case 25: + /* Not enough bandwidth to do 640x480 @ 30 fps */ + if (sd->gspca_dev.width != 640) { + sd->clockdiv = 0; + break; + } + /* Fall through for 640x480 case */ + default: +/* case 20: */ +/* case 15: */ + sd->clockdiv = 1; + break; + case 10: + sd->clockdiv = 2; + break; + case 5: + sd->clockdiv = 5; + break; + } + if (interlaced) { + sd->clockdiv = (sd->clockdiv + 1) * 2 - 1; + /* Higher then 10 does not work */ + if (sd->clockdiv > 10) + sd->clockdiv = 10; + } + break; + + case SEN_OV8610: + /* No framerate control ?? */ + sd->clockdiv = 0; + break; + } + + /* Check if we have enough bandwidth to disable compression */ + fps = (interlaced ? 60 : 30) / (sd->clockdiv + 1) + 1; + needed = fps * sd->gspca_dev.width * sd->gspca_dev.height * 3 / 2; + /* 1400 is a conservative estimate of the max nr of isoc packets/sec */ + if (needed > 1400 * packet_size) { + /* Enable Y and UV quantization and compression */ + reg_w(sd, R511_COMP_EN, 0x07); + reg_w(sd, R511_COMP_LUT_EN, 0x03); + } else { + reg_w(sd, R511_COMP_EN, 0x06); + reg_w(sd, R511_COMP_LUT_EN, 0x00); + } + + reg_w(sd, R51x_SYS_RESET, OV511_RESET_OMNICE); + reg_w(sd, R51x_SYS_RESET, 0); + + return 0; +} + /* Sets up the OV518/OV518+ with the given image parameters * * OV518 needs a completely different approach, until we can figure out what @@ -1718,7 +2272,19 @@ static int sd_init(struct gspca_dev *gspca_dev) */ static int ov518_mode_init_regs(struct sd *sd) { - int hsegs, vsegs; + int hsegs, vsegs, packet_size; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + ov518_reg_w32(sd, R51x_FIFO_PSIZE, packet_size & ~7, 2); /******** Set the mode ********/ @@ -1755,20 +2321,30 @@ static int ov518_mode_init_regs(struct sd *sd) /* Windows driver does this here; who knows why */ reg_w(sd, 0x2f, 0x80); - /******** Set the framerate (to 30 FPS) ********/ - if (sd->bridge == BRIDGE_OV518PLUS) - sd->clockdiv = 1; - else - sd->clockdiv = 0; + /******** Set the framerate ********/ + sd->clockdiv = 1; /* Mode independent, but framerate dependent, regs */ - reg_w(sd, 0x51, 0x04); /* Clock divider; lower==faster */ + /* 0x51: Clock divider; Only works on some cams which use 2 crystals */ + reg_w(sd, 0x51, 0x04); reg_w(sd, 0x22, 0x18); reg_w(sd, 0x23, 0xff); - if (sd->bridge == BRIDGE_OV518PLUS) - reg_w(sd, 0x21, 0x19); - else + if (sd->bridge == BRIDGE_OV518PLUS) { + switch (sd->sensor) { + case SEN_OV7620: + if (sd->gspca_dev.width == 320) { + reg_w(sd, 0x20, 0x00); + reg_w(sd, 0x21, 0x19); + } else { + reg_w(sd, 0x20, 0x60); + reg_w(sd, 0x21, 0x1f); + } + break; + default: + reg_w(sd, 0x21, 0x19); + } + } else reg_w(sd, 0x71, 0x17); /* Compression-related? */ /* FIXME: Sensor-specific */ @@ -1879,7 +2455,11 @@ static int ov519_mode_init_regs(struct sd *sd) reg_w(sd, OV519_R10_H_SIZE, sd->gspca_dev.width >> 4); reg_w(sd, OV519_R11_V_SIZE, sd->gspca_dev.height >> 3); - reg_w(sd, OV519_R12_X_OFFSETL, 0x00); + if (sd->sensor == SEN_OV7670 && + sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv) + reg_w(sd, OV519_R12_X_OFFSETL, 0x04); + else + reg_w(sd, OV519_R12_X_OFFSETL, 0x00); reg_w(sd, OV519_R13_X_OFFSETH, 0x00); reg_w(sd, OV519_R14_Y_OFFSETL, 0x00); reg_w(sd, OV519_R15_Y_OFFSETH, 0x00); @@ -1971,7 +2551,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) int qvga; gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; /******** Mode (VGA/QVGA) and sensor specific regs ********/ switch (sd->sensor) { @@ -1983,21 +2563,16 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); break; case SEN_OV7620: -/* i2c_w(sd, 0x2b, 0x00); */ + case SEN_OV76BE: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); - i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); break; - case SEN_OV76BE: -/* i2c_w(sd, 0x2b, 0x00); */ - i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); - break; case SEN_OV7640: -/* i2c_w(sd, 0x2b, 0x00); */ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); /* i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */ @@ -2016,6 +2591,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) break; case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); break; default: @@ -2023,10 +2599,6 @@ static int mode_init_ov_sensor_regs(struct sd *sd) } /******** Palette-specific regs ********/ - if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { - /* not valid on the OV6620/OV7620/6630? */ - i2c_w_mask(sd, 0x0e, 0x00, 0x40); - } /* The OV518 needs special treatment. Although both the OV518 * and the OV6630 support a 16-bit video bus, only the 8 bit Y @@ -2036,25 +2608,12 @@ static int mode_init_ov_sensor_regs(struct sd *sd) /* OV7640 is 8-bit only */ - if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV7640) + if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF && + sd->sensor != SEN_OV7640) i2c_w_mask(sd, 0x13, 0x00, 0x20); /******** Clock programming ********/ - /* The OV6620 needs special handling. This prevents the - * severe banding that normally occurs */ - if (sd->sensor == SEN_OV6620) { - - /* Clock down */ - i2c_w(sd, 0x2a, 0x04); - i2c_w(sd, 0x11, sd->clockdiv); - i2c_w(sd, 0x2a, 0x84); - /* This next setting is critical. It seems to improve - * the gain or the contrast. The "reserved" bits seem - * to have some effect in this case. */ - i2c_w(sd, 0x2d, 0x85); - } else { - i2c_w(sd, 0x11, sd->clockdiv); - } + i2c_w(sd, 0x11, sd->clockdiv); /******** Special Features ********/ /* no evidence this is possible with OV7670, either */ @@ -2098,13 +2657,14 @@ static void sethvflip(struct sd *sd) static int set_ov_sensor_window(struct sd *sd) { struct gspca_dev *gspca_dev; - int qvga; + int qvga, crop; int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; int ret, hstart, hstop, vstop, vstart; __u8 v; gspca_dev = &sd->gspca_dev; - qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; + crop = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 2; /* The different sensor ICs handle setting up of window differently. * IF YOU SET IT WRONG, YOU WILL GET ALL ZERO ISOC DATA FROM OV51x!! */ @@ -2123,14 +2683,19 @@ static int set_ov_sensor_window(struct sd *sd) break; case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: hwsbase = 0x38; hwebase = 0x3a; vwsbase = 0x05; vwebase = 0x06; - if (qvga) { + if (sd->sensor == SEN_OV66308AF && qvga) /* HDG: this fixes U and V getting swapped */ - hwsbase--; - vwsbase--; + hwsbase++; + if (crop) { + hwsbase += 8; + hwebase += 8; + vwsbase += 11; + vwebase += 11; } break; case SEN_OV7620: @@ -2155,6 +2720,7 @@ static int set_ov_sensor_window(struct sd *sd) switch (sd->sensor) { case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: if (qvga) { /* QCIF */ hwscale = 0; vwscale = 0; @@ -2207,7 +2773,7 @@ static int set_ov_sensor_window(struct sd *sd) if (qvga) { /* QVGA from ov7670.c by * Jonathan Corbet */ hstart = 164; - hstop = 20; + hstop = 28; vstart = 14; vstop = 494; } else { /* VGA */ @@ -2233,7 +2799,6 @@ static int set_ov_sensor_window(struct sd *sd) msleep(10); /* need to sleep between read and write to * same reg! */ i2c_w(sd, OV7670_REG_VREF, v); - sethvflip(sd); } else { i2c_w(sd, 0x17, hwsbase); i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale)); @@ -2250,6 +2815,10 @@ static int sd_start(struct gspca_dev *gspca_dev) int ret = 0; switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + ret = ov511_mode_init_regs(sd); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: ret = ov518_mode_init_regs(sd); @@ -2268,6 +2837,9 @@ static int sd_start(struct gspca_dev *gspca_dev) setcontrast(gspca_dev); setbrightness(gspca_dev); setcolors(gspca_dev); + sethvflip(sd); + setautobrightness(sd); + setfreq(sd); ret = ov51x_restart(sd); if (ret < 0) @@ -2287,23 +2859,88 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control(sd, 0); } -static void ov518_pkt_scan(struct gspca_dev *gspca_dev, +static void ov511_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + __u8 *in, /* isoc packet */ int len) /* iso packet length */ { - PDEBUG(D_STREAM, "ov518_pkt_scan: %d bytes", len); + struct sd *sd = (struct sd *) gspca_dev; - if (len & 7) { - len--; - PDEBUG(D_STREAM, "packet number: %d\n", (int)data[len]); + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th bytes. The 9th byte is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot button on + * 1: snapshot frame + * 0: even/odd field + */ + if (!(in[0] | in[1] | in[2] | in[3] | in[4] | in[5] | in[6] | in[7]) && + (in[8] & 0x08)) { + if (in[8] & 0x80) { + /* Frame end */ + if ((in[9] + 1) * 8 != gspca_dev->width || + (in[10] + 1) * 8 != gspca_dev->height) { + PDEBUG(D_ERR, "Invalid frame size, got: %dx%d," + " requested: %dx%d\n", + (in[9] + 1) * 8, (in[10] + 1) * 8, + gspca_dev->width, gspca_dev->height); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + /* Add 11 byte footer to frame, might be usefull */ + gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11); + return; + } else { + /* Frame start */ + gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0); + sd->packet_nr = 0; + } } + /* Ignore the packet number */ + len--; + + /* intermediate packet */ + gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len); +} + +static void ov518_pkt_scan(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, /* target */ + __u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) 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); gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + sd->packet_nr = 0; + } + + if (gspca_dev->last_packet_type == DISCARD_PACKET) + return; + + /* Does this device use packet numbers ? */ + if (len & 7) { + len--; + if (sd->packet_nr == data[len]) + sd->packet_nr++; + /* The last few packets of the frame (which are all 0's + except that they may contain part of the footer), are + numbered 0 */ + else if (sd->packet_nr == 0 || data[len]) { + PDEBUG(D_ERR, "Invalid packet nr: %d (expect: %d)", + (int)data[len], (int)sd->packet_nr); + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } } /* intermediate packet */ @@ -2364,6 +3001,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: + ov511_pkt_scan(gspca_dev, frame, data, len); break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: @@ -2389,13 +3027,13 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SEN_OV76BE: case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: case SEN_OV7640: i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7620: /* 7620 doesn't like manual changes when in auto mode */ -/*fixme - * if (!sd->auto_brt) */ + if (!sd->autobrightness) i2c_w(sd, OV7610_REG_BRT, val); break; case SEN_OV7670: @@ -2418,6 +3056,7 @@ static void setcontrast(struct gspca_dev *gspca_dev) i2c_w(sd, OV7610_REG_CNT, val); break; case SEN_OV6630: + case SEN_OV66308AF: i2c_w_mask(sd, OV7610_REG_CNT, val >> 4, 0x0f); break; case SEN_OV8610: { @@ -2462,6 +3101,7 @@ static void setcolors(struct gspca_dev *gspca_dev) case SEN_OV76BE: case SEN_OV6620: case SEN_OV6630: + case SEN_OV66308AF: i2c_w(sd, OV7610_REG_SAT, val); break; case SEN_OV7620: @@ -2482,6 +3122,72 @@ static void setcolors(struct gspca_dev *gspca_dev) } } +static void setautobrightness(struct sd *sd) +{ + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + return; + + i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10); +} + +static void setfreq(struct sd *sd) +{ + if (sd->sensor == SEN_OV7670) { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, OV7670_REG_COM8, 0, OV7670_COM8_BFILT); + break; + case 1: /* 50 hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, 0x08, 0x18); + break; + case 2: /* 60 hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, 0x00, 0x18); + break; + case 3: /* Auto hz */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_BFILT, + OV7670_COM8_BFILT); + i2c_w_mask(sd, OV7670_REG_COM11, OV7670_COM11_HZAUTO, + 0x18); + break; + } + } else { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w_mask(sd, 0x2d, 0x00, 0x04); + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + break; + case 1: /* 50 hz (filter on and framerate adj) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + /* 20 fps -> 16.667 fps */ + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) + i2c_w(sd, 0x2b, 0x5e); + else + i2c_w(sd, 0x2b, 0xac); + break; + case 2: /* 60 hz (filter on, ...) */ + i2c_w_mask(sd, 0x2d, 0x04, 0x04); + if (sd->sensor == SEN_OV6620 || + sd->sensor == SEN_OV6630 || + sd->sensor == SEN_OV66308AF) { + /* 20 fps -> 15 fps */ + i2c_w_mask(sd, 0x2a, 0x80, 0x80); + i2c_w(sd, 0x2b, 0xa8); + } else { + /* no framerate adj. */ + i2c_w_mask(sd, 0x2a, 0x00, 0x80); + } + break; + } + } +} + static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -2572,6 +3278,71 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setautobrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autobrightness = val; + if (gspca_dev->streaming) + setautobrightness(sd); + return 0; +} + +static int sd_getautobrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autobrightness; + return 0; +} + +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(sd); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + struct sd *sd = (struct sd *) gspca_dev; + + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + case 3: + if (sd->sensor != SEN_OV7670) + return -EINVAL; + + strcpy((char *) menu->name, "Automatic"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2582,6 +3353,7 @@ static const struct sd_desc sd_desc = { .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -2590,17 +3362,22 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 }, - {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x041e, 0x4064), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x4068), + .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 }, {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS }, {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, + {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, {} }; diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index dc6a6f11354..0d02f41fa7d 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -46,6 +46,7 @@ struct sd { u8 gamma; u8 vflip; /* ov7630/ov7648 only */ u8 infrared; /* mt9v111 only */ + u8 freq; /* ov76xx only */ u8 quality; /* image quality */ #define QUALITY_MIN 60 #define QUALITY_MAX 95 @@ -96,8 +97,11 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val); static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { +#define BRIGHTNESS_IDX 0 { { .id = V4L2_CID_BRIGHTNESS, @@ -113,6 +117,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setbrightness, .get = sd_getbrightness, }, +#define CONTRAST_IDX 1 { { .id = V4L2_CID_CONTRAST, @@ -128,20 +133,22 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, +#define COLOR_IDX 2 { { .id = V4L2_CID_SATURATION, .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", + .name = "Saturation", .minimum = 0, .maximum = 40, .step = 1, -#define COLOR_DEF 32 +#define COLOR_DEF 25 .default_value = COLOR_DEF, }, .set = sd_setcolors, .get = sd_getcolors, }, +#define BLUE_BALANCE_IDX 3 { { .id = V4L2_CID_BLUE_BALANCE, @@ -156,6 +163,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setblue_balance, .get = sd_getblue_balance, }, +#define RED_BALANCE_IDX 4 { { .id = V4L2_CID_RED_BALANCE, @@ -170,6 +178,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setred_balance, .get = sd_getred_balance, }, +#define GAMMA_IDX 5 { { .id = V4L2_CID_GAMMA, @@ -184,7 +193,7 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgamma, .get = sd_getgamma, }, -#define AUTOGAIN_IDX 5 +#define AUTOGAIN_IDX 6 { { .id = V4L2_CID_AUTOGAIN, @@ -200,7 +209,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getautogain, }, /* ov7630/ov7648 only */ -#define VFLIP_IDX 6 +#define VFLIP_IDX 7 { { .id = V4L2_CID_VFLIP, @@ -209,14 +218,14 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 1, .step = 1, -#define VFLIP_DEF 0 /* vflip def = 1 for ov7630 */ +#define VFLIP_DEF 0 .default_value = VFLIP_DEF, }, .set = sd_setvflip, .get = sd_getvflip, }, /* mt9v111 only */ -#define INFRARED_IDX 7 +#define INFRARED_IDX 8 { { .id = V4L2_CID_INFRARED, @@ -231,28 +240,44 @@ static struct ctrl sd_ctrls[] = { .set = sd_setinfrared, .get = sd_getinfrared, }, +/* ov7630/ov7648/ov7660 only */ +#define FREQ_IDX 9 + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 2 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; /* table of the disabled controls */ static __u32 ctrl_dis[] = { - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_HV7131R 0 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MI0360 1 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MO4000 2 */ - (1 << VFLIP_IDX), + (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_MT9V111 3 */ - (1 << INFRARED_IDX) | (1 << VFLIP_IDX), + (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX), /* SENSOR_OM6802 4 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX), + (1 << INFRARED_IDX), /* SENSOR_OV7630 5 */ (1 << INFRARED_IDX), /* SENSOR_OV7648 6 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_OV7660 7 */ - (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), - /* SENSOR_SP80708 8 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | + (1 << FREQ_IDX), /* SENSOR_SP80708 8 */ }; static const struct v4l2_pix_format vga_mode[] = { @@ -268,7 +293,8 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 1}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, - .sizeimage = 640 * 480 * 3 / 8 + 590, + /* Note 3 / 8 is not large enough, not even 5 / 8 is ?! */ + .sizeimage = 640 * 480 * 3 / 4 + 590, .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; @@ -604,7 +630,9 @@ static const u8 ov7630_sensor_init[][8] = { /* win: i2c_r from 00 to 80 */ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, {0xb1, 0x21, 0x0c, 0x20, 0x20, 0x00, 0x00, 0x10}, - {0xd1, 0x21, 0x11, 0x00, 0x48, 0xc0, 0x00, 0x10}, +/* HDG: 0x11 was 0x00 change to 0x01 for better exposure (15 fps instead of 30) + 0x13 was 0xc0 change to 0xc3 for auto gain and exposure */ + {0xd1, 0x21, 0x11, 0x01, 0x48, 0xc3, 0x00, 0x10}, {0xb1, 0x21, 0x15, 0x80, 0x03, 0x00, 0x00, 0x10}, {0xd1, 0x21, 0x17, 0x1b, 0xbd, 0x05, 0xf6, 0x10}, {0xa1, 0x21, 0x1b, 0x04, 0x00, 0x00, 0x00, 0x10}, @@ -638,9 +666,8 @@ static const u8 ov7630_sensor_init[][8] = { {0xa1, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x21, 0x01, 0x80, 0x80, 0x00, 0x00, 0x10}, /* */ - {0xa1, 0x21, 0x11, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, +/* {0xa1, 0x21, 0x2a, 0x88, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ +/* {0xa1, 0x21, 0x2b, 0x34, 0x00, 0x00, 0x00, 0x10}, * set by setfreq */ /* */ {0xa1, 0x21, 0x10, 0x83, 0x00, 0x00, 0x00, 0x10}, /* {0xb1, 0x21, 0x01, 0x88, 0x70, 0x00, 0x00, 0x10}, */ @@ -673,7 +700,7 @@ static const u8 ov7648_sensor_init[][8] = { {0xd1, 0x21, 0x21, 0x86, 0x00, 0xde, 0xa0, 0x10}, /* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ /* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ - {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, +/* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */ /*...*/ /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ /* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN @@ -1294,11 +1321,9 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->gamma = GAMMA_DEF; sd->autogain = AUTOGAIN_DEF; sd->ag_cnt = -1; - if (sd->sensor != SENSOR_OV7630) - sd->vflip = 0; - else - sd->vflip = 1; + sd->vflip = VFLIP_DEF; sd->infrared = INFRARED_DEF; + sd->freq = FREQ_DEF; sd->quality = QUALITY_DEF; sd->jpegqual = 80; @@ -1569,7 +1594,7 @@ static void setautogain(struct gspca_dev *gspca_dev) else comb = 0xa0; if (sd->autogain) - comb |= 0x02; + comb |= 0x03; i2c_w1(&sd->gspca_dev, 0x13, comb); return; } @@ -1585,12 +1610,15 @@ static void setvflip(struct sd *sd) { u8 comn; - if (sd->sensor == SENSOR_OV7630) + if (sd->sensor == SENSOR_OV7630) { comn = 0x02; - else + if (!sd->vflip) + comn |= 0x80; + } else { comn = 0x06; - if (sd->vflip) - comn |= 0x80; + if (sd->vflip) + comn |= 0x80; + } i2c_w1(&sd->gspca_dev, 0x75, comn); } @@ -1602,6 +1630,58 @@ static void setinfrared(struct sd *sd) sd->infrared ? 0x66 : 0x64); } +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->sensor == SENSOR_OV7660) { + switch (sd->freq) { + case 0: /* Banding filter disabled */ + i2c_w1(gspca_dev, 0x13, 0xdf); + break; + case 1: /* 50 hz */ + i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x3b, 0x0a); + break; + case 2: /* 60 hz */ + i2c_w1(gspca_dev, 0x13, 0xff); + i2c_w1(gspca_dev, 0x3b, 0x02); + break; + } + } else { + u8 reg2a = 0, reg2b = 0, reg2d = 0; + + /* Get reg2a / reg2d base values */ + switch (sd->sensor) { + case SENSOR_OV7630: + reg2a = 0x08; + reg2d = 0x01; + break; + case SENSOR_OV7648: + reg2a = 0x11; + reg2d = 0x81; + break; + } + + switch (sd->freq) { + case 0: /* Banding filter disabled */ + break; + case 1: /* 50 hz (filter on and framerate adj) */ + reg2a |= 0x80; + reg2b = 0xac; + reg2d |= 0x04; + break; + case 2: /* 60 hz (filter on, no framerate adj) */ + reg2a |= 0x80; + reg2d |= 0x04; + break; + } + i2c_w1(gspca_dev, 0x2a, reg2a); + i2c_w1(gspca_dev, 0x2b, reg2b); + i2c_w1(gspca_dev, 0x2d, reg2d); + } +} + static void setjpegqual(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1828,6 +1908,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setbrightness(gspca_dev); setcontrast(gspca_dev); setautogain(gspca_dev); + setfreq(gspca_dev); return 0; } @@ -2131,6 +2212,24 @@ static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->freq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->freq; + return 0; +} + static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { @@ -2159,6 +2258,27 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, return 0; } +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -2173,6 +2293,7 @@ static const struct sd_desc sd_desc = { .dq_callback = do_autogain, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, + .querymenu = sd_querymenu, }; /* -- module initialisation -- */ @@ -2233,7 +2354,7 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, #endif {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, -/* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, {} }; diff --git a/drivers/media/video/gspca/stv06xx/Makefile b/drivers/media/video/gspca/stv06xx/Makefile index feeaa94ab58..2f3c3a606ce 100644 --- a/drivers/media/video/gspca/stv06xx/Makefile +++ b/drivers/media/video/gspca/stv06xx/Makefile @@ -3,7 +3,8 @@ obj-$(CONFIG_USB_STV06XX) += gspca_stv06xx.o gspca_stv06xx-objs := stv06xx.o \ stv06xx_vv6410.o \ stv06xx_hdcs.o \ - stv06xx_pb0100.o + stv06xx_pb0100.o \ + stv06xx_st6422.o EXTRA_CFLAGS += -Idrivers/media/video/gspca diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index e573c340632..0da8e0de045 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -92,11 +92,10 @@ static int stv06xx_write_sensor_finish(struct sd *sd) { int err = 0; - if (IS_850(sd)) { + if (sd->bridge == BRIDGE_STV610) { struct usb_device *udev = sd->gspca_dev.dev; __u8 *buf = sd->gspca_dev.usb_buf; - /* Quickam Web needs an extra packet */ buf[0] = 0; err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x04, 0x40, 0x1704, 0, buf, 1, @@ -253,7 +252,7 @@ static int stv06xx_init(struct gspca_dev *gspca_dev) err = sd->sensor->init(sd); - if (dump_sensor) + if (dump_sensor && sd->sensor->dump) sd->sensor->dump(sd); return (err < 0) ? err : 0; @@ -318,6 +317,8 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, __u8 *data, /* isoc packet */ int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; + PDEBUG(D_PACK, "Packet of length %d arrived", len); /* A packet may contain several frames @@ -343,14 +344,29 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, if (len < chunk_len) { PDEBUG(D_ERR, "URB packet length is smaller" " than the specified chunk length"); + gspca_dev->last_packet_type = DISCARD_PACKET; return; } + /* First byte seem to be 02=data 2nd byte is unknown??? */ + if (sd->bridge == BRIDGE_ST6422 && (id & 0xFF00) == 0x0200) + goto frame_data; + switch (id) { case 0x0200: case 0x4200: +frame_data: PDEBUG(D_PACK, "Frame data packet detected"); + if (sd->to_skip) { + int skip = (sd->to_skip < chunk_len) ? + sd->to_skip : chunk_len; + data += skip; + len -= skip; + chunk_len -= skip; + sd->to_skip -= skip; + } + gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, chunk_len); break; @@ -365,6 +381,9 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + if (sd->bridge == BRIDGE_ST6422) + sd->to_skip = gspca_dev->width * 4; + if (chunk_len) PDEBUG(D_ERR, "Chunk length is " "non-zero on a SOF"); @@ -395,8 +414,12 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, /* Unknown chunk with 2 bytes of data, occurs 2-3 times per USB interrupt */ break; + case 0x42ff: + PDEBUG(D_PACK, "Chunk 0x42ff detected"); + /* Special chunk seen sometimes on the ST6422 */ + break; default: - PDEBUG(D_PACK, "Unknown chunk %d detected", id); + PDEBUG(D_PACK, "Unknown chunk 0x%04x detected", id); /* Unknown chunk */ } data += chunk_len; @@ -428,11 +451,16 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; sd->desc = sd_desc; + sd->bridge = id->driver_info; gspca_dev->sd_desc = &sd->desc; if (dump_bridge) stv06xx_dump_bridge(sd); + sd->sensor = &stv06xx_sensor_st6422; + if (!sd->sensor->probe(sd)) + return 0; + sd->sensor = &stv06xx_sensor_vv6410; if (!sd->sensor->probe(sd)) return 0; @@ -457,9 +485,20 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x046d, 0x0840)}, /* QuickCam Express */ - {USB_DEVICE(0x046d, 0x0850)}, /* LEGO cam / QuickCam Web */ - {USB_DEVICE(0x046d, 0x0870)}, /* Dexxa WebCam USB */ + /* QuickCam Express */ + {USB_DEVICE(0x046d, 0x0840), .driver_info = BRIDGE_STV600 }, + /* LEGO cam / QuickCam Web */ + {USB_DEVICE(0x046d, 0x0850), .driver_info = BRIDGE_STV610 }, + /* Dexxa WebCam USB */ + {USB_DEVICE(0x046d, 0x0870), .driver_info = BRIDGE_STV602 }, + /* QuickCam Messenger */ + {USB_DEVICE(0x046D, 0x08F0), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Communicate */ + {USB_DEVICE(0x046D, 0x08F5), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Messenger (new) */ + {USB_DEVICE(0x046D, 0x08F6), .driver_info = BRIDGE_ST6422 }, + /* QuickCam Messenger (new) */ + {USB_DEVICE(0x046D, 0x08DA), .driver_info = BRIDGE_ST6422 }, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index 1207e7d17f1..9df7137fe67 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -93,6 +93,17 @@ struct sd { /* Sensor private data */ void *sensor_priv; + + /* The first 4 lines produced by the stv6422 are no good, this keeps + track of how many bytes we still need to skip during a frame */ + int to_skip; + + /* Bridge / Camera type */ + u8 bridge; + #define BRIDGE_STV600 0 + #define BRIDGE_STV602 1 + #define BRIDGE_STV610 2 + #define BRIDGE_ST6422 3 /* With integrated sensor */ }; int stv06xx_write_bridge(struct sd *sd, u16 address, u16 i2c_data); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index b1690381420..3039ec208f3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -434,7 +434,7 @@ static int hdcs_probe_1x00(struct sd *sd) hdcs->exp.er = 100; /* - * Frame rate on HDCS-1000 0x46D:0x840 depends on PSMP: + * Frame rate on HDCS-1000 with STV600 depends on PSMP: * 4 = doesn't work at all * 5 = 7.8 fps, * 6 = 6.9 fps, @@ -443,7 +443,7 @@ static int hdcs_probe_1x00(struct sd *sd) * 15 = 4.4 fps, * 31 = 2.8 fps * - * Frame rate on HDCS-1000 0x46D:0x870 depends on PSMP: + * Frame rate on HDCS-1000 with STV602 depends on PSMP: * 15 = doesn't work at all * 18 = doesn't work at all * 19 = 7.3 fps @@ -453,7 +453,7 @@ static int hdcs_probe_1x00(struct sd *sd) * 24 = 6.3 fps * 30 = 5.4 fps */ - hdcs->psmp = IS_870(sd) ? 20 : 5; + hdcs->psmp = (sd->bridge == BRIDGE_STV602) ? 20 : 5; sd->sensor_priv = hdcs; @@ -530,7 +530,7 @@ static int hdcs_init(struct sd *sd) int i, err = 0; /* Set the STV0602AA in STV0600 emulation mode */ - if (IS_870(sd)) + if (sd->bridge == BRIDGE_STV602) stv06xx_write_bridge(sd, STV_STV0600_EMULATION, 1); /* Execute the bridge init */ @@ -558,7 +558,7 @@ static int hdcs_init(struct sd *sd) return err; /* Set PGA sample duration - (was 0x7E for IS_870, but caused slow framerate with HDCS-1020) */ + (was 0x7E for the STV602, but caused slow framerate with HDCS-1020) */ if (IS_1020(sd)) err = stv06xx_write_sensor(sd, HDCS_TCTRL, (HDCS_ADC_START_SIG_DUR << 6) | hdcs->psmp); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h index e88c42f7d2f..934b9cebc1a 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -32,14 +32,13 @@ #include "stv06xx.h" -#define IS_850(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x850) -#define IS_870(sd) ((sd)->gspca_dev.dev->descriptor.idProduct == 0x870) #define IS_1020(sd) ((sd)->sensor == &stv06xx_sensor_hdcs1020) extern const struct stv06xx_sensor stv06xx_sensor_vv6410; extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00; extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020; extern const struct stv06xx_sensor stv06xx_sensor_pb0100; +extern const struct stv06xx_sensor stv06xx_sensor_st6422; struct stv06xx_sensor { /* Defines the name of a sensor */ diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c new file mode 100644 index 00000000000..87cb5b9ddfa --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -0,0 +1,453 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * 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 "stv06xx_st6422.h" + +static struct v4l2_pix_format st6422_mode[] = { + /* Note we actually get 124 lines of data, of which we skip the 4st + 4 as they are garbage */ + { + 162, + 120, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 162 * 120, + .bytesperline = 162, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1 + }, + /* Note we actually get 248 lines of data, of which we skip the 4st + 4 as they are garbage, and we tell the app it only gets the + first 240 of the 244 lines it actually gets, so that it ignores + the last 4. */ + { + 324, + 240, + V4L2_PIX_FMT_SGRBG8, + V4L2_FIELD_NONE, + .sizeimage = 324 * 244, + .bytesperline = 324, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0 + }, +}; + +static const struct ctrl st6422_ctrl[] = { +#define BRIGHTNESS_IDX 0 + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 31, + .step = 1, + .default_value = 3 + }, + .set = st6422_set_brightness, + .get = st6422_get_brightness + }, +#define CONTRAST_IDX 1 + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = 11 + }, + .set = st6422_set_contrast, + .get = st6422_get_contrast + }, +#define GAIN_IDX 2 + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64 + }, + .set = st6422_set_gain, + .get = st6422_get_gain + }, +#define EXPOSURE_IDX 3 + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 1023, + .step = 1, + .default_value = 256 + }, + .set = st6422_set_exposure, + .get = st6422_get_exposure + }, +}; + +static int st6422_probe(struct sd *sd) +{ + int i; + s32 *sensor_settings; + + if (sd->bridge != BRIDGE_ST6422) + return -ENODEV; + + info("st6422 sensor detected"); + + sensor_settings = kmalloc(ARRAY_SIZE(st6422_ctrl) * sizeof(s32), + GFP_KERNEL); + if (!sensor_settings) + return -ENOMEM; + + sd->gspca_dev.cam.cam_mode = st6422_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); + sd->desc.ctrls = st6422_ctrl; + sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl); + sd->sensor_priv = sensor_settings; + + for (i = 0; i < sd->desc.nctrls; i++) + sensor_settings[i] = st6422_ctrl[i].qctrl.default_value; + + return 0; +} + +static int st6422_init(struct sd *sd) +{ + int err = 0, i; + + const u16 st6422_bridge_init[][2] = { + { STV_ISO_ENABLE, 0x00 }, /* disable capture */ + { 0x1436, 0x00 }, + { 0x1432, 0x03 }, /* 0x00-0x1F brightness */ + { 0x143a, 0xF9 }, /* 0x00-0x0F contrast */ + { 0x0509, 0x38 }, /* R */ + { 0x050a, 0x38 }, /* G */ + { 0x050b, 0x38 }, /* B */ + { 0x050c, 0x2A }, + { 0x050d, 0x01 }, + + + { 0x1431, 0x00 }, /* 0x00-0x07 ??? */ + { 0x1433, 0x34 }, /* 160x120, 0x00-0x01 night filter */ + { 0x1438, 0x18 }, /* 640x480 */ +/* 18 bayes */ +/* 10 compressed? */ + + { 0x1439, 0x00 }, +/* antiflimmer?? 0xa2 ger perfekt bild mot monitor */ + + { 0x143b, 0x05 }, + { 0x143c, 0x00 }, /* 0x00-0x01 - ??? */ + + +/* shutter time 0x0000-0x03FF */ +/* low value give good picures on moving objects (but requires much light) */ +/* high value gives good picures in darkness (but tends to be overexposed) */ + { 0x143e, 0x01 }, + { 0x143d, 0x00 }, + + { 0x1442, 0xe2 }, +/* write: 1x1x xxxx */ +/* read: 1x1x xxxx */ +/* bit 5 == button pressed and hold if 0 */ +/* write 0xe2,0xea */ + +/* 0x144a */ +/* 0x00 init */ +/* bit 7 == button has been pressed, but not handled */ + +/* interrupt */ +/* if(urb->iso_frame_desc[i].status == 0x80) { */ +/* if(urb->iso_frame_desc[i].status == 0x88) { */ + + { 0x1500, 0xd0 }, + { 0x1500, 0xd0 }, + { 0x1500, 0x50 }, /* 0x00 - 0xFF 0x80 == compr ? */ + + { 0x1501, 0xaf }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + { 0x1502, 0xc2 }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + { 0x1503, 0x45 }, +/* high val-> ljus area blir morkare. */ +/* low val -> ljus area blir ljusare. */ + + { 0x1505, 0x02 }, +/* 2 : 324x248 80352 bytes */ +/* 7 : 248x162 40176 bytes */ +/* c+f: 162*124 20088 bytes */ + + { 0x150e, 0x8e }, + { 0x150f, 0x37 }, + { 0x15c0, 0x00 }, + { 0x15c1, 1023 }, /* 160x120, ISOC_PACKET_SIZE */ + { 0x15c3, 0x08 }, /* 0x04/0x14 ... test pictures ??? */ + + + { 0x143f, 0x01 }, /* commit settings */ + + }; + + for (i = 0; i < ARRAY_SIZE(st6422_bridge_init) && !err; i++) { + err = stv06xx_write_bridge(sd, st6422_bridge_init[i][0], + st6422_bridge_init[i][1]); + } + + return err; +} + +static void st6422_disconnect(struct sd *sd) +{ + sd->sensor = NULL; + kfree(sd->sensor_priv); +} + +static int st6422_start(struct sd *sd) +{ + int err, packet_size; + struct cam *cam = &sd->gspca_dev.cam; + s32 *sensor_settings = sd->sensor_priv; + struct usb_host_interface *alt; + struct usb_interface *intf; + + intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); + alt = usb_altnum_to_altsetting(intf, sd->gspca_dev.alt); + if (!alt) { + PDEBUG(D_ERR, "Couldn't get altsetting"); + return -EIO; + } + + packet_size = le16_to_cpu(alt->endpoint[0].desc.wMaxPacketSize); + err = stv06xx_write_bridge(sd, 0x15c1, packet_size); + if (err < 0) + return err; + + if (cam->cam_mode[sd->gspca_dev.curr_mode].priv) + err = stv06xx_write_bridge(sd, 0x1505, 0x0f); + else + err = stv06xx_write_bridge(sd, 0x1505, 0x02); + if (err < 0) + return err; + + err = st6422_set_brightness(&sd->gspca_dev, + sensor_settings[BRIGHTNESS_IDX]); + if (err < 0) + return err; + + err = st6422_set_contrast(&sd->gspca_dev, + sensor_settings[CONTRAST_IDX]); + if (err < 0) + return err; + + err = st6422_set_exposure(&sd->gspca_dev, + sensor_settings[EXPOSURE_IDX]); + if (err < 0) + return err; + + err = st6422_set_gain(&sd->gspca_dev, + sensor_settings[GAIN_IDX]); + if (err < 0) + return err; + + PDEBUG(D_STREAM, "Starting stream"); + + return 0; +} + +static int st6422_stop(struct sd *sd) +{ + PDEBUG(D_STREAM, "Halting stream"); + + return 0; +} + +static int st6422_get_brightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[BRIGHTNESS_IDX]; + + PDEBUG(D_V4L2, "Read brightness %d", *val); + + return 0; +} + +static int st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[BRIGHTNESS_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + /* val goes from 0 -> 31 */ + PDEBUG(D_V4L2, "Set brightness to %d", val); + err = stv06xx_write_bridge(sd, 0x1432, val); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[CONTRAST_IDX]; + + PDEBUG(D_V4L2, "Read contrast %d", *val); + + return 0; +} + +static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[CONTRAST_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + /* Val goes from 0 -> 15 */ + PDEBUG(D_V4L2, "Set contrast to %d\n", val); + err = stv06xx_write_bridge(sd, 0x143a, 0xf0 | val); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_get_gain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[GAIN_IDX]; + + PDEBUG(D_V4L2, "Read gain %d", *val); + + return 0; +} + +static int st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[GAIN_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + PDEBUG(D_V4L2, "Set gain to %d", val); + + /* Set red, green, blue, gain */ + err = stv06xx_write_bridge(sd, 0x0509, val); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050a, val); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050b, val); + if (err < 0) + return err; + + /* 2 mystery writes */ + err = stv06xx_write_bridge(sd, 0x050c, 0x2a); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x050d, 0x01); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} + +static int st6422_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + *val = sensor_settings[EXPOSURE_IDX]; + + PDEBUG(D_V4L2, "Read exposure %d", *val); + + return 0; +} + +static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val) +{ + int err; + struct sd *sd = (struct sd *) gspca_dev; + s32 *sensor_settings = sd->sensor_priv; + + sensor_settings[EXPOSURE_IDX] = val; + + if (!gspca_dev->streaming) + return 0; + + PDEBUG(D_V4L2, "Set exposure to %d\n", val); + err = stv06xx_write_bridge(sd, 0x143d, val & 0xff); + if (err < 0) + return err; + + err = stv06xx_write_bridge(sd, 0x143e, val >> 8); + if (err < 0) + return err; + + /* commit settings */ + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + return (err < 0) ? err : 0; +} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h new file mode 100644 index 00000000000..b2d45fe5052 --- /dev/null +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h @@ -0,0 +1,59 @@ +/* + * Support for the sensor part which is integrated (I think) into the + * st6422 stv06xx alike bridge, as its integrated there are no i2c writes + * but instead direct bridge writes. + * + * Copyright (c) 2009 Hans de Goede <hdegoede@redhat.com> + * + * Strongly based on qc-usb-messenger, which is: + * Copyright (c) 2001 Jean-Fredric Clere, Nikolas Zimmermann, Georg Acher + * Mark Cave-Ayland, Carlo E Prelz, Dick Streefland + * Copyright (c) 2002, 2003 Tuukka Toivonen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef STV06XX_ST6422_H_ +#define STV06XX_ST6422_H_ + +#include "stv06xx_sensor.h" + +static int st6422_probe(struct sd *sd); +static int st6422_start(struct sd *sd); +static int st6422_init(struct sd *sd); +static int st6422_stop(struct sd *sd); +static void st6422_disconnect(struct sd *sd); + +/* V4L2 controls supported by the driver */ +static int st6422_get_brightness(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_brightness(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_contrast(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_contrast(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_gain(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_gain(struct gspca_dev *gspca_dev, __s32 val); +static int st6422_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); +static int st6422_set_exposure(struct gspca_dev *gspca_dev, __s32 val); + +const struct stv06xx_sensor stv06xx_sensor_st6422 = { + .name = "ST6422", + .init = st6422_init, + .probe = st6422_probe, + .start = st6422_start, + .stop = st6422_stop, + .disconnect = st6422_disconnect, +}; + +#endif diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index 84995bcf4a7..a3b77ed3f08 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -60,6 +60,8 @@ int ivtv_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl) switch (qctrl->id) { /* Standard V4L2 controls */ + case V4L2_CID_USER_CLASS: + return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0); case V4L2_CID_BRIGHTNESS: case V4L2_CID_HUE: case V4L2_CID_SATURATION: diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c index 459c04cbf69..4d794b42d6c 100644 --- a/drivers/media/video/mt9m001.c +++ b/drivers/media/video/mt9m001.c @@ -280,15 +280,9 @@ static int mt9m001_try_fmt(struct soc_camera_device *icd, { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < 32 + icd->y_skip_top) - pix->height = 32 + icd->y_skip_top; - if (pix->height > 1024 + icd->y_skip_top) - pix->height = 1024 + icd->y_skip_top; - if (pix->width < 48) - pix->width = 48; - if (pix->width > 1280) - pix->width = 1280; - pix->width &= ~0x01; /* has to be even, unsure why was ~3 */ + v4l_bound_align_image(&pix->width, 48, 1280, 1, + &pix->height, 32 + icd->y_skip_top, + 1024 + icd->y_skip_top, 0, 0); return 0; } diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c index f72aeb7c4de..4207fb34267 100644 --- a/drivers/media/video/mt9t031.c +++ b/drivers/media/video/mt9t031.c @@ -385,17 +385,9 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd, { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < MT9T031_MIN_HEIGHT) - pix->height = MT9T031_MIN_HEIGHT; - if (pix->height > MT9T031_MAX_HEIGHT) - pix->height = MT9T031_MAX_HEIGHT; - if (pix->width < MT9T031_MIN_WIDTH) - pix->width = MT9T031_MIN_WIDTH; - if (pix->width > MT9T031_MAX_WIDTH) - pix->width = MT9T031_MAX_WIDTH; - - pix->width &= ~0x01; /* has to be even */ - pix->height &= ~0x01; /* has to be even */ + v4l_bound_align_image( + &pix->width, MT9T031_MIN_WIDTH, MT9T031_MAX_WIDTH, 1, + &pix->height, MT9T031_MIN_HEIGHT, MT9T031_MAX_HEIGHT, 1, 0); return 0; } diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c index be20d312b1d..dbdcc86ae50 100644 --- a/drivers/media/video/mt9v022.c +++ b/drivers/media/video/mt9v022.c @@ -364,15 +364,9 @@ static int mt9v022_try_fmt(struct soc_camera_device *icd, { struct v4l2_pix_format *pix = &f->fmt.pix; - if (pix->height < 32 + icd->y_skip_top) - pix->height = 32 + icd->y_skip_top; - if (pix->height > 480 + icd->y_skip_top) - pix->height = 480 + icd->y_skip_top; - if (pix->width < 48) - pix->width = 48; - if (pix->width > 752) - pix->width = 752; - pix->width &= ~0x03; /* ? */ + v4l_bound_align_image(&pix->width, 48, 752, 2 /* ? */, + &pix->height, 32 + icd->y_skip_top, + 480 + icd->y_skip_top, 0, 0); return 0; } diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c index 08cfd3e4ae8..0bc2cf573c7 100644 --- a/drivers/media/video/ov511.c +++ b/drivers/media/video/ov511.c @@ -211,8 +211,6 @@ static const int i2c_detect_tries = 5; static struct usb_device_id device_table [] = { { USB_DEVICE(VEND_OMNIVISION, PROD_OV511) }, { USB_DEVICE(VEND_OMNIVISION, PROD_OV511PLUS) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518) }, - { USB_DEVICE(VEND_OMNIVISION, PROD_OV518PLUS) }, { USB_DEVICE(VEND_MATTEL, PROD_ME2CAM) }, { } /* Terminating entry */ }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c index 10ef1a2c13e..416933ca607 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-audio.c +++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c @@ -48,11 +48,13 @@ static const int routing_scheme0[] = { MSP_DSP_IN_SCART), }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, }; void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -65,7 +67,7 @@ void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo"); if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && + ((sp = routing_schemes[sid]) != NULL) && (hdw->input_val >= 0) && (hdw->input_val < sp->cnt)) { input = sp->def[hdw->input_val]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c index 9023adf3fdc..68980e19409 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c @@ -49,11 +49,13 @@ static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_SVIDEO] = 0, }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_ONAIR] = { - .def = routing_scheme1, - .cnt = ARRAY_SIZE(routing_scheme1), - }, +static const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, }; @@ -65,12 +67,11 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) u32 input; pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", hdw->input_val); - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - input = sp->def[hdw->input_val]; - } else { + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev v4l2 set_input:" " Invalid routing scheme (%u)" @@ -78,6 +79,7 @@ void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } + input = sp->def[hdw->input_val]; sd->ops->audio->s_routing(sd, input, 0, 0); } } diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c index 05e52358ae4..82c13583575 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -68,6 +68,11 @@ static const struct routing_scheme_item routing_scheme0[] = { }, }; +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + /* Specific to gotview device */ static const struct routing_scheme_item routing_schemegv[] = { [PVR2_CVAL_INPUT_TV] = { @@ -90,15 +95,14 @@ static const struct routing_scheme_item routing_schemegv[] = { }, }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, - [PVR2_ROUTING_SCHEME_GOTVIEW] = { - .def = routing_schemegv, - .cnt = ARRAY_SIZE(routing_schemegv), - }, +static const struct routing_scheme routing_defgv = { + .def = routing_schemegv, + .cnt = ARRAY_SIZE(routing_schemegv), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_GOTVIEW] = &routing_defgv, }; void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -110,13 +114,11 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) const struct routing_scheme *sp; unsigned int sid = hdw->hdw_desc->signal_routing_scheme; - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - vid_input = sp->def[hdw->input_val].vid; - aud_input = sp->def[hdw->input_val].aud; - } else { + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev cx2584x set_input:" " Invalid routing scheme (%u)" @@ -124,7 +126,8 @@ void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } - + vid_input = sp->def[hdw->input_val].vid; + aud_input = sp->def[hdw->input_val].aud; pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x set_input vid=0x%x aud=0x%x", vid_input, aud_input); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 0c745b142fb..cbc388729d7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -85,8 +85,8 @@ MODULE_PARM_DESC(video_std,"specify initial video standard"); module_param_array(tolerance, int, NULL, 0444); MODULE_PARM_DESC(tolerance,"specify stream error tolerance"); -/* US Broadcast channel 7 (175.25 MHz) */ -static int default_tv_freq = 175250000L; +/* US Broadcast channel 3 (61.25 MHz), to help with testing */ +static int default_tv_freq = 61250000L; /* 104.3 MHz, a usable FM station for my area */ static int default_radio_freq = 104300000L; @@ -1987,6 +1987,34 @@ static unsigned int pvr2_copy_i2c_addr_list( } +static void pvr2_hdw_cx25840_vbi_hack(struct pvr2_hdw *hdw) +{ + /* + Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit of nuttiness + for cx25840 causes that module to correctly set up its video + scaling. This is really a problem in the cx25840 module itself, + but we work around it here. The problem has not been seen in + ivtv because there VBI is supported and set up. We don't do VBI + here (at least not yet) and thus we never attempted to even set + it up. + */ + struct v4l2_format fmt; + if (hdw->decoder_client_id != PVR2_CLIENT_ID_CX25840) { + /* We're not using a cx25840 so don't enable the hack */ + return; + } + + pvr2_trace(PVR2_TRACE_INIT, + "Module ID %u:" + " Executing cx25840 VBI hack", + hdw->decoder_client_id); + memset(&fmt, 0, sizeof(fmt)); + fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; + v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, + video, s_fmt, &fmt); +} + + static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, const struct pvr2_device_client_desc *cd) { @@ -2078,30 +2106,6 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, /* client-specific setup... */ switch (mid) { case PVR2_CLIENT_ID_CX25840: - hdw->decoder_client_id = mid; - { - /* - Mike Isely <isely@pobox.com> 19-Nov-2006 - This - bit of nuttiness for cx25840 causes that module - to correctly set up its video scaling. This is - really a problem in the cx25840 module itself, - but we work around it here. The problem has not - been seen in ivtv because there VBI is supported - and set up. We don't do VBI here (at least not - yet) and thus we never attempted to even set it - up. - */ - struct v4l2_format fmt; - pvr2_trace(PVR2_TRACE_INIT, - "Module ID %u:" - " Executing cx25840 VBI hack", - mid); - memset(&fmt, 0, sizeof(fmt)); - fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE; - v4l2_device_call_all(&hdw->v4l2_dev, mid, - video, s_fmt, &fmt); - } - break; case PVR2_CLIENT_ID_SAA7115: hdw->decoder_client_id = mid; break; @@ -2202,6 +2206,8 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) cptr->info->set_value(cptr,~0,cptr->info->default_value); } + pvr2_hdw_cx25840_vbi_hack(hdw); + /* Set up special default values for the television and radio frequencies here. It's not really important what these defaults are, but I set them to something usable in the Chicago area just @@ -2954,6 +2960,7 @@ static void pvr2_subdev_update(struct pvr2_hdw *hdw) vs = hdw->std_mask_cur; v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_std, vs); + pvr2_hdw_cx25840_vbi_hack(hdw); } hdw->tuner_signal_stale = !0; hdw->cropcap_stale = !0; @@ -4076,6 +4083,7 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) if (hdw->decoder_client_id) { v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id, core, reset, 0); + pvr2_hdw_cx25840_vbi_hack(hdw); return 0; } pvr2_trace(PVR2_TRACE_INIT, diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index d2fe7c8f2c3..4c96cf48c79 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -54,6 +54,11 @@ static const int routing_scheme0[] = { [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, }; +static const struct routing_scheme routing_def0 = { + .def = routing_scheme0, + .cnt = ARRAY_SIZE(routing_scheme0), +}; + static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4, [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5, @@ -61,15 +66,14 @@ static const int routing_scheme1[] = { [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */ }; -static const struct routing_scheme routing_schemes[] = { - [PVR2_ROUTING_SCHEME_HAUPPAUGE] = { - .def = routing_scheme0, - .cnt = ARRAY_SIZE(routing_scheme0), - }, - [PVR2_ROUTING_SCHEME_ONAIR] = { - .def = routing_scheme1, - .cnt = ARRAY_SIZE(routing_scheme1), - }, +static const struct routing_scheme routing_def1 = { + .def = routing_scheme1, + .cnt = ARRAY_SIZE(routing_scheme1), +}; + +static const struct routing_scheme *routing_schemes[] = { + [PVR2_ROUTING_SCHEME_HAUPPAUGE] = &routing_def0, + [PVR2_ROUTING_SCHEME_ONAIR] = &routing_def1, }; void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) @@ -81,12 +85,12 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)", hdw->input_val); - if ((sid < ARRAY_SIZE(routing_schemes)) && - ((sp = routing_schemes + sid) != NULL) && - (hdw->input_val >= 0) && - (hdw->input_val < sp->cnt)) { - input = sp->def[hdw->input_val]; - } else { + + sp = (sid < ARRAY_SIZE(routing_schemes)) ? + routing_schemes[sid] : NULL; + if ((sp == NULL) || + (hdw->input_val < 0) || + (hdw->input_val >= sp->cnt)) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "*** WARNING *** subdev v4l2 set_input:" " Invalid routing scheme (%u)" @@ -94,6 +98,7 @@ void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd) sid, hdw->input_val); return; } + input = sp->def[hdw->input_val]; sd->ops->video->s_routing(sd, input, 0, 0); } } diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index f60de40fd21..46e0d8ad880 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -163,13 +163,6 @@ CICR0_EOFM | CICR0_FOM) /* - * YUV422P picture size should be a multiple of 16, so the heuristic aligns - * height, width on 4 byte boundaries to reach the 16 multiple for the size. - */ -#define YUV422P_X_Y_ALIGN 4 -#define YUV422P_SIZE_ALIGN YUV422P_X_Y_ALIGN * YUV422P_X_Y_ALIGN - -/* * Structures */ enum pxa_camera_active_dma { @@ -1398,28 +1391,15 @@ static int pxa_camera_try_fmt(struct soc_camera_device *icd, return -EINVAL; } - /* limit to pxa hardware capabilities */ - if (pix->height < 32) - pix->height = 32; - if (pix->height > 2048) - pix->height = 2048; - if (pix->width < 48) - pix->width = 48; - if (pix->width > 2048) - pix->width = 2048; - pix->width &= ~0x01; - /* - * YUV422P planar format requires images size to be a 16 bytes - * multiple. If not, zeros will be inserted between Y and U planes, and - * U and V planes, and YUV422P standard would be violated. + * Limit to pxa hardware capabilities. YUV422P planar format requires + * images size to be a multiple of 16 bytes. If not, zeros will be + * inserted between Y and U planes, and U and V planes, which violates + * the YUV422P standard. */ - if (xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P) { - if (!IS_ALIGNED(pix->width * pix->height, YUV422P_SIZE_ALIGN)) - pix->height = ALIGN(pix->height, YUV422P_X_Y_ALIGN); - if (!IS_ALIGNED(pix->width * pix->height, YUV422P_SIZE_ALIGN)) - pix->width = ALIGN(pix->width, YUV422P_X_Y_ALIGN); - } + v4l_bound_align_image(&pix->width, 48, 2048, 1, + &pix->height, 32, 2048, 0, + xlate->host_fmt->fourcc == V4L2_PIX_FMT_YUV422P ? 4 : 0); pix->bytesperline = pix->width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index e305c1674ce..ba87128542e 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1640,15 +1640,8 @@ static int saa7134_try_fmt_vid_cap(struct file *file, void *priv, } f->fmt.pix.field = field; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index d369e8409ab..0db88a53d92 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -689,16 +689,8 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, /* FIXME: calculate using depth and bus width */ - if (f->fmt.pix.height < 4) - f->fmt.pix.height = 4; - if (f->fmt.pix.height > 1920) - f->fmt.pix.height = 1920; - if (f->fmt.pix.width < 2) - f->fmt.pix.width = 2; - if (f->fmt.pix.width > 2560) - f->fmt.pix.width = 2560; - f->fmt.pix.width &= ~0x01; - f->fmt.pix.height &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 2, 2560, 1, + &f->fmt.pix.height, 4, 1920, 2, 0); f->fmt.pix.bytesperline = f->fmt.pix.width * DIV_ROUND_UP(xlate->host_fmt->depth, 8); diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c index b30c4924821..b90e9da3167 100644 --- a/drivers/media/video/tcm825x.c +++ b/drivers/media/video/tcm825x.c @@ -878,7 +878,7 @@ static int tcm825x_probe(struct i2c_client *client, return rval; } -static int __exit tcm825x_remove(struct i2c_client *client) +static int tcm825x_remove(struct i2c_client *client) { struct tcm825x_sensor *sensor = i2c_get_clientdata(client); @@ -902,7 +902,7 @@ static struct i2c_driver tcm825x_i2c_driver = { .name = TCM825X_NAME, }, .probe = tcm825x_probe, - .remove = __exit_p(tcm825x_remove), + .remove = tcm825x_remove, .id_table = tcm825x_id, }; diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig index e4cb99c1f94..adb1c044ad7 100644 --- a/drivers/media/video/usbvideo/Kconfig +++ b/drivers/media/video/usbvideo/Kconfig @@ -38,10 +38,13 @@ config USB_KONICAWC module will be called konicawc. config USB_QUICKCAM_MESSENGER - tristate "USB Logitech Quickcam Messenger" + tristate "USB Logitech Quickcam Messenger (DEPRECATED)" depends on VIDEO_V4L1 select VIDEO_USBVIDEO ---help--- + This driver is DEPRECATED please use the gspca stv06xx module + instead. + Say Y or M here to enable support for the USB Logitech Quickcam Messenger webcam. diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index f96475626da..b91d66a767d 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -802,6 +802,17 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct v4l2_device *v4l2_dev, /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + error: /* If we have a client but no subdev, then something went wrong and we must unregister the client. */ @@ -852,6 +863,17 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct v4l2_device *v4l2_dev, /* Decrease the module use count to match the first try_module_get. */ module_put(client->driver->driver.owner); + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, 0, NULL); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + error: /* If we have a client but no subdev, then something went wrong and we must unregister the client. */ @@ -872,6 +894,89 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev_addr(struct v4l2_device *v4l2_dev } EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev_addr); +/* Load an i2c sub-device. */ +struct v4l2_subdev *v4l2_i2c_new_subdev_board(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, const char *module_name, + struct i2c_board_info *info, const unsigned short *probe_addrs) +{ + struct v4l2_subdev *sd = NULL; + struct i2c_client *client; + + BUG_ON(!v4l2_dev); + + if (module_name) + request_module(module_name); + + /* Create the i2c client */ + if (info->addr == 0 && probe_addrs) + client = i2c_new_probed_device(adapter, info, probe_addrs); + else + client = i2c_new_device(adapter, info); + + /* Note: by loading the module first we are certain that c->driver + will be set if the driver was found. If the module was not loaded + first, then the i2c core tries to delay-load the module for us, + and then c->driver is still NULL until the module is finally + loaded. This delay-load mechanism doesn't work if other drivers + want to use the i2c device, so explicitly loading the module + is the best alternative. */ + if (client == NULL || client->driver == NULL) + goto error; + + /* Lock the module so we can safely get the v4l2_subdev pointer */ + if (!try_module_get(client->driver->driver.owner)) + goto error; + sd = i2c_get_clientdata(client); + + /* Register with the v4l2_device which increases the module's + use count as well. */ + if (v4l2_device_register_subdev(v4l2_dev, sd)) + sd = NULL; + /* Decrease the module use count to match the first try_module_get. */ + module_put(client->driver->driver.owner); + + if (sd) { + /* We return errors from v4l2_subdev_call only if we have the + callback as the .s_config is not mandatory */ + int err = v4l2_subdev_call(sd, core, s_config, + info->irq, info->platform_data); + + if (err && err != -ENOIOCTLCMD) { + v4l2_device_unregister_subdev(sd); + sd = NULL; + } + } + +error: + /* If we have a client but no subdev, then something went wrong and + we must unregister the client. */ + if (client && sd == NULL) + i2c_unregister_device(client); + return sd; +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_board); + +struct v4l2_subdev *v4l2_i2c_new_subdev_cfg(struct v4l2_device *v4l2_dev, + struct i2c_adapter *adapter, + const char *module_name, const char *client_type, + int irq, void *platform_data, + u8 addr, const unsigned short *probe_addrs) +{ + struct i2c_board_info info; + + /* Setup the i2c board info with the device type and + the device address. */ + memset(&info, 0, sizeof(info)); + strlcpy(info.type, client_type, sizeof(info.type)); + info.addr = addr; + info.irq = irq; + info.platform_data = platform_data; + + return v4l2_i2c_new_subdev_board(v4l2_dev, adapter, module_name, + &info, probe_addrs); +} +EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev_cfg); + /* Return i2c client address of v4l2_subdev. */ unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd) { @@ -916,4 +1021,78 @@ const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type) } EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs); -#endif +#endif /* defined(CONFIG_I2C) */ + +/* Clamp x to be between min and max, aligned to a multiple of 2^align. min + * and max don't have to be aligned, but there must be at least one valid + * value. E.g., min=17,max=31,align=4 is not allowed as there are no multiples + * of 16 between 17 and 31. */ +static unsigned int clamp_align(unsigned int x, unsigned int min, + unsigned int max, unsigned int align) +{ + /* Bits that must be zero to be aligned */ + unsigned int mask = ~((1 << align) - 1); + + /* Round to nearest aligned value */ + if (align) + x = (x + (1 << (align - 1))) & mask; + + /* Clamp to aligned value of min and max */ + if (x < min) + x = (min + ~mask) & mask; + else if (x > max) + x = max & mask; + + return x; +} + +/* Bound an image to have a width between wmin and wmax, and height between + * hmin and hmax, inclusive. Additionally, the width will be a multiple of + * 2^walign, the height will be a multiple of 2^halign, and the overall size + * (width*height) will be a multiple of 2^salign. The image may be shrunk + * or enlarged to fit the alignment constraints. + * + * The width or height maximum must not be smaller than the corresponding + * minimum. The alignments must not be so high there are no possible image + * sizes within the allowed bounds. wmin and hmin must be at least 1 + * (don't use 0). If you don't care about a certain alignment, specify 0, + * as 2^0 is 1 and one byte alignment is equivalent to no alignment. If + * you only want to adjust downward, specify a maximum that's the same as + * the initial value. + */ +void v4l_bound_align_image(u32 *w, unsigned int wmin, unsigned int wmax, + unsigned int walign, + u32 *h, unsigned int hmin, unsigned int hmax, + unsigned int halign, unsigned int salign) +{ + *w = clamp_align(*w, wmin, wmax, walign); + *h = clamp_align(*h, hmin, hmax, halign); + + /* Usually we don't need to align the size and are done now. */ + if (!salign) + return; + + /* How much alignment do we have? */ + walign = __ffs(*w); + halign = __ffs(*h); + /* Enough to satisfy the image alignment? */ + if (walign + halign < salign) { + /* Max walign where there is still a valid width */ + unsigned int wmaxa = __fls(wmax ^ (wmin - 1)); + /* Max halign where there is still a valid height */ + unsigned int hmaxa = __fls(hmax ^ (hmin - 1)); + + /* up the smaller alignment until we have enough */ + do { + if (halign >= hmaxa || + (walign <= halign && walign < wmaxa)) { + *w = clamp_align(*w, wmin, wmax, walign + 1); + walign = __ffs(*w); + } else { + *h = clamp_align(*h, hmin, hmax, halign + 1); + halign = __ffs(*h); + } + } while (halign + walign < salign); + } +} +EXPORT_SYMBOL_GPL(v4l_bound_align_image); diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index fbfefae7886..cd726685846 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -883,15 +883,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, maxh = norm_maxh(); f->fmt.pix.field = field; - if (f->fmt.pix.height < 32) - f->fmt.pix.height = 32; - if (f->fmt.pix.height > maxh) - f->fmt.pix.height = maxh; - if (f->fmt.pix.width < 48) - f->fmt.pix.width = 48; - if (f->fmt.pix.width > maxw) - f->fmt.pix.width = maxw; - f->fmt.pix.width &= ~0x03; + v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2, + &f->fmt.pix.height, 32, maxh, 0, 0); f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; f->fmt.pix.sizeimage = diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c index f59b2bd07e8..6c3f23e31b5 100644 --- a/drivers/media/video/w9968cf.c +++ b/drivers/media/video/w9968cf.c @@ -460,7 +460,7 @@ static int w9968cf_set_picture(struct w9968cf_device*, struct video_picture); static int w9968cf_set_window(struct w9968cf_device*, struct video_window); static int w9968cf_postprocess_frame(struct w9968cf_device*, struct w9968cf_frame_t*); -static int w9968cf_adjust_window_size(struct w9968cf_device*, u16* w, u16* h); +static int w9968cf_adjust_window_size(struct w9968cf_device*, u32 *w, u32 *h); static void w9968cf_init_framelist(struct w9968cf_device*); static void w9968cf_push_frame(struct w9968cf_device*, u8 f_num); static void w9968cf_pop_frame(struct w9968cf_device*,struct w9968cf_frame_t**); @@ -1763,8 +1763,7 @@ w9968cf_set_window(struct w9968cf_device* cam, struct video_window win) #define UNSC(x) ((x) >> 10) /* Make sure we are using a supported resolution */ - if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, - (u16*)&win.height))) + if ((err = w9968cf_adjust_window_size(cam, &win.width, &win.height))) goto error; /* Scaling factors */ @@ -1914,12 +1913,9 @@ error: Return 0 on success, -1 otherwise. --------------------------------------------------------------------------*/ static int -w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) +w9968cf_adjust_window_size(struct w9968cf_device *cam, u32 *width, u32 *height) { - u16 maxw, maxh; - - if ((*width < cam->minwidth) || (*height < cam->minheight)) - return -ERANGE; + unsigned int maxw, maxh, align; maxw = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && w9968cf_vpp ? max((u16)W9968CF_MAX_WIDTH, cam->maxwidth) @@ -1927,16 +1923,10 @@ w9968cf_adjust_window_size(struct w9968cf_device* cam, u16* width, u16* height) maxh = cam->upscaling && !(cam->vpp_flag & VPP_DECOMPRESSION) && w9968cf_vpp ? max((u16)W9968CF_MAX_HEIGHT, cam->maxheight) : cam->maxheight; + align = (cam->vpp_flag & VPP_DECOMPRESSION) ? 4 : 0; - if (*width > maxw) - *width = maxw; - if (*height > maxh) - *height = maxh; - - if (cam->vpp_flag & VPP_DECOMPRESSION) { - *width &= ~15L; /* multiple of 16 */ - *height &= ~15L; - } + v4l_bound_align_image(width, cam->minwidth, maxw, align, + height, cam->minheight, maxh, align, 0); PDBGG("Window size adjusted w=%u, h=%u ", *width, *height) @@ -3043,8 +3033,8 @@ static long w9968cf_v4l_ioctl(struct file *filp, if (win.clipcount != 0 || win.flags != 0) return -EINVAL; - if ((err = w9968cf_adjust_window_size(cam, (u16*)&win.width, - (u16*)&win.height))) { + if ((err = w9968cf_adjust_window_size(cam, &win.width, + &win.height))) { DBG(4, "Resolution not supported (%ux%u). " "VIDIOCSWIN failed", win.width, win.height) return err; @@ -3116,6 +3106,7 @@ static long w9968cf_v4l_ioctl(struct file *filp, { struct video_mmap mmap; struct w9968cf_frame_t* fr; + u32 w, h; int err = 0; if (copy_from_user(&mmap, arg, sizeof(mmap))) @@ -3164,8 +3155,10 @@ static long w9968cf_v4l_ioctl(struct file *filp, } } - if ((err = w9968cf_adjust_window_size(cam, (u16*)&mmap.width, - (u16*)&mmap.height))) { + w = mmap.width; h = mmap.height; + err = w9968cf_adjust_window_size(cam, &w, &h); + mmap.width = w; mmap.height = h; + if (err) { DBG(4, "Resolution not supported (%dx%d). " "VIDIOCMCAPTURE failed", mmap.width, mmap.height) diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 643cccaa1aa..3d7df32a3d8 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -2088,16 +2088,10 @@ static int zoran_try_fmt_vid_cap(struct file *file, void *__fh, return -EINVAL; } - bpp = (zoran_formats[i].depth + 7) / 8; - fmt->fmt.pix.width &= ~((bpp == 2) ? 1 : 3); - if (fmt->fmt.pix.width > BUZ_MAX_WIDTH) - fmt->fmt.pix.width = BUZ_MAX_WIDTH; - if (fmt->fmt.pix.width < BUZ_MIN_WIDTH) - fmt->fmt.pix.width = BUZ_MIN_WIDTH; - if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT) - fmt->fmt.pix.height = BUZ_MAX_HEIGHT; - if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT) - fmt->fmt.pix.height = BUZ_MIN_HEIGHT; + bpp = DIV_ROUND_UP(zoran_formats[i].depth, 8); + v4l_bound_align_image( + &fmt->fmt.pix.width, BUZ_MIN_WIDTH, BUZ_MAX_WIDTH, bpp == 2 ? 1 : 2, + &fmt->fmt.pix.height, BUZ_MIN_HEIGHT, BUZ_MAX_HEIGHT, 0, 0); mutex_unlock(&zr->resource_lock); return 0; diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c index 20e0b447e8e..55ff25244af 100644 --- a/drivers/message/fusion/mptsas.c +++ b/drivers/message/fusion/mptsas.c @@ -3518,7 +3518,7 @@ retry_page: } else mptsas_volume_delete(ioc, sas_info->fw.id); } - mutex_lock(&ioc->sas_device_info_mutex); + mutex_unlock(&ioc->sas_device_info_mutex); /* expanders */ mutex_lock(&ioc->sas_topology_mutex); @@ -3549,7 +3549,7 @@ retry_page: goto redo_expander_scan; } } - mutex_lock(&ioc->sas_topology_mutex); + mutex_unlock(&ioc->sas_topology_mutex); } /** diff --git a/drivers/mfd/twl4030-core.c b/drivers/mfd/twl4030-core.c index cd1008c19cd..ca54996ffd0 100644 --- a/drivers/mfd/twl4030-core.c +++ b/drivers/mfd/twl4030-core.c @@ -101,6 +101,12 @@ #define twl_has_usb() false #endif +#if defined(CONFIG_TWL4030_WATCHDOG) || \ + defined(CONFIG_TWL4030_WATCHDOG_MODULE) +#define twl_has_watchdog() true +#else +#define twl_has_watchdog() false +#endif /* Triton Core internal information (BEGIN) */ @@ -526,6 +532,12 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features) usb_transceiver = child; } + if (twl_has_watchdog()) { + child = add_child(0, "twl4030_wdt", NULL, 0, false, 0, 0); + if (IS_ERR(child)) + return PTR_ERR(child); + } + if (twl_has_regulator()) { /* child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1); diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c240454fd11..8664feebc93 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -46,6 +46,7 @@ #define MANUFACTURER_INTEL 0x0089 #define I82802AB 0x00ad #define I82802AC 0x00ac +#define PF38F4476 0x881c #define MANUFACTURER_ST 0x0020 #define M50LPW080 0x002F #define M50FLW080A 0x0080 @@ -315,10 +316,20 @@ static struct cfi_fixup fixup_table[] = { { 0, 0, NULL, NULL } }; +static void cfi_fixup_major_minor(struct cfi_private *cfi, + struct cfi_pri_intelext *extp) +{ + if (cfi->mfr == MANUFACTURER_INTEL && + cfi->id == PF38F4476 && extp->MinorVersion == '3') + extp->MinorVersion = '1'; +} + static inline struct cfi_pri_intelext * read_pri_intelext(struct map_info *map, __u16 adr) { + struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *extp; + unsigned int extra_size = 0; unsigned int extp_size = sizeof(*extp); again: @@ -326,6 +337,8 @@ read_pri_intelext(struct map_info *map, __u16 adr) if (!extp) return NULL; + cfi_fixup_major_minor(cfi, extp); + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '5')) { printk(KERN_ERR " Unknown Intel/Sharp Extended Query " @@ -340,19 +353,24 @@ read_pri_intelext(struct map_info *map, __u16 adr) extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); - if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') { - unsigned int extra_size = 0; - int nb_parts, i; + if (extp->MinorVersion >= '0') { + extra_size = 0; /* Protection Register info */ extra_size += (extp->NumProtectionFields - 1) * sizeof(struct cfi_intelext_otpinfo); + } + if (extp->MinorVersion >= '1') { /* Burst Read info */ extra_size += 2; if (extp_size < sizeof(*extp) + extra_size) goto need_more; - extra_size += extp->extra[extra_size-1]; + extra_size += extp->extra[extra_size - 1]; + } + + if (extp->MinorVersion >= '3') { + int nb_parts, i; /* Number of hardware-partitions */ extra_size += 1; diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index e824b9b9b05..ccc4cfc7e4b 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -166,6 +166,7 @@ #define SST39LF040 0x00D7 #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6 +#define SST39SF040 0x00B7 #define SST49LF004B 0x0060 #define SST49LF040B 0x0050 #define SST49LF008A 0x005a @@ -1393,6 +1394,18 @@ static const struct amd_flash_info jedec_table[] = { } }, { .mfr_id = MANUFACTURER_SST, + .dev_id = SST39SF040, + .name = "SST 39SF040", + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, + .regions = { + ERASEINFO(0x01000,128), + } + }, { + .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF040B, .name = "SST 49LF040B", .devtypes = CFI_DEVICETYPE_X8, diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index cc6369ea67d..59c46126a5c 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -500,6 +500,9 @@ static struct flash_info __devinitdata m25p_data [] = { { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, }, { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, + /* Macronix */ + { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, + /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ @@ -528,6 +531,7 @@ static struct flash_info __devinitdata m25p_data [] = { { "m25p64", 0x202017, 0, 64 * 1024, 128, }, { "m25p128", 0x202018, 0, 256 * 1024, 64, }, + { "m45pe10", 0x204011, 0, 64 * 1024, 2, }, { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 82923bd2d9c..0b98654d8ee 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -105,15 +105,6 @@ config MSP_FLASH_MAP_LIMIT default "0x02000000" depends on MSP_FLASH_MAP_LIMIT_32M -config MTD_PMC_MSP_RAMROOT - tristate "Embedded RAM block device for root on PMC-Sierra MSP" - depends on PMC_MSP_EMBEDDED_ROOTFS && \ - (MTD_BLOCK || MTD_BLOCK_RO) && \ - MTD_RAM - help - This provides support for the embedded root file system - on PMC MSP devices. This memory is mapped as a MTD block device. - config MTD_SUN_UFLASH tristate "Sun Microsystems userflash support" depends on SPARC && MTD_CFI && PCI @@ -270,7 +261,7 @@ config MTD_ALCHEMY config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" - depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT + depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN help MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". For details, see <http://www.ssv-embedded.de/ssv/pc104/p169.htm> @@ -501,7 +492,7 @@ config MTD_BFIN_ASYNC If compiled as a module, it will be called bfin-async-flash. config MTD_UCLINUX - tristate "Generic uClinux RAM/ROM filesystem support" + bool "Generic uClinux RAM/ROM filesystem support" depends on MTD_PARTITIONS && MTD_RAM && !MMU help Map driver to support image based filesystems for uClinux. diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 2dbc1bec848..8bae7f9850c 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -25,7 +25,6 @@ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o -obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 576611f605d..365c77b1b87 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -40,6 +40,9 @@ struct async_state { uint32_t flash_ambctl0, flash_ambctl1; uint32_t save_ambctl0, save_ambctl1; unsigned long irq_flags; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif }; static void switch_to_flash(struct async_state *state) @@ -170,6 +173,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) if (ret > 0) { pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); add_mtd_partitions(state->mtd, pdata->parts, ret); + state->parts = pdata->parts; } else if (pdata->nr_parts) { pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); @@ -193,6 +197,7 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev) gpio_free(state->enet_flash_pin); #ifdef CONFIG_MTD_PARTITIONS del_mtd_partitions(state->mtd); + kfree(state->parts); #endif map_destroy(state->mtd); kfree(state); diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index c9681a339a5..b08a798ee25 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -36,27 +36,33 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> #include <asm/mach/flash.h> #include <mach/hardware.h> #include <asm/system.h> -#ifdef CONFIG_ARCH_P720T -#define FLASH_BASE (0x04000000) -#define FLASH_SIZE (64*1024*1024) -#endif +#define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2) -struct armflash_info { +struct armflash_subdev_info { + char name[SUBDEV_NAME_SIZE]; + struct mtd_info *mtd; + struct map_info map; struct flash_platform_data *plat; +}; + +struct armflash_info { struct resource *res; struct mtd_partition *parts; struct mtd_info *mtd; - struct map_info map; + int nr_subdev; + struct armflash_subdev_info subdev[0]; }; static void armflash_set_vpp(struct map_info *map, int on) { - struct armflash_info *info = container_of(map, struct armflash_info, map); + struct armflash_subdev_info *info = + container_of(map, struct armflash_subdev_info, map); if (info->plat && info->plat->set_vpp) info->plat->set_vpp(on); @@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on) static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; -static int armflash_probe(struct platform_device *dev) +static int armflash_subdev_probe(struct armflash_subdev_info *subdev, + struct resource *res) { - struct flash_platform_data *plat = dev->dev.platform_data; - struct resource *res = dev->resource; - unsigned int size = res->end - res->start + 1; - struct armflash_info *info; - int err; + struct flash_platform_data *plat = subdev->plat; + resource_size_t size = res->end - res->start + 1; void __iomem *base; + int err = 0; - info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); - if (!info) { - err = -ENOMEM; - goto out; - } - - info->plat = plat; - if (plat && plat->init) { - err = plat->init(); - if (err) - goto no_resource; - } - - info->res = request_mem_region(res->start, size, "armflash"); - if (!info->res) { + if (!request_mem_region(res->start, size, subdev->name)) { err = -EBUSY; - goto no_resource; + goto out; } base = ioremap(res->start, size); @@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev) /* * look for CFI based flash parts fitted to this board */ - info->map.size = size; - info->map.bankwidth = plat->width; - info->map.phys = res->start; - info->map.virt = base; - info->map.name = dev_name(&dev->dev); - info->map.set_vpp = armflash_set_vpp; + subdev->map.size = size; + subdev->map.bankwidth = plat->width; + subdev->map.phys = res->start; + subdev->map.virt = base; + subdev->map.name = subdev->name; + subdev->map.set_vpp = armflash_set_vpp; - simple_map_init(&info->map); + simple_map_init(&subdev->map); /* * Also, the CFI layer automatically works out what size * of chips we have, and does the necessary identification * for us automatically. */ - info->mtd = do_map_probe(plat->map_name, &info->map); - if (!info->mtd) { + subdev->mtd = do_map_probe(plat->map_name, &subdev->map); + if (!subdev->mtd) { err = -ENXIO; goto no_device; } - info->mtd->owner = THIS_MODULE; + subdev->mtd->owner = THIS_MODULE; + + /* Successful? */ + if (err == 0) + return err; + + if (subdev->mtd) + map_destroy(subdev->mtd); + no_device: + iounmap(base); + no_mem: + release_mem_region(res->start, size); + out: + return err; +} + +static void armflash_subdev_remove(struct armflash_subdev_info *subdev) +{ + if (subdev->mtd) + map_destroy(subdev->mtd); + if (subdev->map.virt) + iounmap(subdev->map.virt); + release_mem_region(subdev->map.phys, subdev->map.size); +} + +static int armflash_probe(struct platform_device *dev) +{ + struct flash_platform_data *plat = dev->dev.platform_data; + unsigned int size; + struct armflash_info *info; + int i, nr, err; + + /* Count the number of devices */ + for (nr = 0; ; nr++) + if (!platform_get_resource(dev, IORESOURCE_MEM, nr)) + break; + if (nr == 0) { + err = -ENODEV; + goto out; + } + + size = sizeof(struct armflash_info) + + sizeof(struct armflash_subdev_info) * nr; + info = kzalloc(size, GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto out; + } + + if (plat && plat->init) { + err = plat->init(); + if (err) + goto no_resource; + } + + for (i = 0; i < nr; i++) { + struct armflash_subdev_info *subdev = &info->subdev[i]; + struct resource *res; + + res = platform_get_resource(dev, IORESOURCE_MEM, i); + if (!res) + break; + + if (nr == 1) + /* No MTD concatenation, just use the default name */ + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s", + dev_name(&dev->dev)); + else + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d", + dev_name(&dev->dev), i); + subdev->plat = plat; + + err = armflash_subdev_probe(subdev, res); + if (err) + break; + } + info->nr_subdev = i; + + if (err) + goto subdev_err; + + if (info->nr_subdev == 1) + info->mtd = info->subdev[0].mtd; + else if (info->nr_subdev > 1) { +#ifdef CONFIG_MTD_CONCAT + struct mtd_info *cdev[info->nr_subdev]; + + /* + * We detected multiple devices. Concatenate them together. + */ + for (i = 0; i < info->nr_subdev; i++) + cdev[i] = info->subdev[i].mtd; + + info->mtd = mtd_concat_create(cdev, info->nr_subdev, + dev_name(&dev->dev)); + if (info->mtd == NULL) + err = -ENXIO; +#else + printk(KERN_ERR "armflash: multiple devices found but " + "MTD concat support disabled.\n"); + err = -ENXIO; +#endif + } + + if (err < 0) + goto cleanup; err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); if (err > 0) { @@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev) "mtd partition registration failed: %d\n", err); } - if (err == 0) + if (err == 0) { platform_set_drvdata(dev, info); + return err; + } /* - * If we got an error, free all resources. + * We got an error, free all resources. */ - if (err < 0) { - if (info->mtd) { - del_mtd_partitions(info->mtd); - map_destroy(info->mtd); - } - kfree(info->parts); - - no_device: - iounmap(base); - no_mem: - release_mem_region(res->start, size); - no_resource: - if (plat && plat->exit) - plat->exit(); - kfree(info); + cleanup: + if (info->mtd) { + del_mtd_partitions(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } + kfree(info->parts); + subdev_err: + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); + no_resource: + if (plat && plat->exit) + plat->exit(); + kfree(info); out: return err; } @@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev) static int armflash_remove(struct platform_device *dev) { struct armflash_info *info = platform_get_drvdata(dev); + struct flash_platform_data *plat = dev->dev.platform_data; + int i; platform_set_drvdata(dev, NULL); if (info) { if (info->mtd) { del_mtd_partitions(info->mtd); - map_destroy(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } kfree(info->parts); - iounmap(info->map.virt); - release_resource(info->res); - kfree(info->res); + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); - if (info->plat && info->plat->exit) - info->plat->exit(); + if (plat && plat->exit) + plat->exit(); kfree(info); } diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 29a90115735..380648e9051 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -195,42 +195,6 @@ err_out: } #ifdef CONFIG_PM -static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state) -{ - struct physmap_flash_info *info = platform_get_drvdata(dev); - int ret = 0; - int i; - - for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - if (info->mtd[i]->suspend) { - ret = info->mtd[i]->suspend(info->mtd[i]); - if (ret) - goto fail; - } - - return 0; -fail: - for (--i; i >= 0; --i) - if (info->mtd[i]->suspend) { - BUG_ON(!info->mtd[i]->resume); - info->mtd[i]->resume(info->mtd[i]); - } - - return ret; -} - -static int physmap_flash_resume(struct platform_device *dev) -{ - struct physmap_flash_info *info = platform_get_drvdata(dev); - int i; - - for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - if (info->mtd[i]->resume) - info->mtd[i]->resume(info->mtd[i]); - - return 0; -} - static void physmap_flash_shutdown(struct platform_device *dev) { struct physmap_flash_info *info = platform_get_drvdata(dev); @@ -242,16 +206,12 @@ static void physmap_flash_shutdown(struct platform_device *dev) info->mtd[i]->resume(info->mtd[i]); } #else -#define physmap_flash_suspend NULL -#define physmap_flash_resume NULL #define physmap_flash_shutdown NULL #endif static struct platform_driver physmap_flash_driver = { .probe = physmap_flash_probe, .remove = physmap_flash_remove, - .suspend = physmap_flash_suspend, - .resume = physmap_flash_resume, .shutdown = physmap_flash_shutdown, .driver = { .name = "physmap-flash", diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index c83a60fada5..39d357b2eb4 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -20,16 +20,23 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> #include <linux/of.h> #include <linux/of_platform.h> +struct of_flash_list { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; +}; + struct of_flash { - struct mtd_info *mtd; - struct map_info map; - struct resource *res; + struct mtd_info *cmtd; #ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; #endif + int list_size; /* number of elements in of_flash_list */ + struct of_flash_list list[0]; }; #ifdef CONFIG_MTD_PARTITIONS @@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev, static int of_flash_remove(struct of_device *dev) { struct of_flash *info; + int i; info = dev_get_drvdata(&dev->dev); if (!info) return 0; dev_set_drvdata(&dev->dev, NULL); - if (info->mtd) { +#ifdef CONFIG_MTD_CONCAT + if (info->cmtd != info->list[0].mtd) { + del_mtd_device(info->cmtd); + mtd_concat_destroy(info->cmtd); + } +#endif + + if (info->cmtd) { if (OF_FLASH_PARTS(info)) { - del_mtd_partitions(info->mtd); + del_mtd_partitions(info->cmtd); kfree(OF_FLASH_PARTS(info)); } else { - del_mtd_device(info->mtd); + del_mtd_device(info->cmtd); } - map_destroy(info->mtd); } - if (info->map.virt) - iounmap(info->map.virt); + for (i = 0; i < info->list_size; i++) { + if (info->list[i].mtd) + map_destroy(info->list[i].mtd); - if (info->res) { - release_resource(info->res); - kfree(info->res); + if (info->list[i].map.virt) + iounmap(info->list[i].map.virt); + + if (info->list[i].res) { + release_resource(info->list[i].res); + kfree(info->list[i].res); + } } + kfree(info); + return 0; } @@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev, const char *probe_type = match->data; const u32 *width; int err; - - err = -ENXIO; - if (of_address_to_resource(dp, 0, &res)) { - dev_err(&dev->dev, "Can't get IO address from device tree\n"); + int i; + int count; + const u32 *p; + int reg_tuple_size; + struct mtd_info **mtd_list = NULL; + + reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); + + /* + * Get number of "reg" tuples. Scan for MTD devices on area's + * described by each "reg" region. This makes it possible (including + * the concat support) to support the Intel P30 48F4400 chips which + * consists internally of 2 non-identical NOR chips on one die. + */ + p = of_get_property(dp, "reg", &count); + if (count % reg_tuple_size != 0) { + dev_err(&dev->dev, "Malformed reg property on %s\n", + dev->node->full_name); + err = -EINVAL; goto err_out; } - - dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", - (unsigned long long)res.start, (unsigned long long)res.end); + count /= reg_tuple_size; err = -ENOMEM; - info = kzalloc(sizeof(*info), GFP_KERNEL); + 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; dev_set_drvdata(&dev->dev, info); - err = -EBUSY; - info->res = request_mem_region(res.start, res.end - res.start + 1, - dev_name(&dev->dev)); - if (!info->res) - goto err_out; + for (i = 0; i < count; i++) { + err = -ENXIO; + if (of_address_to_resource(dp, i, &res)) { + dev_err(&dev->dev, "Can't get IO address from device" + " tree\n"); + goto err_out; + } - err = -ENXIO; - width = of_get_property(dp, "bank-width", NULL); - if (!width) { - dev_err(&dev->dev, "Can't get bank width from device tree\n"); - goto err_out; - } + dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", + (unsigned long long)res.start, + (unsigned long long)res.end); + + err = -EBUSY; + info->list[i].res = request_mem_region(res.start, res.end - + res.start + 1, + dev_name(&dev->dev)); + if (!info->list[i].res) + goto err_out; + + err = -ENXIO; + width = of_get_property(dp, "bank-width", NULL); + if (!width) { + dev_err(&dev->dev, "Can't get bank width from device" + " tree\n"); + goto err_out; + } - info->map.name = dev_name(&dev->dev); - info->map.phys = res.start; - info->map.size = res.end - res.start + 1; - info->map.bankwidth = *width; + 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.bankwidth = *width; + + err = -ENOMEM; + info->list[i].map.virt = ioremap(info->list[i].map.phys, + info->list[i].map.size); + if (!info->list[i].map.virt) { + dev_err(&dev->dev, "Failed to ioremap() flash" + " region\n"); + goto err_out; + } - err = -ENOMEM; - info->map.virt = ioremap(info->map.phys, info->map.size); - if (!info->map.virt) { - dev_err(&dev->dev, "Failed to ioremap() flash region\n"); - goto err_out; - } + simple_map_init(&info->list[i].map); - simple_map_init(&info->map); + if (probe_type) { + info->list[i].mtd = do_map_probe(probe_type, + &info->list[i].map); + } else { + info->list[i].mtd = obsolete_probe(dev, + &info->list[i].map); + } + mtd_list[i] = info->list[i].mtd; - if (probe_type) - info->mtd = do_map_probe(probe_type, &info->map); - else - info->mtd = obsolete_probe(dev, &info->map); + err = -ENXIO; + if (!info->list[i].mtd) { + dev_err(&dev->dev, "do_map_probe() failed\n"); + goto err_out; + } else { + info->list_size++; + } + info->list[i].mtd->owner = THIS_MODULE; + info->list[i].mtd->dev.parent = &dev->dev; + } - err = -ENXIO; - if (!info->mtd) { - dev_err(&dev->dev, "do_map_probe() failed\n"); - goto err_out; + err = 0; + if (info->list_size == 1) { + info->cmtd = info->list[0].mtd; + } else if (info->list_size > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ +#ifdef CONFIG_MTD_CONCAT + info->cmtd = mtd_concat_create(mtd_list, info->list_size, + dev_name(&dev->dev)); + if (info->cmtd == NULL) + err = -ENXIO; +#else + printk(KERN_ERR "physmap_of: multiple devices " + "found but MTD concat support disabled.\n"); + err = -ENXIO; +#endif } - info->mtd->owner = THIS_MODULE; - info->mtd->dev.parent = &dev->dev; + if (err) + goto err_out; #ifdef CONFIG_MTD_PARTITIONS /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ - err = parse_mtd_partitions(info->mtd, part_probe_types, - &info->parts, 0); + err = parse_mtd_partitions(info->cmtd, part_probe_types, + &info->parts, 0); if (err < 0) return err; @@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev, } if (err > 0) - add_mtd_partitions(info->mtd, info->parts, err); + add_mtd_partitions(info->cmtd, info->parts, err); else #endif - add_mtd_device(info->mtd); + add_mtd_device(info->cmtd); + + kfree(mtd_list); return 0; err_out: + kfree(mtd_list); of_flash_remove(dev); + return err; } diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c deleted file mode 100644 index 30de5c0c09a..00000000000 --- a/drivers/mtd/maps/pmcmsp-ramroot.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Mapping of the rootfs in a physical region of memory - * - * Copyright (C) 2005-2007 PMC-Sierra Inc. - * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.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 SOFTWARE IS PROVIDED ``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 AUTHOR 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. - * - * 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/types.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/fs.h> -#include <linux/root_dev.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> - -#include <asm/io.h> - -#include <msp_prom.h> - -static struct mtd_info *rr_mtd; - -struct map_info rr_map = { - .name = "ramroot", - .bankwidth = 4, -}; - -static int __init init_rrmap(void) -{ - void *ramroot_start; - unsigned long ramroot_size; - - /* Check for supported rootfs types */ - if (get_ramroot(&ramroot_start, &ramroot_size)) { - rr_map.phys = CPHYSADDR(ramroot_start); - rr_map.size = ramroot_size; - - printk(KERN_NOTICE - "PMC embedded root device: 0x%08lx @ 0x%08lx\n", - rr_map.size, (unsigned long)rr_map.phys); - } else { - printk(KERN_ERR - "init_rrmap: no supported embedded rootfs detected!\n"); - return -ENXIO; - } - - /* Map rootfs to I/O space for block device driver */ - rr_map.virt = ioremap(rr_map.phys, rr_map.size); - if (!rr_map.virt) { - printk(KERN_ERR "Failed to ioremap\n"); - return -EIO; - } - - simple_map_init(&rr_map); - - rr_mtd = do_map_probe("map_ram", &rr_map); - if (rr_mtd) { - rr_mtd->owner = THIS_MODULE; - - add_mtd_device(rr_mtd); - - return 0; - } - - iounmap(rr_map.virt); - return -ENXIO; -} - -static void __exit cleanup_rrmap(void) -{ - del_mtd_device(rr_mtd); - map_destroy(rr_mtd); - - iounmap(rr_map.virt); - rr_map.virt = NULL; -} - -MODULE_AUTHOR("PMC-Sierra, Inc"); -MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem"); -MODULE_LICENSE("GPL"); - -module_init(init_rrmap); -module_exit(cleanup_rrmap); diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 572d32fdf38..643aa06b599 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -140,24 +140,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static int pxa2xx_flash_suspend(struct platform_device *dev, pm_message_t state) -{ - struct pxa2xx_flash_info *info = platform_get_drvdata(dev); - int ret = 0; - - if (info->mtd && info->mtd->suspend) - ret = info->mtd->suspend(info->mtd); - return ret; -} - -static int pxa2xx_flash_resume(struct platform_device *dev) -{ - struct pxa2xx_flash_info *info = platform_get_drvdata(dev); - - if (info->mtd && info->mtd->resume) - info->mtd->resume(info->mtd); - return 0; -} static void pxa2xx_flash_shutdown(struct platform_device *dev) { struct pxa2xx_flash_info *info = platform_get_drvdata(dev); @@ -166,8 +148,6 @@ static void pxa2xx_flash_shutdown(struct platform_device *dev) info->mtd->resume(info->mtd); } #else -#define pxa2xx_flash_suspend NULL -#define pxa2xx_flash_resume NULL #define pxa2xx_flash_shutdown NULL #endif @@ -178,8 +158,6 @@ static struct platform_driver pxa2xx_flash_driver = { }, .probe = pxa2xx_flash_probe, .remove = __devexit_p(pxa2xx_flash_remove), - .suspend = pxa2xx_flash_suspend, - .resume = pxa2xx_flash_resume, .shutdown = pxa2xx_flash_shutdown, }; diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index d39f0adac84..83ed64512c5 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -145,25 +145,6 @@ err_out: } #ifdef CONFIG_PM -static int rbtx4939_flash_suspend(struct platform_device *dev, - pm_message_t state) -{ - struct rbtx4939_flash_info *info = platform_get_drvdata(dev); - - if (info->mtd->suspend) - return info->mtd->suspend(info->mtd); - return 0; -} - -static int rbtx4939_flash_resume(struct platform_device *dev) -{ - struct rbtx4939_flash_info *info = platform_get_drvdata(dev); - - if (info->mtd->resume) - info->mtd->resume(info->mtd); - return 0; -} - static void rbtx4939_flash_shutdown(struct platform_device *dev) { struct rbtx4939_flash_info *info = platform_get_drvdata(dev); @@ -173,16 +154,12 @@ static void rbtx4939_flash_shutdown(struct platform_device *dev) info->mtd->resume(info->mtd); } #else -#define rbtx4939_flash_suspend NULL -#define rbtx4939_flash_resume NULL #define rbtx4939_flash_shutdown NULL #endif static struct platform_driver rbtx4939_flash_driver = { .probe = rbtx4939_flash_probe, .remove = rbtx4939_flash_remove, - .suspend = rbtx4939_flash_suspend, - .resume = rbtx4939_flash_resume, .shutdown = rbtx4939_flash_shutdown, .driver = { .name = "rbtx4939-flash", diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 05e9362dc7f..c6210f5118d 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -415,25 +415,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state) -{ - struct sa_info *info = platform_get_drvdata(dev); - int ret = 0; - - if (info) - ret = info->mtd->suspend(info->mtd); - - return ret; -} - -static int sa1100_mtd_resume(struct platform_device *dev) -{ - struct sa_info *info = platform_get_drvdata(dev); - if (info) - info->mtd->resume(info->mtd); - return 0; -} - static void sa1100_mtd_shutdown(struct platform_device *dev) { struct sa_info *info = platform_get_drvdata(dev); @@ -441,16 +422,12 @@ static void sa1100_mtd_shutdown(struct platform_device *dev) info->mtd->resume(info->mtd); } #else -#define sa1100_mtd_suspend NULL -#define sa1100_mtd_resume NULL #define sa1100_mtd_shutdown NULL #endif static struct platform_driver sa1100_mtd_driver = { .probe = sa1100_mtd_probe, .remove = __exit_p(sa1100_mtd_remove), - .suspend = sa1100_mtd_suspend, - .resume = sa1100_mtd_resume, .shutdown = sa1100_mtd_shutdown, .driver = { .name = "sa1100-mtd", diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 81756e39771..d4314fb8821 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -22,15 +22,19 @@ /****************************************************************************/ +extern char _ebss; + struct map_info uclinux_ram_map = { .name = "RAM", + .phys = (unsigned long)&_ebss, + .size = 0, }; -struct mtd_info *uclinux_ram_mtdinfo; +static struct mtd_info *uclinux_ram_mtdinfo; /****************************************************************************/ -struct mtd_partition uclinux_romfs[] = { +static struct mtd_partition uclinux_romfs[] = { { .name = "ROMfs" } }; @@ -38,7 +42,7 @@ struct mtd_partition uclinux_romfs[] = { /****************************************************************************/ -int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, +static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys) { struct map_info *map = mtd->priv; @@ -55,12 +59,10 @@ static int __init uclinux_mtd_init(void) { struct mtd_info *mtd; struct map_info *mapp; - extern char _ebss; - unsigned long addr = (unsigned long) &_ebss; mapp = &uclinux_ram_map; - mapp->phys = addr; - mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(addr + 8)))); + if (!mapp->size) + mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); mapp->bankwidth = 4; printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index aaac3b6800b..c3f62654b6d 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -291,7 +291,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) gd->private_data = new; new->blkcore_priv = gd; gd->queue = tr->blkcore_priv->rq; - gd->driverfs_dev = new->mtd->dev.parent; + gd->driverfs_dev = &new->mtd->dev; if (new->readonly) set_disk_ro(gd, 1); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 763d3f0a1f4..5b081cb8435 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -14,6 +14,7 @@ #include <linux/sched.h> #include <linux/smp_lock.h> #include <linux/backing-dev.h> +#include <linux/compat.h> #include <linux/mtd/mtd.h> #include <linux/mtd/compatmac.h> @@ -355,6 +356,100 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode) # define otp_select_filemode(f,m) -EOPNOTSUPP #endif +static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, + uint64_t start, uint32_t length, void __user *ptr, + uint32_t __user *retp) +{ + struct mtd_oob_ops ops; + uint32_t retlen; + int ret = 0; + + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + + if (length > 4096) + return -EINVAL; + + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT; + + if (ret) + return ret; + + ops.ooblen = length; + ops.ooboffs = start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(length, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + if (copy_from_user(ops.oobbuf, ptr, length)) { + kfree(ops.oobbuf); + return -EFAULT; + } + + start &= ~((uint64_t)mtd->oobsize - 1); + ret = mtd->write_oob(mtd, start, &ops); + + if (ops.oobretlen > 0xFFFFFFFFU) + ret = -EOVERFLOW; + retlen = ops.oobretlen; + if (copy_to_user(retp, &retlen, sizeof(length))) + ret = -EFAULT; + + kfree(ops.oobbuf); + return ret; +} + +static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start, + uint32_t length, void __user *ptr, uint32_t __user *retp) +{ + struct mtd_oob_ops ops; + int ret = 0; + + if (length > 4096) + return -EINVAL; + + if (!mtd->read_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_WRITE, ptr, + length) ? 0 : -EFAULT; + if (ret) + return ret; + + ops.ooblen = length; + ops.ooboffs = start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(length, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + start &= ~((uint64_t)mtd->oobsize - 1); + ret = mtd->read_oob(mtd, start, &ops); + + if (put_user(ops.oobretlen, retp)) + ret = -EFAULT; + else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf, + ops.oobretlen)) + ret = -EFAULT; + + kfree(ops.oobbuf); + return ret; +} + static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { @@ -417,6 +512,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; case MEMERASE: + case MEMERASE64: { struct erase_info *erase; @@ -427,20 +523,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (!erase) ret = -ENOMEM; else { - struct erase_info_user einfo; - wait_queue_head_t waitq; DECLARE_WAITQUEUE(wait, current); init_waitqueue_head(&waitq); - if (copy_from_user(&einfo, argp, - sizeof(struct erase_info_user))) { - kfree(erase); - return -EFAULT; + if (cmd == MEMERASE64) { + struct erase_info_user64 einfo64; + + if (copy_from_user(&einfo64, argp, + sizeof(struct erase_info_user64))) { + kfree(erase); + return -EFAULT; + } + erase->addr = einfo64.start; + erase->len = einfo64.length; + } else { + struct erase_info_user einfo32; + + if (copy_from_user(&einfo32, argp, + sizeof(struct erase_info_user))) { + kfree(erase); + return -EFAULT; + } + erase->addr = einfo32.start; + erase->len = einfo32.length; } - erase->addr = einfo.start; - erase->len = einfo.length; erase->mtd = mtd; erase->callback = mtdchar_erase_callback; erase->priv = (unsigned long)&waitq; @@ -474,100 +582,56 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMWRITEOOB: { struct mtd_oob_buf buf; - struct mtd_oob_ops ops; - struct mtd_oob_buf __user *user_buf = argp; - uint32_t retlen; - - if(!(file->f_mode & FMODE_WRITE)) - return -EPERM; - - if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) - return -EFAULT; - - if (buf.length > 4096) - return -EINVAL; - - if (!mtd->write_oob) - ret = -EOPNOTSUPP; - else - ret = access_ok(VERIFY_READ, buf.ptr, - buf.length) ? 0 : EFAULT; - - if (ret) - return ret; - - ops.ooblen = buf.length; - ops.ooboffs = buf.start & (mtd->oobsize - 1); - ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; - - if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) - return -EINVAL; - - ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); - if (!ops.oobbuf) - return -ENOMEM; - - if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { - kfree(ops.oobbuf); - return -EFAULT; - } + struct mtd_oob_buf __user *buf_user = argp; - buf.start &= ~(mtd->oobsize - 1); - ret = mtd->write_oob(mtd, buf.start, &ops); - - if (ops.oobretlen > 0xFFFFFFFFU) - ret = -EOVERFLOW; - retlen = ops.oobretlen; - if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length))) + /* NOTE: writes return length to buf_user->length */ + if (copy_from_user(&buf, argp, sizeof(buf))) ret = -EFAULT; - - kfree(ops.oobbuf); + else + ret = mtd_do_writeoob(file, mtd, buf.start, buf.length, + buf.ptr, &buf_user->length); break; - } case MEMREADOOB: { struct mtd_oob_buf buf; - struct mtd_oob_ops ops; - - if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) - return -EFAULT; - - if (buf.length > 4096) - return -EINVAL; + struct mtd_oob_buf __user *buf_user = argp; - if (!mtd->read_oob) - ret = -EOPNOTSUPP; + /* NOTE: writes return length to buf_user->start */ + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; else - ret = access_ok(VERIFY_WRITE, buf.ptr, - buf.length) ? 0 : -EFAULT; - if (ret) - return ret; - - ops.ooblen = buf.length; - ops.ooboffs = buf.start & (mtd->oobsize - 1); - ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; + ret = mtd_do_readoob(mtd, buf.start, buf.length, + buf.ptr, &buf_user->start); + break; + } - if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) - return -EINVAL; + case MEMWRITEOOB64: + { + struct mtd_oob_buf64 buf; + struct mtd_oob_buf64 __user *buf_user = argp; - ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); - if (!ops.oobbuf) - return -ENOMEM; + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; + else + ret = mtd_do_writeoob(file, mtd, buf.start, buf.length, + (void __user *)(uintptr_t)buf.usr_ptr, + &buf_user->length); + break; + } - buf.start &= ~(mtd->oobsize - 1); - ret = mtd->read_oob(mtd, buf.start, &ops); + case MEMREADOOB64: + { + struct mtd_oob_buf64 buf; + struct mtd_oob_buf64 __user *buf_user = argp; - if (put_user(ops.oobretlen, (uint32_t __user *)argp)) - ret = -EFAULT; - else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, - ops.oobretlen)) + if (copy_from_user(&buf, argp, sizeof(buf))) ret = -EFAULT; - - kfree(ops.oobbuf); + else + ret = mtd_do_readoob(mtd, buf.start, buf.length, + (void __user *)(uintptr_t)buf.usr_ptr, + &buf_user->length); break; } @@ -758,6 +822,68 @@ static int mtd_ioctl(struct inode *inode, struct file *file, return ret; } /* memory_ioctl */ +#ifdef CONFIG_COMPAT + +struct mtd_oob_buf32 { + u_int32_t start; + u_int32_t length; + compat_caddr_t ptr; /* unsigned char* */ +}; + +#define MEMWRITEOOB32 _IOWR('M', 3, struct mtd_oob_buf32) +#define MEMREADOOB32 _IOWR('M', 4, struct mtd_oob_buf32) + +static long mtd_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + void __user *argp = compat_ptr(arg); + int ret = 0; + + lock_kernel(); + + switch (cmd) { + case MEMWRITEOOB32: + { + struct mtd_oob_buf32 buf; + struct mtd_oob_buf32 __user *buf_user = argp; + + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; + else + ret = mtd_do_writeoob(file, mtd, buf.start, + buf.length, compat_ptr(buf.ptr), + &buf_user->length); + break; + } + + case MEMREADOOB32: + { + struct mtd_oob_buf32 buf; + struct mtd_oob_buf32 __user *buf_user = argp; + + /* NOTE: writes return length to buf->start */ + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; + else + ret = mtd_do_readoob(mtd, buf.start, + buf.length, compat_ptr(buf.ptr), + &buf_user->start); + break; + } + default: + ret = mtd_ioctl(inode, file, cmd, (unsigned long)argp); + } + + unlock_kernel(); + + return ret; +} + +#endif /* CONFIG_COMPAT */ + /* * try to determine where a shared mapping can be made * - only supported for NOMMU at the moment (MMU can't doesn't copy private @@ -817,6 +943,9 @@ static const struct file_operations mtd_fops = { .read = mtd_read, .write = mtd_write, .ioctl = mtd_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mtd_compat_ioctl, +#endif .open = mtd_open, .release = mtd_close, .mmap = mtd_mmap, diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bccb4b1ffc4..fac54a3fa3f 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -23,8 +23,15 @@ #include "mtdcore.h" - -static struct class *mtd_class; +static int mtd_cls_suspend(struct device *dev, pm_message_t state); +static int mtd_cls_resume(struct device *dev); + +static struct class mtd_class = { + .name = "mtd", + .owner = THIS_MODULE, + .suspend = mtd_cls_suspend, + .resume = mtd_cls_resume, +}; /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ @@ -52,7 +59,26 @@ static void mtd_release(struct device *dev) /* remove /dev/mtdXro node if needed */ if (index) - device_destroy(mtd_class, index + 1); + device_destroy(&mtd_class, index + 1); +} + +static int mtd_cls_suspend(struct device *dev, pm_message_t state) +{ + struct mtd_info *mtd = dev_to_mtd(dev); + + if (mtd->suspend) + return mtd->suspend(mtd); + else + return 0; +} + +static int mtd_cls_resume(struct device *dev) +{ + struct mtd_info *mtd = dev_to_mtd(dev); + + if (mtd->resume) + mtd->resume(mtd); + return 0; } static ssize_t mtd_type_show(struct device *dev, @@ -269,7 +295,7 @@ int add_mtd_device(struct mtd_info *mtd) * physical device. */ mtd->dev.type = &mtd_devtype; - mtd->dev.class = mtd_class; + mtd->dev.class = &mtd_class; mtd->dev.devt = MTD_DEVT(i); dev_set_name(&mtd->dev, "mtd%d", i); if (device_register(&mtd->dev) != 0) { @@ -278,7 +304,7 @@ int add_mtd_device(struct mtd_info *mtd) } if (MTD_DEVT(i)) - device_create(mtd_class, mtd->dev.parent, + device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL, "mtd%dro", i); @@ -604,11 +630,12 @@ done: static int __init init_mtd(void) { - mtd_class = class_create(THIS_MODULE, "mtd"); + int ret; + ret = class_register(&mtd_class); - if (IS_ERR(mtd_class)) { - pr_err("Error creating mtd class.\n"); - return PTR_ERR(mtd_class); + if (ret) { + pr_err("Error registering mtd class: %d\n", ret); + return ret; } #ifdef CONFIG_PROC_FS if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) @@ -623,7 +650,7 @@ static void __exit cleanup_mtd(void) if (proc_mtd) remove_proc_entry( "mtd", NULL); #endif /* CONFIG_PROC_FS */ - class_destroy(mtd_class); + class_unregister(&mtd_class); } module_init(init_mtd); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 29675edb44b..349fcbe5cc0 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -27,9 +27,7 @@ struct mtd_part { struct mtd_info mtd; struct mtd_info *master; uint64_t offset; - int index; struct list_head list; - int registered; }; /* @@ -321,8 +319,7 @@ int del_mtd_partitions(struct mtd_info *master) list_for_each_entry_safe(slave, next, &mtd_partitions, list) if (slave->master == master) { list_del(&slave->list); - if (slave->registered) - del_mtd_device(&slave->mtd); + del_mtd_device(&slave->mtd); kfree(slave); } @@ -395,7 +392,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd.get_fact_prot_info = part_get_fact_prot_info; if (master->sync) slave->mtd.sync = part_sync; - if (!partno && master->suspend && master->resume) { + if (!partno && !master->dev.class && master->suspend && master->resume) { slave->mtd.suspend = part_suspend; slave->mtd.resume = part_resume; } @@ -412,7 +409,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd.erase = part_erase; slave->master = master; slave->offset = part->offset; - slave->index = partno; if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; @@ -500,15 +496,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, } out_register: - if (part->mtdp) { - /* store the object pointer (caller may or may not register it*/ - *part->mtdp = &slave->mtd; - slave->registered = 0; - } else { - /* register our partition */ - add_mtd_device(&slave->mtd); - slave->registered = 1; - } + /* register our partition */ + add_mtd_device(&slave->mtd); + return slave; } diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index f3276897859..ce96c091f01 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA help Support for NAND flash on Amstrad E3 (Delta). +config MTD_NAND_OMAP2 + tristate "NAND Flash device on OMAP2 and OMAP3" + depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3) + help + Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. + config MTD_NAND_TS7250 tristate "NAND Flash device on TS-7250 board" depends on MACH_TS72XX @@ -139,27 +145,27 @@ config MTD_NAND_PPCHAMELEONEVB This enables the NAND flash driver on the PPChameleon EVB Board. config MTD_NAND_S3C2410 - tristate "NAND Flash support for S3C2410/S3C2440 SoC" - depends on ARCH_S3C2410 + tristate "NAND Flash support for Samsung S3C SoCs" + depends on ARCH_S3C2410 || ARCH_S3C64XX help - This enables the NAND flash controller on the S3C2410 and S3C2440 + This enables the NAND flash controller on the S3C24xx and S3C64xx SoCs No board specific support is done by this driver, each board must advertise a platform_device for the driver to attach. config MTD_NAND_S3C2410_DEBUG - bool "S3C2410 NAND driver debug" + bool "Samsung S3C NAND driver debug" depends on MTD_NAND_S3C2410 help - Enable debugging of the S3C2410 NAND driver + Enable debugging of the S3C NAND driver config MTD_NAND_S3C2410_HWECC - bool "S3C2410 NAND Hardware ECC" + bool "Samsung S3C NAND Hardware ECC" depends on MTD_NAND_S3C2410 help - Enable the use of the S3C2410's internal ECC generator when - using NAND. Early versions of the chip have had problems with + Enable the use of the controller's internal ECC generator when + using NAND. Early versions of the chips have had problems with incorrect ECC generation, and if using these, the default of software ECC is preferable. @@ -171,7 +177,7 @@ config MTD_NAND_NDFC NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs config MTD_NAND_S3C2410_CLKSTOP - bool "S3C2410 NAND IDLE clock stop" + bool "Samsung S3C NAND IDLE clock stop" depends on MTD_NAND_S3C2410 default n help diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index d33860ac42c..f3a786b3cff 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o +obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 47a33cec379..2802992b39d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -24,6 +24,7 @@ #include <linux/slab.h> #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/platform_device.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> @@ -47,6 +48,9 @@ #define no_ecc 0 #endif +static int on_flash_bbt = 0; +module_param(on_flash_bbt, int, 0); + /* Register access macros */ #define ecc_readl(add, reg) \ __raw_readl(add + ATMEL_ECC_##reg) @@ -459,12 +463,17 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (host->board->det_pin) { if (gpio_get_value(host->board->det_pin)) { - printk("No SmartMedia card inserted.\n"); + printk(KERN_INFO "No SmartMedia card inserted.\n"); res = ENXIO; goto err_no_card; } } + if (on_flash_bbt) { + printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); + nand_chip->options |= NAND_USE_FLASH_BBT; + } + /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { res = -ENXIO; diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 4c2a67ca801..8506e7e606f 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int bf5xx_nand_dma_rw(struct mtd_info *mtd, +static void bf5xx_nand_dma_rw(struct mtd_info *mtd, uint8_t *buf, int is_read) { struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); @@ -496,11 +496,20 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd, /* setup DMA register with Blackfin DMA API */ set_dma_config(CH_NFC, 0x0); set_dma_start_addr(CH_NFC, (unsigned long) buf); + +/* The DMAs have different size on BF52x and BF54x */ +#ifdef CONFIG_BF52x + set_dma_x_count(CH_NFC, (page_size >> 1)); + set_dma_x_modify(CH_NFC, 2); + val = DI_EN | WDSIZE_16; +#endif + +#ifdef CONFIG_BF54x set_dma_x_count(CH_NFC, (page_size >> 2)); set_dma_x_modify(CH_NFC, 4); - - /* setup write or read operation */ val = DI_EN | WDSIZE_32; +#endif + /* setup write or read operation */ if (is_read) val |= WNR; set_dma_config(CH_NFC, val); @@ -512,8 +521,6 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd, else bfin_write_NFC_PGCTL(0x2); wait_for_completion(&info->dma_completion); - - return 0; } static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 02700f769b8..0fad6487e6f 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -44,7 +44,7 @@ * and some flavors of secondary chipselect (e.g. based on A12) as used * with multichip packages. * - * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC + * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC * available on chips like the DM355 and OMAP-L137 and needed with the * more error-prone MLC NAND chips. * @@ -54,11 +54,14 @@ struct davinci_nand_info { struct mtd_info mtd; struct nand_chip chip; + struct nand_ecclayout ecclayout; struct device *dev; struct clk *clk; bool partitioned; + bool is_readmode; + void __iomem *base; void __iomem *vaddr; @@ -73,6 +76,7 @@ struct davinci_nand_info { }; static DEFINE_SPINLOCK(davinci_nand_lock); +static bool ecc4_busy; #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) @@ -218,6 +222,192 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, /*----------------------------------------------------------------------*/ /* + * 4-bit hardware ECC ... context maintained over entire AEMIF + * + * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME + * since that forces use of a problematic "infix OOB" layout. + * Among other things, it trashes manufacturer bad block markers. + * Also, and specific to this hardware, it ECC-protects the "prepad" + * in the OOB ... while having ECC protection for parts of OOB would + * seem useful, the current MTD stack sometimes wants to update the + * OOB without recomputing ECC. + */ + +static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&davinci_nand_lock, flags); + + /* Start 4-bit ECC calculation for read/write */ + val = davinci_nand_readl(info, NANDFCR_OFFSET); + val &= ~(0x03 << 4); + val |= (info->core_chipsel << 4) | BIT(12); + davinci_nand_writel(info, NANDFCR_OFFSET, val); + + info->is_readmode = (mode == NAND_ECC_READ); + + spin_unlock_irqrestore(&davinci_nand_lock, flags); +} + +/* Read raw ECC code after writing to NAND. */ +static void +nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4]) +{ + const u32 mask = 0x03ff03ff; + + code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask; + code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask; + code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask; + code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask; +} + +/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */ +static int nand_davinci_calculate_4bit(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + u32 raw_ecc[4], *p; + unsigned i; + + /* After a read, terminate ECC calculation by a dummy read + * of some 4-bit ECC register. ECC covers everything that + * was read; correct() just uses the hardware state, so + * ecc_code is not needed. + */ + if (info->is_readmode) { + davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); + return 0; + } + + /* Pack eight raw 10-bit ecc values into ten bytes, making + * two passes which each convert four values (in upper and + * lower halves of two 32-bit words) into five bytes. The + * ROM boot loader uses this same packing scheme. + */ + nand_davinci_readecc_4bit(info, raw_ecc); + for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { + *ecc_code++ = p[0] & 0xff; + *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); + *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); + *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); + *ecc_code++ = (p[1] >> 18) & 0xff; + } + + return 0; +} + +/* Correct up to 4 bits in data we just read, using state left in the + * hardware plus the ecc_code computed when it was first written. + */ +static int nand_davinci_correct_4bit(struct mtd_info *mtd, + u_char *data, u_char *ecc_code, u_char *null) +{ + int i; + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned short ecc10[8]; + unsigned short *ecc16; + u32 syndrome[4]; + unsigned num_errors, corrected; + + /* All bytes 0xff? It's an erased page; ignore its ECC. */ + for (i = 0; i < 10; i++) { + if (ecc_code[i] != 0xff) + goto compare; + } + return 0; + +compare: + /* Unpack ten bytes into eight 10 bit values. We know we're + * little-endian, and use type punning for less shifting/masking. + */ + if (WARN_ON(0x01 & (unsigned) ecc_code)) + return -EINVAL; + ecc16 = (unsigned short *)ecc_code; + + ecc10[0] = (ecc16[0] >> 0) & 0x3ff; + ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0); + ecc10[2] = (ecc16[1] >> 4) & 0x3ff; + ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc); + ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300); + ecc10[5] = (ecc16[3] >> 2) & 0x3ff; + ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0); + ecc10[7] = (ecc16[4] >> 6) & 0x3ff; + + /* Tell ECC controller about the expected ECC codes. */ + for (i = 7; i >= 0; i--) + davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]); + + /* Allow time for syndrome calculation ... then read it. + * A syndrome of all zeroes 0 means no detected errors. + */ + davinci_nand_readl(info, NANDFSR_OFFSET); + nand_davinci_readecc_4bit(info, syndrome); + if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) + return 0; + + /* 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. + */ + davinci_nand_writel(info, NANDFCR_OFFSET, + davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); + for (;;) { + u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); + + switch ((fsr >> 8) & 0x0f) { + case 0: /* no error, should not happen */ + return 0; + case 1: /* five or more errors detected */ + return -EIO; + case 2: /* error addresses computed */ + case 3: + num_errors = 1 + ((fsr >> 16) & 0x03); + goto correct; + default: /* still working on it */ + cpu_relax(); + continue; + } + } + +correct: + /* correct each error */ + for (i = 0, corrected = 0; i < num_errors; i++) { + int error_address, error_value; + + if (i > 1) { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD2_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL2_OFFSET); + } else { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD1_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL1_OFFSET); + } + + if (i & 1) { + error_address >>= 16; + error_value >>= 16; + } + error_address &= 0x3ff; + error_address = (512 + 7) - error_address; + + if (error_address < 512) { + data[error_address] ^= error_value; + corrected++; + } + } + + return corrected; +} + +/*----------------------------------------------------------------------*/ + +/* * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's * how these chips are normally wired. This translates to both 8 and 16 * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4). @@ -294,6 +484,23 @@ static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info) /*----------------------------------------------------------------------*/ +/* An ECC layout for using 4-bit ECC with small-page flash, storing + * ten ECC bytes plus the manufacturer's bad block marker byte, and + * and not overlapping the default BBT markers. + */ +static struct nand_ecclayout hwecc4_small __initconst = { + .eccbytes = 10, + .eccpos = { 0, 1, 2, 3, 4, + /* offset 5 holds the badblock marker */ + 6, 7, + 13, 14, 15, }, + .oobfree = { + {.offset = 8, .length = 5, }, + {.offset = 16, }, + }, +}; + + static int __init nand_davinci_probe(struct platform_device *pdev) { struct davinci_nand_pdata *pdata = pdev->dev.platform_data; @@ -306,6 +513,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev) uint32_t val; nand_ecc_modes_t ecc_mode; + /* insist on board-specific configuration */ + if (!pdata) + return -ENODEV; + /* which external chipselect will we be managing? */ if (pdev->id < 0 || pdev->id > 3) return -ENODEV; @@ -351,7 +562,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.select_chip = nand_davinci_select_chip; /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ - info->chip.options = pdata ? pdata->options : 0; + info->chip.options = pdata->options; info->ioaddr = (uint32_t __force) vaddr; @@ -360,14 +571,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->mask_chipsel = pdata->mask_chipsel; /* use nandboot-capable ALE/CLE masks by default */ - if (pdata && pdata->mask_ale) - info->mask_ale = pdata->mask_cle; - else - info->mask_ale = MASK_ALE; - if (pdata && pdata->mask_cle) - info->mask_cle = pdata->mask_cle; - else - info->mask_cle = MASK_CLE; + info->mask_ale = pdata->mask_cle ? : MASK_ALE; + info->mask_cle = pdata->mask_cle ? : MASK_CLE; /* Set address of hardware control function */ info->chip.cmd_ctrl = nand_davinci_hwcontrol; @@ -377,30 +582,44 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.read_buf = nand_davinci_read_buf; info->chip.write_buf = nand_davinci_write_buf; - /* use board-specific ECC config; else, the best available */ - if (pdata) - ecc_mode = pdata->ecc_mode; - else - ecc_mode = NAND_ECC_HW; + /* Use board-specific ECC config */ + ecc_mode = pdata->ecc_mode; + ret = -EINVAL; switch (ecc_mode) { case NAND_ECC_NONE: case NAND_ECC_SOFT: + pdata->ecc_bits = 0; break; case NAND_ECC_HW: - info->chip.ecc.calculate = nand_davinci_calculate_1bit; - info->chip.ecc.correct = nand_davinci_correct_1bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; + if (pdata->ecc_bits == 4) { + /* No sanity checks: CPUs must support this, + * and the chips may not use NAND_BUSWIDTH_16. + */ + + /* No sharing 4-bit hardware between chipselects yet */ + spin_lock_irq(&davinci_nand_lock); + if (ecc4_busy) + ret = -EBUSY; + else + ecc4_busy = true; + spin_unlock_irq(&davinci_nand_lock); + + if (ret == -EBUSY) + goto err_ecc; + + info->chip.ecc.calculate = nand_davinci_calculate_4bit; + info->chip.ecc.correct = nand_davinci_correct_4bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; + info->chip.ecc.bytes = 10; + } else { + info->chip.ecc.calculate = nand_davinci_calculate_1bit; + info->chip.ecc.correct = nand_davinci_correct_1bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; + info->chip.ecc.bytes = 3; + } info->chip.ecc.size = 512; - info->chip.ecc.bytes = 3; break; - case NAND_ECC_HW_SYNDROME: - /* FIXME implement */ - info->chip.ecc.size = 512; - info->chip.ecc.bytes = 10; - - dev_warn(&pdev->dev, "4-bit ECC nyet supported\n"); - /* FALL THROUGH */ default: ret = -EINVAL; goto err_ecc; @@ -441,12 +660,56 @@ static int __init nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1); + ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); goto err_scan; } + /* Update ECC layout if needed ... for 1-bit HW ECC, the default + * is OK, but it allocates 6 bytes when only 3 are needed (for + * each 512 bytes). For the 4-bit HW ECC, that default is not + * usable: 10 bytes are needed, not 6. + */ + if (pdata->ecc_bits == 4) { + int chunks = info->mtd.writesize / 512; + + if (!chunks || info->mtd.oobsize < 16) { + dev_dbg(&pdev->dev, "too small\n"); + ret = -EINVAL; + goto err_scan; + } + + /* For small page chips, preserve the manufacturer's + * badblock marking data ... and make sure a flash BBT + * table marker fits in the free bytes. + */ + if (chunks == 1) { + info->ecclayout = hwecc4_small; + info->ecclayout.oobfree[1].length = + info->mtd.oobsize - 16; + 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). + */ + dev_warn(&pdev->dev, "no 4-bit ECC support yet " + "for large page NAND\n"); + ret = -EIO; + goto err_scan; + +syndrome_done: + info->chip.ecc.layout = &info->ecclayout; + } + + ret = nand_scan_tail(&info->mtd); + if (ret < 0) + goto err_scan; + if (mtd_has_partitions()) { struct mtd_partition *mtd_parts = NULL; int mtd_parts_nb = 0; @@ -455,22 +718,11 @@ static int __init nand_davinci_probe(struct platform_device *pdev) static const char *probes[] __initconst = { "cmdlinepart", NULL }; - const char *master_name; - - /* Set info->mtd.name = 0 temporarily */ - master_name = info->mtd.name; - info->mtd.name = (char *)0; - - /* info->mtd.name == 0, means: don't bother checking - <mtd-id> */ mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, &mtd_parts, 0); - - /* Restore info->mtd.name */ - info->mtd.name = master_name; } - if (mtd_parts_nb <= 0 && pdata) { + if (mtd_parts_nb <= 0) { mtd_parts = pdata->parts; mtd_parts_nb = pdata->nr_parts; } @@ -483,7 +735,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->partitioned = true; } - } else if (pdata && pdata->nr_parts) { + } else if (pdata->nr_parts) { dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", pdata->nr_parts, info->mtd.name); } @@ -509,6 +761,11 @@ err_scan: err_clk_enable: clk_put(info->clk); + spin_lock_irq(&davinci_nand_lock); + if (ecc_mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + err_ecc: err_clk: err_ioremap: @@ -532,6 +789,11 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) else status = del_mtd_device(&info->mtd); + spin_lock_irq(&davinci_nand_lock); + if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + iounmap(info->base); iounmap(info->vaddr); diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 40c26080ecd..76beea40d2c 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = { static struct nand_ecclayout nand_hw_eccoob_16 = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 6}, {12, 4}, } + .oobfree = {{0, 5}, {11, 5}, } +}; + +static struct nand_ecclayout nand_hw_eccoob_64 = { + .eccbytes = 20, + .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, + .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } }; #ifdef CONFIG_MTD_PARTITIONS @@ -192,7 +199,7 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries, } udelay(1); } - if (max_retries <= 0) + if (max_retries < 0) DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", __func__, param); } @@ -795,9 +802,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_addr(host, (page_addr & 0xff), false); if (host->pagesize_2k) { - send_addr(host, (page_addr >> 8) & 0xFF, false); - if (mtd->size >= 0x40000000) + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, false); send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, true); } else { /* One more address cycle for higher density devices */ if (mtd->size >= 0x4000000) { @@ -923,7 +934,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.mode = NAND_ECC_HW; this->ecc.size = 512; this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_8; tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_ECC_EN; writew(tmp, host->regs + NFC_CONFIG1); @@ -957,12 +967,44 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.layout = &nand_hw_eccoob_16; } - host->pagesize_2k = 0; + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1)) { + err = -ENXIO; + goto escan; + } - /* Scan to find existence of the device */ - if (nand_scan(mtd, 1)) { - DEBUG(MTD_DEBUG_LEVEL0, - "MXC_ND: Unable to find any NAND device.\n"); + host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; + + if (this->ecc.mode == NAND_ECC_HW) { + switch (mtd->oobsize) { + case 8: + this->ecc.layout = &nand_hw_eccoob_8; + break; + case 16: + this->ecc.layout = &nand_hw_eccoob_16; + break; + case 64: + this->ecc.layout = &nand_hw_eccoob_64; + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.mode = NAND_ECC_SOFT; + this->ecc.calculate = NULL; + this->ecc.correct = NULL; + this->ecc.hwctl = NULL; + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_ECC_EN; + writew(tmp, host->regs + NFC_CONFIG1); + break; + } + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { err = -ENXIO; goto escan; } @@ -985,7 +1027,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) return 0; escan: - free_irq(host->irq, NULL); + free_irq(host->irq, host); eirq: iounmap(host->regs); eres: @@ -1005,7 +1047,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); nand_release(&host->mtd); - free_irq(host->irq, NULL); + free_irq(host->irq, host); iounmap(host->regs); kfree(host); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 3d7ed432fa4..8c21b89d2d0 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd) * the out of band area */ chip->ecc.layout->oobavail = 0; - for (i = 0; chip->ecc.layout->oobfree[i].length; i++) + for (i = 0; chip->ecc.layout->oobfree[i].length + && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; mtd->oobavail = chip->ecc.layout->oobavail; diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 868147acce2..c0cb87d6d16 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc); int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { - unsigned char b0, b1, b2; - unsigned char byte_addr, bit_addr; + 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; diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c new file mode 100644 index 00000000000..0cd76f89f4b --- /dev/null +++ b/drivers/mtd/nand/omap2.c @@ -0,0 +1,776 @@ +/* + * Copyright © 2004 Texas Instruments, Jian Zhang <jzhang@ti.com> + * Copyright © 2004 Micron Technology Inc. + * Copyright © 2004 David Brownell + * + * 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/delay.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <linux/io.h> + +#include <asm/dma.h> + +#include <mach/gpmc.h> +#include <mach/nand.h> + +#define GPMC_IRQ_STATUS 0x18 +#define GPMC_ECC_CONFIG 0x1F4 +#define GPMC_ECC_CONTROL 0x1F8 +#define GPMC_ECC_SIZE_CONFIG 0x1FC +#define GPMC_ECC1_RESULT 0x200 + +#define DRIVER_NAME "omap2-nand" + +/* size (4 KiB) for IO mapping */ +#define NAND_IO_SIZE SZ_4K + +#define NAND_WP_OFF 0 +#define NAND_WP_BIT 0x00000010 +#define WR_RD_PIN_MONITORING 0x00600000 + +#define GPMC_BUF_FULL 0x00000001 +#define GPMC_BUF_EMPTY 0x00000000 + +#define NAND_Ecc_P1e (1 << 0) +#define NAND_Ecc_P2e (1 << 1) +#define NAND_Ecc_P4e (1 << 2) +#define NAND_Ecc_P8e (1 << 3) +#define NAND_Ecc_P16e (1 << 4) +#define NAND_Ecc_P32e (1 << 5) +#define NAND_Ecc_P64e (1 << 6) +#define NAND_Ecc_P128e (1 << 7) +#define NAND_Ecc_P256e (1 << 8) +#define NAND_Ecc_P512e (1 << 9) +#define NAND_Ecc_P1024e (1 << 10) +#define NAND_Ecc_P2048e (1 << 11) + +#define NAND_Ecc_P1o (1 << 16) +#define NAND_Ecc_P2o (1 << 17) +#define NAND_Ecc_P4o (1 << 18) +#define NAND_Ecc_P8o (1 << 19) +#define NAND_Ecc_P16o (1 << 20) +#define NAND_Ecc_P32o (1 << 21) +#define NAND_Ecc_P64o (1 << 22) +#define NAND_Ecc_P128o (1 << 23) +#define NAND_Ecc_P256o (1 << 24) +#define NAND_Ecc_P512o (1 << 25) +#define NAND_Ecc_P1024o (1 << 26) +#define NAND_Ecc_P2048o (1 << 27) + +#define TF(value) (value ? 1 : 0) + +#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) +#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) +#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) +#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) +#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) +#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) +#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) +#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) + +#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) +#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) +#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) +#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) +#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) +#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) +#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) +#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) + +#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) +#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) +#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) +#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) +#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) +#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) +#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) +#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) + +#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) +#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) +#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) +#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) +#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) +#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) +#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) +#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) + +#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) +#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +struct omap_nand_info { + struct nand_hw_control controller; + struct omap_nand_platform_data *pdata; + struct mtd_info mtd; + struct mtd_partition *parts; + struct nand_chip nand; + struct platform_device *pdev; + + int gpmc_cs; + unsigned long phys_base; + void __iomem *gpmc_cs_baseaddr; + void __iomem *gpmc_baseaddr; +}; + +/** + * omap_nand_wp - This function enable or disable the Write Protect feature + * @mtd: MTD device structure + * @mode: WP ON/OFF + */ +static void omap_nand_wp(struct mtd_info *mtd, int mode) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + + unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG); + + if (mode) + config &= ~(NAND_WP_BIT); /* WP is ON */ + else + config |= (NAND_WP_BIT); /* WP is OFF */ + + __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG)); +} + +/** + * omap_hwcontrol - hardware specific access to control-lines + * @mtd: MTD device structure + * @cmd: command to device + * @ctrl: + * NAND_NCE: bit 0 -> don't care + * NAND_CLE: bit 1 -> Command Latch + * NAND_ALE: bit 2 -> Address Latch + * + * NOTE: boards may use different bits for these!! + */ +static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_COMMAND; + info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + break; + + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_ADDRESS; + info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + break; + + case NAND_CTRL_CHANGE | NAND_NCE: + info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + break; + } + + if (cmd != NAND_CMD_NONE) + __raw_writeb(cmd, info->nand.IO_ADDR_W); +} + +/** + * omap_read_buf16 - 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_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); +} + +/** + * omap_write_buf16 - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + u16 *p = (u16 *) buf; + + /* FIXME try bursts of writesw() or DMA ... */ + len >>= 1; + + while (len--) { + writew(*p++, info->nand.IO_ADDR_W); + + while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + + GPMC_STATUS) & GPMC_BUF_FULL)) + ; + } +} +/** + * omap_verify_buf - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + */ +static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + u16 *p = (u16 *) buf; + + len >>= 1; + while (len--) { + if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R))) + return -EFAULT; + } + + return 0; +} + +#ifdef CONFIG_MTD_NAND_OMAP_HWECC +/** + * omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller + * @mtd: MTD device structure + */ +static void omap_hwecc_init(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_chip *chip = mtd->priv; + unsigned long val = 0x0; + + /* Read from ECC Control Register */ + val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* Clear all ECC | Enable Reg1 */ + val = ((0x00000001<<8) | 0x00000001); + __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + + /* Read from ECC Size Config Register */ + val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); + /* ECCSIZE1=512 | Select eccResultsize[0-3] */ + val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F)); + __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); +} + +/** + * gen_true_ecc - This function will generate true ECC value + * @ecc_buf: buffer to store ecc code + * + * This generated true ECC value can be used when correcting + * data read from NAND flash memory core + */ +static void gen_true_ecc(u8 *ecc_buf) +{ + u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | + ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); + + ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | + P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); + ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | + P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); + ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | + P1e(tmp) | P2048o(tmp) | P2048e(tmp)); +} + +/** + * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data + * @ecc_data1: ecc code from nand spare area + * @ecc_data2: ecc code from hardware register obtained from hardware ecc + * @page_data: page data + * + * This function compares two ECC's and indicates if there is an error. + * If the error can be corrected it will be corrected to the buffer. + */ +static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ + u8 *ecc_data2, /* read from register */ + u8 *page_data) +{ + uint i; + u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; + u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; + u8 ecc_bit[24]; + u8 ecc_sum = 0; + u8 find_bit = 0; + uint find_byte = 0; + int isEccFF; + + isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); + + gen_true_ecc(ecc_data1); + gen_true_ecc(ecc_data2); + + for (i = 0; i <= 2; i++) { + *(ecc_data1 + i) = ~(*(ecc_data1 + i)); + *(ecc_data2 + i) = ~(*(ecc_data2 + i)); + } + + for (i = 0; i < 8; i++) { + tmp0_bit[i] = *ecc_data1 % 2; + *ecc_data1 = *ecc_data1 / 2; + } + + for (i = 0; i < 8; i++) { + tmp1_bit[i] = *(ecc_data1 + 1) % 2; + *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + tmp2_bit[i] = *(ecc_data1 + 2) % 2; + *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; + } + + for (i = 0; i < 8; i++) { + comp0_bit[i] = *ecc_data2 % 2; + *ecc_data2 = *ecc_data2 / 2; + } + + for (i = 0; i < 8; i++) { + comp1_bit[i] = *(ecc_data2 + 1) % 2; + *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + comp2_bit[i] = *(ecc_data2 + 2) % 2; + *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; + } + + for (i = 0; i < 6; i++) + ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; + + ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; + ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; + + for (i = 0; i < 24; i++) + ecc_sum += ecc_bit[i]; + + switch (ecc_sum) { + case 0: + /* Not reached because this function is not called if + * ECC values are equal + */ + return 0; + + case 1: + /* Uncorrectable error */ + DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); + return -1; + + case 11: + /* UN-Correctable error */ + DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n"); + return -1; + + case 12: + /* Correctable error */ + find_byte = (ecc_bit[23] << 8) + + (ecc_bit[21] << 7) + + (ecc_bit[19] << 6) + + (ecc_bit[17] << 5) + + (ecc_bit[15] << 4) + + (ecc_bit[13] << 3) + + (ecc_bit[11] << 2) + + (ecc_bit[9] << 1) + + ecc_bit[7]; + + find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; + + DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at " + "offset: %d, bit: %d\n", find_byte, find_bit); + + page_data[find_byte] ^= (1 << find_bit); + + return 0; + default: + if (isEccFF) { + if (ecc_data2[0] == 0 && + ecc_data2[1] == 0 && + ecc_data2[2] == 0) + return 0; + } + DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n"); + return -1; + } +} + +/** + * omap_correct_data - Compares the ECC read with HW generated ECC + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + * + * Compares the ecc read from nand spare area with ECC registers values + * and if ECC's mismached, it will call 'omap_compare_ecc' for error detection + * and correction. + */ +static int omap_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + int blockCnt = 0, i = 0, ret = 0; + + /* Ex NAND_ECC_HW12_2048 */ + if ((info->nand.ecc.mode == NAND_ECC_HW) && + (info->nand.ecc.size == 2048)) + blockCnt = 4; + else + blockCnt = 1; + + for (i = 0; i < blockCnt; i++) { + if (memcmp(read_ecc, calc_ecc, 3) != 0) { + ret = omap_compare_ecc(read_ecc, calc_ecc, dat); + if (ret < 0) + return ret; + } + read_ecc += 3; + calc_ecc += 3; + dat += 512; + } + return 0; +} + +/** + * omap_calcuate_ecc - Generate non-inverted ECC bytes. + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Using noninverted ECC can be considered ugly since writing a blank + * page ie. padding will clear the ECC bytes. This is no problem as long + * nobody is trying to write data on the seemingly unused page. Reading + * an erased page will produce an ECC mismatch between generated and read + * ECC bytes that has to be dealt with separately. + */ +static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + unsigned long val = 0x0; + unsigned long reg; + + /* Start Reading from HW ECC1_Result = 0x200 */ + reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT); + val = __raw_readl(reg); + *ecc_code++ = val; /* P128e, ..., P1e */ + *ecc_code++ = val >> 16; /* P128o, ..., P1o */ + /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ + *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); + reg += 4; + + return 0; +} + +/** + * omap_enable_hwecc - This function enables the hardware ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_chip *chip = mtd->priv; + unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG); + + switch (mode) { + case NAND_ECC_READ: + __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + break; + case NAND_ECC_READSYN: + __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + break; + case NAND_ECC_WRITE: + __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + break; + default: + DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n", + mode); + break; + } + + __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG); +} +#endif + +/** + * omap_wait - wait until the command is done + * @mtd: MTD device structure + * @chip: NAND Chip structure + * + * Wait function is called during Program and erase operations and + * the way it is called from MTD layer, we should wait till the NAND + * chip is ready after the programming/erase operation has completed. + * + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + */ +static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct nand_chip *this = mtd->priv; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + unsigned long timeo = jiffies; + int status, state = this->state; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr + + GPMC_CS_NAND_COMMAND; + this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA; + + __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W); + + while (time_before(jiffies, timeo)) { + status = __raw_readb(this->IO_ADDR_R); + if (!(status & 0x40)) + break; + } + return status; +} + +/** + * omap_dev_ready - calls the platform specific dev_ready function + * @mtd: MTD device structure + */ +static int omap_dev_ready(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS); + + if ((val & 0x100) == 0x100) { + /* Clear IRQ Interrupt */ + val |= 0x100; + val &= ~(0x0); + __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS); + } else { + unsigned int cnt = 0; + while (cnt++ < 0x1FF) { + if ((val & 0x100) == 0x100) + return 0; + val = __raw_readl(info->gpmc_baseaddr + + GPMC_IRQ_STATUS); + } + } + + return 1; +} + +static int __devinit omap_nand_probe(struct platform_device *pdev) +{ + struct omap_nand_info *info; + struct omap_nand_platform_data *pdata; + int err; + unsigned long val; + + + pdata = pdev->dev.platform_data; + if (pdata == NULL) { + dev_err(&pdev->dev, "platform data missing\n"); + return -ENODEV; + } + + info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + platform_set_drvdata(pdev, info); + + spin_lock_init(&info->controller.lock); + init_waitqueue_head(&info->controller.wq); + + info->pdev = pdev; + + info->gpmc_cs = pdata->cs; + info->gpmc_baseaddr = pdata->gpmc_baseaddr; + info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr; + + info->mtd.priv = &info->nand; + info->mtd.name = dev_name(&pdev->dev); + info->mtd.owner = THIS_MODULE; + + err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base); + if (err < 0) { + dev_err(&pdev->dev, "Cannot request GPMC CS\n"); + goto out_free_info; + } + + /* Enable RD PIN Monitoring Reg */ + if (pdata->dev_ready) { + val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1); + val |= WR_RD_PIN_MONITORING; + gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val); + } + + val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7); + val &= ~(0xf << 8); + val |= (0xc & 0xf) << 8; + gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val); + + /* NAND write protect off */ + omap_nand_wp(&info->mtd, NAND_WP_OFF); + + if (!request_mem_region(info->phys_base, NAND_IO_SIZE, + pdev->dev.driver->name)) { + err = -EBUSY; + goto out_free_cs; + } + + info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE); + if (!info->nand.IO_ADDR_R) { + 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 + * register after monitoring the RDY/BSY line.Otherwise use a standard + * chip delay which is slightly more than tR (AC Timing) of the NAND + * device and read status register until you get a failure or success + */ + if (pdata->dev_ready) { + info->nand.dev_ready = omap_dev_ready; + info->nand.chip_delay = 0; + } else { + info->nand.waitfunc = omap_wait; + info->nand.chip_delay = 50; + } + + info->nand.options |= NAND_SKIP_BBTSCAN; + if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000) + == 0x1000) + info->nand.options |= NAND_BUSWIDTH_16; + +#ifdef CONFIG_MTD_NAND_OMAP_HWECC + info->nand.ecc.bytes = 3; + info->nand.ecc.size = 512; + info->nand.ecc.calculate = omap_calculate_ecc; + info->nand.ecc.hwctl = omap_enable_hwecc; + info->nand.ecc.correct = omap_correct_data; + info->nand.ecc.mode = NAND_ECC_HW; + + /* init HW ECC */ + omap_hwecc_init(&info->mtd); +#else + info->nand.ecc.mode = NAND_ECC_SOFT; +#endif + + /* DIP switches on some boards change between 8 and 16 bit + * bus widths for flash. Try the other width if the first try fails. + */ + if (nand_scan(&info->mtd, 1)) { + info->nand.options ^= NAND_BUSWIDTH_16; + if (nand_scan(&info->mtd, 1)) { + err = -ENXIO; + goto out_release_mem_region; + } + } + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); + if (err > 0) + add_mtd_partitions(&info->mtd, info->parts, err); + else if (pdata->parts) + add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); + else +#endif + add_mtd_device(&info->mtd); + + platform_set_drvdata(pdev, &info->mtd); + + return 0; + +out_release_mem_region: + release_mem_region(info->phys_base, NAND_IO_SIZE); +out_free_cs: + gpmc_cs_free(info->gpmc_cs); +out_free_info: + kfree(info); + + return err; +} + +static int omap_nand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct omap_nand_info *info = mtd->priv; + + platform_set_drvdata(pdev, NULL); + /* Release NAND device, its internal structures and partitions */ + nand_release(&info->mtd); + iounmap(info->nand.IO_ADDR_R); + kfree(&info->mtd); + return 0; +} + +static struct platform_driver omap_nand_driver = { + .probe = omap_nand_probe, + .remove = omap_nand_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init omap_nand_init(void) +{ + printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); + return platform_driver_register(&omap_nand_driver); +} + +static void __exit omap_nand_exit(void) +{ + platform_driver_unregister(&omap_nand_driver); +} + +module_init(omap_nand_init); +module_exit(omap_nand_exit); + +MODULE_ALIAS(DRIVER_NAME); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index c2dfd3ea353..7ad972229db 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl writeb(cmd, nc->IO_ADDR_W + offs); } +static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + void __iomem *io_base = chip->IO_ADDR_R; + uint64_t *buf64; + int i = 0; + + while (len && (unsigned long)buf & 7) { + *buf++ = readb(io_base); + len--; + } + buf64 = (uint64_t *)buf; + while (i < len/8) { + uint64_t x; + asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base)); + buf64[i++] = x; + } + i *= 8; + while (i < len) + buf[i++] = readb(io_base); +} + static int __init orion_nand_probe(struct platform_device *pdev) { struct mtd_info *mtd; @@ -83,6 +105,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) nc->priv = board; nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; nc->cmd_ctrl = orion_nand_cmd_ctrl; + nc->read_buf = orion_nand_read_buf; nc->ecc.mode = NAND_ECC_SOFT; if (board->chip_delay) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 86e1d08eee0..4e16c6f5bdd 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; data->chip.dev_ready = pdata->ctrl.dev_ready; data->chip.select_chip = pdata->ctrl.select_chip; + data->chip.write_buf = pdata->ctrl.write_buf; + data->chip.read_buf = pdata->ctrl.read_buf; data->chip.chip_delay = pdata->chip.chip_delay; data->chip.options |= pdata->chip.options; @@ -70,6 +72,13 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); + /* Handle any platform specific setup */ + if (pdata->ctrl.probe) { + res = pdata->ctrl.probe(pdev); + if (res) + goto out; + } + /* Scan to find existance of the device */ if (nand_scan(&data->mtd, 1)) { res = -ENXIO; @@ -86,6 +95,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) return 0; } } + if (pdata->chip.set_parts) + pdata->chip.set_parts(data->mtd.size, &pdata->chip); if (pdata->chip.partitions) { data->parts = pdata->chip.partitions; res = add_mtd_partitions(&data->mtd, data->parts, @@ -99,6 +110,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) nand_release(&data->mtd); out: + if (pdata->ctrl.remove) + pdata->ctrl.remove(pdev); platform_set_drvdata(pdev, NULL); iounmap(data->io_base); kfree(data); @@ -111,15 +124,15 @@ out: static int __devexit plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); -#ifdef CONFIG_MTD_PARTITIONS struct platform_nand_data *pdata = pdev->dev.platform_data; -#endif nand_release(&data->mtd); #ifdef CONFIG_MTD_PARTITIONS if (data->parts && data->parts != pdata->chip.partitions) kfree(data->parts); #endif + if (pdata->ctrl.remove) + pdata->ctrl.remove(pdev); iounmap(data->io_base); kfree(data); @@ -128,7 +141,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) static struct platform_driver plat_nand_driver = { .probe = plat_nand_probe, - .remove = plat_nand_remove, + .remove = __devexit_p(plat_nand_remove), .driver = { .name = "gen_nand", .owner = THIS_MODULE, diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8e375d5fe23..11dc7e69c4f 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = { struct s3c2410_nand_info; +/** + * struct s3c2410_nand_mtd - driver MTD structure + * @mtd: The MTD instance to pass to the MTD layer. + * @chip: The NAND chip information. + * @set: The platform information supplied for this set of NAND chips. + * @info: Link back to the hardware information. + * @scan_res: The result from calling nand_scan_ident(). +*/ struct s3c2410_nand_mtd { struct mtd_info mtd; struct nand_chip chip; @@ -90,6 +98,21 @@ enum s3c_cpu_type { /* overview of the s3c2410 nand state */ +/** + * struct s3c2410_nand_info - NAND controller state. + * @mtds: An array of MTD instances on this controoler. + * @platform: The platform data for this board. + * @device: The platform device we bound to. + * @area: The IO area resource that came from request_mem_region(). + * @clk: The clock resource for this controller. + * @regs: The area mapped for the hardware registers described by @area. + * @sel_reg: Pointer to the register controlling the NAND selection. + * @sel_bit: The bit in @sel_reg to select the NAND chip. + * @mtd_count: The number of MTDs created from this controller. + * @save_sel: The contents of @sel_reg to be saved over suspend. + * @clk_rate: The clock rate from @clk. + * @cpu_type: The exact type of this controller. + */ struct s3c2410_nand_info { /* mtd info */ struct nand_hw_control controller; @@ -145,12 +168,19 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info) #define NS_IN_KHZ 1000000 +/** + * s3c_nand_calc_rate - calculate timing data. + * @wanted: The cycle time in nanoseconds. + * @clk: The clock rate in kHz. + * @max: The maximum divider value. + * + * Calculate the timing value from the given parameters. + */ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) { int result; - result = (wanted * clk) / NS_IN_KHZ; - result++; + result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); pr_debug("result %d from %ld, %d\n", result, clk, wanted); @@ -169,13 +199,21 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) /* controller setup */ +/** + * s3c2410_nand_setrate - setup controller timing information. + * @info: The controller instance. + * + * Given the information supplied by the platform, calculate and set + * the necessary timing registers in the hardware to generate the + * necessary timing cycles to the hardware. + */ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) { struct s3c2410_platform_nand *plat = info->platform; int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; int tacls, twrph0, twrph1; unsigned long clkrate = clk_get_rate(info->clk); - unsigned long set, cfg, mask; + unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); unsigned long flags; /* calculate the timing information for the controller */ @@ -215,9 +253,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) case TYPE_S3C2440: case TYPE_S3C2412: - mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | - S3C2410_NFCONF_TWRPH0(7) | - S3C2410_NFCONF_TWRPH1(7)); + mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | + S3C2440_NFCONF_TWRPH0(7) | + S3C2440_NFCONF_TWRPH1(7)); set = S3C2440_NFCONF_TACLS(tacls - 1); set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); @@ -225,14 +263,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) break; default: - /* keep compiler happy */ - mask = 0; - set = 0; BUG(); } - dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); - local_irq_save(flags); cfg = readl(info->regs + S3C2410_NFCONF); @@ -242,9 +275,18 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) local_irq_restore(flags); + dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); + return 0; } +/** + * s3c2410_nand_inithw - basic hardware initialisation + * @info: The hardware state. + * + * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() + * to setup the hardware access speeds and set the controller to be enabled. +*/ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) { int ret; @@ -268,8 +310,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) return 0; } -/* select chip */ - +/** + * s3c2410_nand_select_chip - select the given nand chip + * @mtd: The MTD instance for this chip. + * @chip: The chip number. + * + * This is called by the MTD layer to either select a given chip for the + * @mtd instance, or to indicate that the access has finished and the + * chip can be de-selected. + * + * The routine ensures that the nFCE line is correctly setup, and any + * platform specific selection code is called to route nFCE to the specific + * chip. + */ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) { struct s3c2410_nand_info *info; @@ -530,7 +583,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - readsl(info->regs + S3C2440_NFDATA, buf, len / 4); + + readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); + + /* cleanup if we've got less than a word to do */ + if (len & 3) { + buf += len & ~3; + + for (; len & 3; len--) + *buf++ = readb(info->regs + S3C2440_NFDATA); + } } static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) @@ -542,7 +604,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - writesl(info->regs + S3C2440_NFDATA, buf, len / 4); + + writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); + + /* cleanup any fractional write */ + if (len & 3) { + buf += len & ~3; + + for (; len & 3; len--, buf++) + writeb(*buf, info->regs + S3C2440_NFDATA); + } } /* cpufreq driver support */ @@ -593,7 +664,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf /* device management functions */ -static int s3c2410_nand_remove(struct platform_device *pdev) +static int s3c24xx_nand_remove(struct platform_device *pdev) { struct s3c2410_nand_info *info = to_nand_info(pdev); @@ -645,17 +716,31 @@ static int s3c2410_nand_remove(struct platform_device *pdev) } #ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *mtd, struct s3c2410_nand_set *set) { + struct mtd_partition *part_info; + int nr_part = 0; + if (set == NULL) return add_mtd_device(&mtd->mtd); - if (set->nr_partitions > 0 && set->partitions != NULL) { - return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); + if (set->nr_partitions == 0) { + mtd->mtd.name = set->name; + nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, + &part_info, 0); + } else { + if (set->nr_partitions > 0 && set->partitions != NULL) { + nr_part = set->nr_partitions; + part_info = set->partitions; + } } + if (nr_part > 0 && part_info) + return add_mtd_partitions(&mtd->mtd, part_info, nr_part); + return add_mtd_device(&mtd->mtd); } #else @@ -667,11 +752,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, } #endif -/* s3c2410_nand_init_chip +/** + * s3c2410_nand_init_chip - initialise a single instance of an chip + * @info: The base NAND controller the chip is on. + * @nmtd: The new controller MTD instance to fill in. + * @set: The information passed from the board specific platform data. * - * init a single instance of an chip -*/ - + * Initialise the given @nmtd from the information in @info and @set. This + * readies the structure for use with the MTD layer functions by ensuring + * all pointers are setup and the necessary control routines selected. + */ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd, struct s3c2410_nand_set *set) @@ -757,14 +847,40 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, if (set->disable_ecc) chip->ecc.mode = NAND_ECC_NONE; + + switch (chip->ecc.mode) { + case NAND_ECC_NONE: + dev_info(info->device, "NAND ECC disabled\n"); + break; + case NAND_ECC_SOFT: + dev_info(info->device, "NAND soft ECC\n"); + break; + case NAND_ECC_HW: + dev_info(info->device, "NAND hardware ECC\n"); + break; + default: + dev_info(info->device, "NAND ECC UNKNOWN\n"); + break; + } + + /* If you use u-boot BBT creation code, specifying this flag will + * let the kernel fish out the BBT from the NAND, and also skip the + * full NAND scan that can take 1/2s or so. Little things... */ + if (set->flash_bbt) + chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; } -/* s3c2410_nand_update_chip +/** + * s3c2410_nand_update_chip - post probe update + * @info: The controller instance. + * @nmtd: The driver version of the MTD instance. * - * post-probe chip update, to change any items, such as the - * layout for large page nand - */ - + * This routine is called after the chip probe has succesfully completed + * and the relevant per-chip information updated. This call ensure that + * we update the internal state accordingly. + * + * The internal state is currently limited to the ECC state information. +*/ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd) { @@ -773,33 +889,33 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, dev_dbg(info->device, "chip %p => page shift %d\n", chip, chip->page_shift); - if (hardware_ecc) { + if (chip->ecc.mode != NAND_ECC_HW) + return; + /* change the behaviour depending on wether we are using * the large or small page nand device */ - if (chip->page_shift > 10) { - chip->ecc.size = 256; - chip->ecc.bytes = 3; - } else { - chip->ecc.size = 512; - chip->ecc.bytes = 3; - chip->ecc.layout = &nand_hw_eccoob; - } + if (chip->page_shift > 10) { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + } else { + chip->ecc.size = 512; + chip->ecc.bytes = 3; + chip->ecc.layout = &nand_hw_eccoob; } } -/* s3c2410_nand_probe +/* s3c24xx_nand_probe * * called by device layer when it finds a device matching * one our driver can handled. This code checks to see if * it can allocate all necessary resources then calls the * nand layer to look for devices */ - -static int s3c24xx_nand_probe(struct platform_device *pdev, - enum s3c_cpu_type cpu_type) +static int s3c24xx_nand_probe(struct platform_device *pdev) { struct s3c2410_platform_nand *plat = to_nand_plat(pdev); + enum s3c_cpu_type cpu_type; struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; @@ -809,6 +925,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int nr_sets; int setno; + cpu_type = platform_get_device_id(pdev)->driver_data; + pr_debug("s3c2410_nand_probe(%p)\n", pdev); info = kmalloc(sizeof(*info), GFP_KERNEL); @@ -922,7 +1040,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, return 0; exit_error: - s3c2410_nand_remove(pdev); + s3c24xx_nand_remove(pdev); if (err == 0) err = -EINVAL; @@ -983,50 +1101,33 @@ static int s3c24xx_nand_resume(struct platform_device *dev) /* driver device registration */ -static int s3c2410_nand_probe(struct platform_device *dev) -{ - return s3c24xx_nand_probe(dev, TYPE_S3C2410); -} - -static int s3c2440_nand_probe(struct platform_device *dev) -{ - return s3c24xx_nand_probe(dev, TYPE_S3C2440); -} - -static int s3c2412_nand_probe(struct platform_device *dev) -{ - return s3c24xx_nand_probe(dev, TYPE_S3C2412); -} - -static struct platform_driver s3c2410_nand_driver = { - .probe = s3c2410_nand_probe, - .remove = s3c2410_nand_remove, - .suspend = s3c24xx_nand_suspend, - .resume = s3c24xx_nand_resume, - .driver = { - .name = "s3c2410-nand", - .owner = THIS_MODULE, +static struct platform_device_id s3c24xx_driver_ids[] = { + { + .name = "s3c2410-nand", + .driver_data = TYPE_S3C2410, + }, { + .name = "s3c2440-nand", + .driver_data = TYPE_S3C2440, + }, { + .name = "s3c2412-nand", + .driver_data = TYPE_S3C2412, + }, { + .name = "s3c6400-nand", + .driver_data = TYPE_S3C2412, /* compatible with 2412 */ }, + { } }; -static struct platform_driver s3c2440_nand_driver = { - .probe = s3c2440_nand_probe, - .remove = s3c2410_nand_remove, - .suspend = s3c24xx_nand_suspend, - .resume = s3c24xx_nand_resume, - .driver = { - .name = "s3c2440-nand", - .owner = THIS_MODULE, - }, -}; +MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); -static struct platform_driver s3c2412_nand_driver = { - .probe = s3c2412_nand_probe, - .remove = s3c2410_nand_remove, +static struct platform_driver s3c24xx_nand_driver = { + .probe = s3c24xx_nand_probe, + .remove = s3c24xx_nand_remove, .suspend = s3c24xx_nand_suspend, .resume = s3c24xx_nand_resume, + .id_table = s3c24xx_driver_ids, .driver = { - .name = "s3c2412-nand", + .name = "s3c24xx-nand", .owner = THIS_MODULE, }, }; @@ -1035,16 +1136,12 @@ static int __init s3c2410_nand_init(void) { printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); - platform_driver_register(&s3c2412_nand_driver); - platform_driver_register(&s3c2440_nand_driver); - return platform_driver_register(&s3c2410_nand_driver); + return platform_driver_register(&s3c24xx_nand_driver); } static void __exit s3c2410_nand_exit(void) { - platform_driver_unregister(&s3c2412_nand_driver); - platform_driver_unregister(&s3c2440_nand_driver); - platform_driver_unregister(&s3c2410_nand_driver); + platform_driver_unregister(&s3c24xx_nand_driver); } module_init(s3c2410_nand_init); @@ -1053,6 +1150,3 @@ module_exit(s3c2410_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>"); MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); -MODULE_ALIAS("platform:s3c2410-nand"); -MODULE_ALIAS("platform:s3c2412-nand"); -MODULE_ALIAS("platform:s3c2440-nand"); diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 81247926489..488088eff2c 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -64,7 +64,7 @@ struct txx9ndfmc_priv { struct nand_chip chip; struct mtd_info mtd; int cs; - char mtdname[BUS_ID_SIZE + 2]; + const char *mtdname; }; #define MAX_TXX9NDFMC_DEV 4 @@ -334,16 +334,23 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) if (plat->ch_mask != 1) { txx9_priv->cs = i; - sprintf(txx9_priv->mtdname, "%s.%u", - dev_name(&dev->dev), i); + txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", + dev_name(&dev->dev), i); } else { txx9_priv->cs = -1; - strcpy(txx9_priv->mtdname, dev_name(&dev->dev)); + txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), + GFP_KERNEL); + } + if (!txx9_priv->mtdname) { + kfree(txx9_priv); + dev_err(&dev->dev, "Unable to allocate MTD name.\n"); + continue; } if (plat->wide_mask & (1 << i)) chip->options |= NAND_BUSWIDTH_16; if (nand_scan(mtd, 1)) { + kfree(txx9_priv->mtdname); kfree(txx9_priv); continue; } @@ -385,6 +392,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) kfree(drvdata->parts[i]); #endif del_mtd_device(mtd); + kfree(txx9_priv->mtdname); kfree(txx9_priv); } return 0; diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 6391e3dc800..38d656b9b2e 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -565,7 +565,7 @@ int omap2_onenand_rephase(void) NULL, __adjust_timing); } -static void __devexit omap2_onenand_shutdown(struct platform_device *pdev) +static void omap2_onenand_shutdown(struct platform_device *pdev) { struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); @@ -777,7 +777,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev) static struct platform_driver omap2_onenand_driver = { .probe = omap2_onenand_probe, - .remove = omap2_onenand_remove, + .remove = __devexit_p(omap2_onenand_remove), .shutdown = omap2_onenand_shutdown, .driver = { .name = DRIVER_NAME, diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 30d6999e5f9..6e829095ea9 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -9,6 +9,10 @@ * auto-placement support, read-while load support, various fixes * Copyright (C) Nokia Corporation, 2007 * + * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com> + * Flex-OneNAND support + * Copyright (C) Samsung Electronics, 2008 + * * 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. @@ -16,6 +20,7 @@ #include <linux/kernel.h> #include <linux/module.h> +#include <linux/moduleparam.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/delay.h> @@ -27,6 +32,38 @@ #include <asm/io.h> +/* Default Flex-OneNAND boundary and lock respectively */ +static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; + +module_param_array(flex_bdry, int, NULL, 0400); +MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND" + "Syntax:flex_bdry=DIE_BDRY,LOCK,..." + "DIE_BDRY: SLC boundary of the die" + "LOCK: Locking information for SLC boundary" + " : 0->Set boundary in unlocked status" + " : 1->Set boundary in locked status"); + +/** + * onenand_oob_128 - oob info for Flex-Onenand with 4KB page + * For now, we expose only 64 out of 80 ecc bytes + */ +static struct nand_ecclayout onenand_oob_128 = { + .eccbytes = 64, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 102, 103, 104, 105 + }, + .oobfree = { + {2, 4}, {18, 4}, {34, 4}, {50, 4}, + {66, 4}, {82, 4}, {98, 4}, {114, 4} + } +}; + /** * onenand_oob_64 - oob info for large (2KB) page */ @@ -65,6 +102,14 @@ static const unsigned char ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ }; /** @@ -171,6 +216,70 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) } /** + * flexonenand_block- For given address return block number + * @param this - OneNAND device structure + * @param addr - Address for which block number is needed + */ +static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr) +{ + unsigned boundary, blk, die = 0; + + if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { + die = 1; + addr -= this->diesize[0]; + } + + boundary = this->boundary[die]; + + blk = addr >> (this->erase_shift - 1); + if (blk > boundary) + blk = (blk + boundary + 1) >> 1; + + blk += die ? this->density_mask : 0; + return blk; +} + +inline unsigned onenand_block(struct onenand_chip *this, loff_t addr) +{ + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + return flexonenand_block(this, addr); +} + +/** + * flexonenand_addr - Return address of the block + * @this: OneNAND device structure + * @block: Block number on Flex-OneNAND + * + * Return address of the block + */ +static loff_t flexonenand_addr(struct onenand_chip *this, int block) +{ + loff_t ofs = 0; + int die = 0, boundary; + + if (ONENAND_IS_DDP(this) && block >= this->density_mask) { + block -= this->density_mask; + die = 1; + ofs = this->diesize[0]; + } + + boundary = this->boundary[die]; + ofs += (loff_t)block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1); + return ofs; +} + +loff_t onenand_addr(struct onenand_chip *this, int block) +{ + if (!FLEXONENAND(this)) + return (loff_t)block << this->erase_shift; + return flexonenand_addr(this, block); +} +EXPORT_SYMBOL(onenand_addr); + +/** * onenand_get_density - [DEFAULT] Get OneNAND density * @param dev_id OneNAND device ID * @@ -183,6 +292,22 @@ static inline int onenand_get_density(int dev_id) } /** + * flexonenand_region - [Flex-OneNAND] Return erase region of addr + * @param mtd MTD device structure + * @param addr address whose erase region needs to be identified + */ +int flexonenand_region(struct mtd_info *mtd, loff_t addr) +{ + int i; + + for (i = 0; i < mtd->numeraseregions; i++) + if (addr < mtd->eraseregions[i].offset) + break; + return i - 1; +} +EXPORT_SYMBOL(flexonenand_region); + +/** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd MTD device structure * @param cmd the command to be sent @@ -207,16 +332,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le page = -1; break; + case FLEXONENAND_CMD_PI_ACCESS: + /* addr contains die index */ + block = addr * this->density_mask; + page = -1; + break; + case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_OTP_ACCESS: - block = (int) (addr >> this->erase_shift); + block = onenand_block(this, addr); page = -1; break; + case FLEXONENAND_CMD_READ_PI: + cmd = ONENAND_CMD_READ; + block = addr * this->density_mask; + page = 0; + break; + default: - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); + block = onenand_block(this, addr); + page = (int) (addr - onenand_addr(this, block)) >> this->page_shift; if (ONENAND_IS_2PLANE(this)) { /* Make the even block number */ @@ -236,7 +373,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - if (ONENAND_IS_2PLANE(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this)) /* It is always BufferRAM0 */ ONENAND_SET_BUFFERRAM0(this); else @@ -258,13 +395,18 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le if (page != -1) { /* Now we use page size operation */ - int sectors = 4, count = 4; + int sectors = 0, count = 0; int dataram; switch (cmd) { + case FLEXONENAND_CMD_RECOVER_LSB: case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - dataram = ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_MLC(this)) + /* It is always BufferRAM0 */ + dataram = ONENAND_SET_BUFFERRAM0(this); + else + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); break; default: @@ -293,6 +435,30 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le } /** + * onenand_read_ecc - return ecc status + * @param this onenand chip structure + */ +static inline int onenand_read_ecc(struct onenand_chip *this) +{ + int ecc, i, result = 0; + + if (!FLEXONENAND(this)) + return this->read_word(this->base + ONENAND_REG_ECC_STATUS); + + for (i = 0; i < 4; i++) { + ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i); + if (likely(!ecc)) + continue; + if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) + return ONENAND_ECC_2BIT_ALL; + else + result = ONENAND_ECC_1BIT_ALL; + } + + return result; +} + +/** * onenand_wait - [DEFAULT] wait until the command is done * @param mtd MTD device structure * @param state state to select the max. timeout value @@ -331,14 +497,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) * power off recovery (POR) test, it should read ECC status first */ if (interrupt & ONENAND_INT_READ) { - int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + int ecc = onenand_read_ecc(this); if (ecc) { if (ecc & ONENAND_ECC_2BIT_ALL) { printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); mtd->ecc_stats.failed++; return -EBADMSG; } else if (ecc & ONENAND_ECC_1BIT_ALL) { - printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); + printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc); mtd->ecc_stats.corrected++; } } @@ -656,7 +822,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) if (found && ONENAND_IS_DDP(this)) { /* Select DataRAM for DDP */ - int block = (int) (addr >> this->erase_shift); + int block = onenand_block(this, addr); int value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); } @@ -816,6 +982,149 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col } /** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param mtd MTD device structure + * @param addr address to recover + * @param status return value from onenand_wait / onenand_bbt_wait + * + * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has + * lower page address and MSB page has higher page address in paired pages. + * If power off occurs during MSB page program, the paired LSB page data can + * become corrupt. LSB page recovery read is a way to read LSB page though page + * data are corrupted. When uncorrectable error occurs as a result of LSB page + * read after power up, issue LSB page recovery read. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) +{ + struct onenand_chip *this = mtd->priv; + int i; + + /* Recovery is only for Flex-OneNAND */ + if (!FLEXONENAND(this)) + return status; + + /* check if we failed due to uncorrectable error */ + if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR) + return status; + + /* check if address lies in MLC region */ + i = flexonenand_region(mtd, addr); + if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) + return status; + + /* We are attempting to reread, so decrement stats.failed + * which was incremented by onenand_wait due to read failure + */ + printk(KERN_INFO "onenand_recover_lsb: Attempting to recover from uncorrectable read\n"); + mtd->ecc_stats.failed--; + + /* Issue the LSB page recovery command */ + this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); + return this->wait(mtd, FL_READING); +} + +/** + * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops: oob operation description structure + * + * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram. + * So, read-while-load is not present. + */ +static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + u_char *buf = ops->datbuf; + u_char *oobbuf = ops->oobbuf; + int read = 0, column, thislen; + int oobread = 0, oobcolumn, thisooblen, oobsize; + int ret = 0; + int writesize = this->writesize; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + if (ops->mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + oobcolumn = from & (mtd->oobsize - 1); + + /* Do not allow reads past end of device */ + if (from + len > mtd->size) { + printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n"); + ops->retlen = 0; + ops->oobretlen = 0; + return -EINVAL; + } + + stats = mtd->ecc_stats; + + while (read < len) { + cond_resched(); + + thislen = min_t(int, writesize, len - read); + + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; + + if (!onenand_check_bufferram(mtd, from)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } + + this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); + if (oobbuf) { + thisooblen = oobsize - oobcolumn; + thisooblen = min_t(int, thisooblen, ooblen - oobread); + + if (ops->mode == MTD_OOB_AUTO) + onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); + oobread += thisooblen; + oobbuf += thisooblen; + oobcolumn = 0; + } + + read += thislen; + if (read == len) + break; + + from += thislen; + buf += thislen; + } + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + ops->retlen = read; + ops->oobretlen = oobread; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} + +/** * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure * @param from offset to read from @@ -962,7 +1271,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; u_char *buf = ops->oobbuf; - int ret = 0; + int ret = 0, readcmd; from += ops->ooboffs; @@ -993,17 +1302,22 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats; + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + while (read < len) { cond_resched(); thislen = oobsize - column; thislen = min_t(int, thislen, len); - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + if (ret && ret != -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); break; @@ -1053,6 +1367,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + struct onenand_chip *this = mtd->priv; struct mtd_oob_ops ops = { .len = len, .ooblen = 0, @@ -1062,7 +1377,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, int ret; onenand_get_device(mtd, FL_READING); - ret = onenand_read_ops_nolock(mtd, from, &ops); + ret = ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, &ops) : + onenand_read_ops_nolock(mtd, from, &ops); onenand_release_device(mtd); *retlen = ops.retlen; @@ -1080,6 +1397,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + struct onenand_chip *this = mtd->priv; int ret; switch (ops->mode) { @@ -1094,7 +1412,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, onenand_get_device(mtd, FL_READING); if (ops->datbuf) - ret = onenand_read_ops_nolock(mtd, from, ops); + ret = ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, ops) : + onenand_read_ops_nolock(mtd, from, ops); else ret = onenand_read_oob_nolock(mtd, from, ops); onenand_release_device(mtd); @@ -1128,11 +1448,11 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (interrupt & ONENAND_INT_READ) { - int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + int ecc = onenand_read_ecc(this); if (ecc & ONENAND_ECC_2BIT_ALL) { printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" ", controller error 0x%04x\n", ecc, ctrl); - return ONENAND_BBT_READ_ERROR; + return ONENAND_BBT_READ_ECC_ERROR; } } else { printk(KERN_ERR "onenand_bbt_wait: read timeout!" @@ -1163,7 +1483,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; - int ret = 0; + int ret = 0, readcmd; size_t len = ops->ooblen; u_char *buf = ops->oobbuf; @@ -1183,17 +1503,22 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, column = from & (mtd->oobsize - 1); + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + while (read < len) { cond_resched(); thislen = mtd->oobsize - column; thislen = min_t(int, thislen, len); - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); - ret = onenand_bbt_wait(mtd, FL_READING); + ret = this->bbt_wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + if (ret) break; @@ -1230,9 +1555,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to { struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; - int status, i; + int status, i, readcmd; - this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + + this->command(mtd, readcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_READING); if (status) @@ -1633,7 +1960,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; - int written = 0; + int written = 0, oobcmd; u_char *oobbuf; size_t len = ops->ooblen; const u_char *buf = ops->oobbuf; @@ -1675,6 +2002,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, oobbuf = this->oob_buf; + oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; + /* Loop until all data write */ while (written < len) { int thislen = min_t(int, oobsize, len - written); @@ -1692,7 +2021,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); - this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + if (ONENAND_IS_MLC(this)) { + /* Set main area of DataRAM to 0xff*/ + memset(this->page_buf, 0xff, mtd->writesize); + this->write_bufferram(mtd, ONENAND_DATARAM, + this->page_buf, 0, mtd->writesize); + } + + this->command(mtd, oobcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { @@ -1815,29 +2151,48 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) { struct onenand_chip *this = mtd->priv; unsigned int block_size; - loff_t addr; - int len; - int ret = 0; + loff_t addr = instr->addr; + loff_t len = instr->len; + int ret = 0, i; + struct mtd_erase_region_info *region = NULL; + loff_t region_end = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); - block_size = (1 << this->erase_shift); - - /* Start address must align on block boundary */ - if (unlikely(instr->addr & (block_size - 1))) { - printk(KERN_ERR "onenand_erase: Unaligned address\n"); + /* Do not allow erase past end of device */ + if (unlikely((len + addr) > mtd->size)) { + printk(KERN_ERR "onenand_erase: Erase past end of device\n"); return -EINVAL; } - /* Length must align on block boundary */ - if (unlikely(instr->len & (block_size - 1))) { - printk(KERN_ERR "onenand_erase: Length not block aligned\n"); - return -EINVAL; + if (FLEXONENAND(this)) { + /* Find the eraseregion of this address */ + i = flexonenand_region(mtd, addr); + region = &mtd->eraseregions[i]; + + block_size = region->erasesize; + region_end = region->offset + region->erasesize * region->numblocks; + + /* Start address within region must align on block boundary. + * Erase region's start offset is always block start address. + */ + if (unlikely((addr - region->offset) & (block_size - 1))) { + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + return -EINVAL; + } + } else { + block_size = 1 << this->erase_shift; + + /* Start address must align on block boundary */ + if (unlikely(addr & (block_size - 1))) { + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + return -EINVAL; + } } - /* Do not allow erase past end of device */ - if (unlikely((instr->len + instr->addr) > mtd->size)) { - printk(KERN_ERR "onenand_erase: Erase past end of device\n"); + /* Length must align on block boundary */ + if (unlikely(len & (block_size - 1))) { + printk(KERN_ERR "onenand_erase: Length not block aligned\n"); return -EINVAL; } @@ -1847,9 +2202,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) onenand_get_device(mtd, FL_ERASING); /* Loop throught the pages */ - len = instr->len; - addr = instr->addr; - instr->state = MTD_ERASING; while (len) { @@ -1869,7 +2221,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { - printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); + printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", + onenand_block(this, addr)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -1877,6 +2230,22 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) len -= block_size; addr += block_size; + + if (addr == region_end) { + if (!len) + break; + region++; + + block_size = region->erasesize; + region_end = region->offset + region->erasesize * region->numblocks; + + if (len & (block_size - 1)) { + /* FIXME: This should be handled at MTD partitioning level. */ + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + goto erase_exit; + } + } + } instr->state = MTD_ERASE_DONE; @@ -1955,13 +2324,17 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) int block; /* Get block number */ - block = ((int) ofs) >> bbm->bbt_erase_shift; + block = onenand_block(this, 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 */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - return onenand_write_oob_nolock(mtd, ofs, &ops); + /* FIXME : What to do when marking SLC block in partition + * with MLC erasesize? For now, it is not advisable to + * create partitions containing both SLC and MLC regions. + */ + return onenand_write_oob_nolock(mtd, ofs, &ops); } /** @@ -2005,8 +2378,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int int start, end, block, value, status; int wp_status_mask; - start = ofs >> this->erase_shift; - end = len >> this->erase_shift; + start = onenand_block(this, ofs); + end = onenand_block(this, ofs + len) - 1; if (cmd == ONENAND_CMD_LOCK) wp_status_mask = ONENAND_WP_LS; @@ -2018,7 +2391,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ - this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); + this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write lock command */ this->command(mtd, cmd, 0, 0); @@ -2039,7 +2412,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int } /* Block lock scheme */ - for (block = start; block < start + end; block++) { + for (block = start; block < end + 1; block++) { /* Set block address */ value = onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); @@ -2147,7 +2520,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; loff_t ofs = 0; - size_t len = this->chipsize; + loff_t len = mtd->size; if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Set start block address */ @@ -2163,12 +2536,16 @@ static void onenand_unlock_all(struct mtd_info *mtd) & ONENAND_CTRL_ONGO) continue; + /* Don't check lock status */ + if (this->options & ONENAND_SKIP_UNLOCK_CHECK) + return; + /* Check lock status */ if (onenand_check_lock_status(this)) return; /* Workaround for all block unlock in DDP */ - if (ONENAND_IS_DDP(this)) { + if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { /* All blocks on another chip */ ofs = this->chipsize >> 1; len = this->chipsize >> 1; @@ -2210,7 +2587,9 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_read_ops_nolock(mtd, from, &ops); + ret = ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, &ops) : + onenand_read_ops_nolock(mtd, from, &ops); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2277,21 +2656,32 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; - struct mtd_oob_ops ops = { - .mode = MTD_OOB_PLACE, - .ooblen = len, - .oobbuf = buf, - .ooboffs = 0, - }; + struct mtd_oob_ops ops; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_write_oob_nolock(mtd, from, &ops); - - *retlen = ops.oobretlen; + if (FLEXONENAND(this)) { + /* + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. + */ + ops.len = mtd->writesize; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); + *retlen = ops.retlen; + } else { + ops.mode = MTD_OOB_PLACE; + ops.ooblen = len; + ops.oobbuf = buf; + ops.ooboffs = 0; + ret = onenand_write_oob_nolock(mtd, from, &ops); + *retlen = ops.oobretlen; + } /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2475,27 +2865,34 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { struct onenand_chip *this = mtd->priv; - u_char *oob_buf = this->oob_buf; + u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; size_t retlen; int ret; - memset(oob_buf, 0xff, mtd->oobsize); + memset(buf, 0xff, FLEXONENAND(this) ? this->writesize + : mtd->oobsize); /* * Note: OTP lock operation * OTP block : 0xXXFC * 1st block : 0xXXF3 (If chip support) * Both : 0xXXF0 (If chip support) */ - oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; + if (FLEXONENAND(this)) + buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC; + else + buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; /* * Write lock mark to 8th word of sector0 of page0 of the spare0. * We write 16 bytes spare area instead of 2 bytes. + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. */ + from = 0; - len = 16; + len = FLEXONENAND(this) ? mtd->writesize : 16; - ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER); + ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); return ret ? : retlen; } @@ -2542,6 +2939,14 @@ static void onenand_check_features(struct mtd_info *mtd) break; } + if (ONENAND_IS_MLC(this)) + this->options &= ~ONENAND_HAS_2PLANE; + + if (FLEXONENAND(this)) { + this->options &= ~ONENAND_HAS_CONT_LOCK; + this->options |= ONENAND_HAS_UNLOCK_ALL; + } + if (this->options & ONENAND_HAS_CONT_LOCK) printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); if (this->options & ONENAND_HAS_UNLOCK_ALL) @@ -2559,14 +2964,16 @@ static void onenand_check_features(struct mtd_info *mtd) */ static void onenand_print_device_info(int device, int version) { - int vcc, demuxed, ddp, density; + int vcc, demuxed, ddp, density, flexonenand; vcc = device & ONENAND_DEVICE_VCC_MASK; demuxed = device & ONENAND_DEVICE_IS_DEMUX; ddp = device & ONENAND_DEVICE_IS_DDP; density = onenand_get_density(device); - printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", - demuxed ? "" : "Muxed ", + flexonenand = device & DEVICE_IS_FLEXONENAND; + printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", + demuxed ? "" : "Muxed ", + flexonenand ? "Flex-" : "", ddp ? "(DDP)" : "", (16 << density), vcc ? "2.65/3.3" : "1.8", @@ -2576,6 +2983,7 @@ static void onenand_print_device_info(int device, int version) static const struct onenand_manufacturers onenand_manuf_ids[] = { {ONENAND_MFR_SAMSUNG, "Samsung"}, + {ONENAND_MFR_NUMONYX, "Numonyx"}, }; /** @@ -2605,6 +3013,261 @@ static int onenand_check_maf(int manuf) } /** +* flexonenand_get_boundary - Reads the SLC boundary +* @param onenand_info - onenand info structure +**/ +static int flexonenand_get_boundary(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned die, bdry; + int ret, syscfg, locked; + + /* Disable ECC */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); + + for (die = 0; die < this->dies; die++) { + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + ret = this->wait(mtd, FL_READING); + + bdry = this->read_word(this->base + ONENAND_DATARAM); + if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) + locked = 0; + else + locked = 1; + this->boundary[die] = bdry & FLEXONENAND_PI_MASK; + + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + ret = this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info + * boundary[], diesize[], mtd->size, mtd->erasesize + * @param mtd - MTD device structure + */ +static void flexonenand_get_size(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int die, i, eraseshift, density; + int blksperdie, maxbdry; + loff_t ofs; + + density = onenand_get_density(this->device_id); + blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + maxbdry = blksperdie - 1; + eraseshift = this->erase_shift - 1; + + mtd->numeraseregions = this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die = ofs = 0; + i = -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = + this->boundary[die] + 1; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -= 1; + mtd->eraseregions[i].numblocks += + this->boundary[die] + 1; + ofs += (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = maxbdry ^ + this->boundary[die]; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -= 1; + } + + /* Expose MLC erase size except when all blocks are SLC */ + mtd->erasesize = 1 << this->erase_shift; + if (mtd->numeraseregions == 1) + mtd->erasesize >>= 1; + + printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); + for (i = 0; i < mtd->numeraseregions; i++) + printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," + " numblocks: %04u]\n", + (unsigned int) mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + + for (die = 0, mtd->size = 0; die < this->dies; die++) { + this->diesize[die] = (loff_t)blksperdie << this->erase_shift; + this->diesize[die] -= (loff_t)(this->boundary[die] + 1) + << (this->erase_shift - 1); + mtd->size += this->diesize[die]; + } +} + +/** + * flexonenand_check_blocks_erased - Check if blocks are erased + * @param mtd_info - mtd info structure + * @param start - first erase block to check + * @param end - last erase block to check + * + * Converting an unerased block from MLC to SLC + * causes byte values to change. Since both data and its ECC + * have changed, reads on the block give uncorrectable error. + * This might lead to the block being detected as bad. + * + * Avoid this by ensuring that the block to be converted is + * erased. + */ +static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end) +{ + struct onenand_chip *this = mtd->priv; + int i, ret; + int block; + struct mtd_oob_ops ops = { + .mode = MTD_OOB_PLACE, + .ooboffs = 0, + .ooblen = mtd->oobsize, + .datbuf = NULL, + .oobbuf = this->oob_buf, + }; + loff_t addr; + + printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); + + for (block = start; block <= end; block++) { + addr = flexonenand_addr(this, block); + if (onenand_block_isbad_nolock(mtd, addr, 0)) + continue; + + /* + * Since main area write results in ECC write to spare, + * it is sufficient to check only ECC bytes for change. + */ + ret = onenand_read_oob_nolock(mtd, addr, &ops); + if (ret) + return ret; + + for (i = 0; i < mtd->oobsize; i++) + if (this->oob_buf[i] != 0xff) + break; + + if (i != mtd->oobsize) { + printk(KERN_WARNING "Block %d not erased.\n", block); + return 1; + } + } + + return 0; +} + +/** + * flexonenand_set_boundary - Writes the SLC boundary + * @param mtd - mtd info structure + */ +int flexonenand_set_boundary(struct mtd_info *mtd, int die, + int boundary, int lock) +{ + struct onenand_chip *this = mtd->priv; + int ret, density, blksperdie, old, new, thisboundary; + loff_t addr; + + /* Change only once for SDP Flex-OneNAND */ + if (die && (!ONENAND_IS_DDP(this))) + return 0; + + /* boundary value of -1 indicates no required change */ + if (boundary < 0 || boundary == this->boundary[die]) + return 0; + + density = onenand_get_density(this->device_id); + blksperdie = ((16 << density) << 20) >> this->erase_shift; + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + + if (boundary >= blksperdie) { + printk(KERN_ERR "flexonenand_set_boundary: Invalid boundary value. " + "Boundary not changed.\n"); + return -EINVAL; + } + + /* Check if converting blocks are erased */ + old = this->boundary[die] + (die * this->density_mask); + new = boundary + (die * this->density_mask); + ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new)); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); + return ret; + } + + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + /* Check is boundary is locked */ + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + ret = this->wait(mtd, FL_READING); + + thisboundary = this->read_word(this->base + ONENAND_DATARAM); + if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { + printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); + ret = 1; + goto out; + } + + printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", + die, boundary, lock ? "(Locked)" : "(Unlocked)"); + + addr = die ? this->diesize[0] : 0; + + boundary &= FLEXONENAND_PI_MASK; + boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); + + this->command(mtd, ONENAND_CMD_ERASE, addr, 0); + ret = this->wait(mtd, FL_ERASING); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Failed PI erase for Die %d\n", die); + goto out; + } + + this->write_word(boundary, this->base + ONENAND_DATARAM); + this->command(mtd, ONENAND_CMD_PROG, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Failed PI write for Die %d\n", die); + goto out; + } + + this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); + ret = this->wait(mtd, FL_WRITING); +out: + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); + this->wait(mtd, FL_RESETING); + if (!ret) + /* Recalculate device size on boundary change*/ + flexonenand_get_size(mtd); + + return ret; +} + +/** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd MTD device structure * @@ -2621,7 +3284,7 @@ static int onenand_probe(struct mtd_info *mtd) /* Save system configuration 1 */ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); /* Clear Sync. Burst Read mode to read BootRAM */ - this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1); /* Send the command for reading device ID from BootRAM */ this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); @@ -2646,6 +3309,7 @@ static int onenand_probe(struct mtd_info *mtd) maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); /* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) @@ -2657,29 +3321,55 @@ static int onenand_probe(struct mtd_info *mtd) this->version_id = ver_id; density = onenand_get_density(dev_id); + if (FLEXONENAND(this)) { + this->dies = ONENAND_IS_DDP(this) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions = this->dies << 1; + mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1), GFP_KERNEL); + if (!mtd->eraseregions) + return -ENOMEM; + } + + /* + * For Flex-OneNAND, chipsize represents maximum possible device size. + * mtd->size represents the actual device size. + */ this->chipsize = (16 << density) << 20; - /* Set density mask. it is used for DDP */ - if (ONENAND_IS_DDP(this)) - this->density_mask = (1 << (density + 6)); - else - this->density_mask = 0; /* OneNAND page size & block size */ /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); + /* We use the full BufferRAM */ + if (ONENAND_IS_MLC(this)) + mtd->writesize <<= 1; + mtd->oobsize = mtd->writesize >> 5; /* Pages per a block are always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; + /* + * Flex-OneNAND SLC area has 64 pages per block. + * Flex-OneNAND MLC area has 128 pages per block. + * Expose MLC erase size to find erase_shift and page_mask. + */ + if (FLEXONENAND(this)) + mtd->erasesize <<= 1; this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; + /* Set density mask. it is used for DDP */ + if (ONENAND_IS_DDP(this)) + this->density_mask = this->chipsize >> (this->erase_shift + 1); /* It's real page size */ this->writesize = mtd->writesize; /* REVIST: Multichip handling */ - mtd->size = this->chipsize; + if (FLEXONENAND(this)) + flexonenand_get_size(mtd); + else + mtd->size = this->chipsize; /* Check OneNAND features */ onenand_check_features(mtd); @@ -2734,7 +3424,7 @@ static void onenand_resume(struct mtd_info *mtd) */ int onenand_scan(struct mtd_info *mtd, int maxchips) { - int i; + int i, ret; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2746,6 +3436,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) this->command = onenand_command; if (!this->wait) onenand_setup_wait(mtd); + if (!this->bbt_wait) + this->bbt_wait = onenand_bbt_wait; + if (!this->unlock_all) + this->unlock_all = onenand_unlock_all; if (!this->read_bufferram) this->read_bufferram = onenand_read_bufferram; @@ -2796,6 +3490,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) * Allow subpage writes up to oobsize. */ switch (mtd->oobsize) { + case 128: + this->ecclayout = &onenand_oob_128; + mtd->subpage_sft = 0; + break; case 64: this->ecclayout = &onenand_oob_64; mtd->subpage_sft = 2; @@ -2859,9 +3557,18 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->owner = THIS_MODULE; /* Unlock whole block */ - onenand_unlock_all(mtd); + this->unlock_all(mtd); + + ret = this->scan_bbt(mtd); + if ((!FLEXONENAND(this)) || ret) + return ret; - return this->scan_bbt(mtd); + /* Change Flex-OneNAND boundaries if required */ + for (i = 0; i < MAX_DIES; i++) + flexonenand_set_boundary(mtd, i, flex_bdry[2 * i], + flex_bdry[(2 * i) + 1]); + + return 0; } /** @@ -2890,6 +3597,7 @@ void onenand_release(struct mtd_info *mtd) kfree(this->page_buf); if (this->options & ONENAND_OOBBUF_ALLOC) kfree(this->oob_buf); + kfree(mtd->eraseregions); } EXPORT_SYMBOL_GPL(onenand_scan); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 2f53b51c680..a91fcac1af0 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr loff_t from; size_t readlen, ooblen; struct mtd_oob_ops ops; + int rgn; printk(KERN_INFO "Scanning device for bad blocks\n"); @@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* Note that numblocks is 2 * (real numblocks) here; * see i += 2 below as it makses shifting and masking less painful */ - numblocks = mtd->size >> (bbm->bbt_erase_shift - 1); + numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); startblock = 0; from = 0; @@ -106,7 +107,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr } } i += 2; - from += (1 << bbm->bbt_erase_shift); + + if (FLEXONENAND(this)) { + rgn = flexonenand_region(mtd, from); + from += mtd->eraseregions[rgn].erasesize; + } else + from += (1 << bbm->bbt_erase_shift); } return 0; @@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) uint8_t res; /* Get block number * 2 */ - block = (int) (offs >> (bbm->bbt_erase_shift - 1)); + block = (int) (onenand_block(this, offs) << 1); res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03; DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n", @@ -178,7 +184,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) struct bbm_info *bbm = this->bbm; int len, ret = 0; - len = mtd->size >> (this->erase_shift + 2); + len = this->chipsize >> (this->erase_shift + 2); /* Allocate memory (2bit per block) and clear the memory bad block table */ bbm->bbt = kzalloc(len, GFP_KERNEL); if (!bbm->bbt) { diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c index d64200b7c94..f6e3c8aebd3 100644 --- a/drivers/mtd/onenand/onenand_sim.c +++ b/drivers/mtd/onenand/onenand_sim.c @@ -6,6 +6,10 @@ * Copyright © 2005-2007 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * + * Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com> + * Flex-OneNAND simulator support + * Copyright (C) Samsung Electronics, 2008 + * * 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. @@ -24,16 +28,38 @@ #ifndef CONFIG_ONENAND_SIM_MANUFACTURER #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec #endif + #ifndef CONFIG_ONENAND_SIM_DEVICE_ID #define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 #endif + +#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1) + #ifndef CONFIG_ONENAND_SIM_VERSION_ID #define CONFIG_ONENAND_SIM_VERSION_ID 0x1e #endif +#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID +#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND +#endif + +/* Initial boundary values for Flex-OneNAND Simulator */ +#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY +#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01 +#endif + +#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY +#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01 +#endif + static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; +static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID; +static int boundary[] = { + CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY, + CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY, +}; struct onenand_flash { void __iomem *base; @@ -57,12 +83,18 @@ struct onenand_flash { (writew(v, this->base + ONENAND_REG_WP_STATUS)) /* It has all 0xff chars */ -#define MAX_ONENAND_PAGESIZE (2048 + 64) +#define MAX_ONENAND_PAGESIZE (4096 + 128) static unsigned char *ffchars; +#if CONFIG_FLEXONENAND +#define PARTITION_NAME "Flex-OneNAND simulator partition" +#else +#define PARTITION_NAME "OneNAND simulator partition" +#endif + static struct mtd_partition os_partitions[] = { { - .name = "OneNAND simulator partition", + .name = PARTITION_NAME, .offset = 0, .size = MTDPART_SIZ_FULL, }, @@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd) switch (cmd) { case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_UNLOCK_ALL: if (block_lock_scheme) ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); else @@ -228,10 +261,12 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, { struct mtd_info *mtd = &info->mtd; struct onenand_flash *flash = this->priv; - int main_offset, spare_offset; + int main_offset, spare_offset, die = 0; void __iomem *src; void __iomem *dest; unsigned int i; + static int pi_operation; + int erasesize, rgn; if (dataram) { main_offset = mtd->writesize; @@ -241,10 +276,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, spare_offset = 0; } + if (pi_operation) { + die = readw(this->base + ONENAND_REG_START_ADDRESS2); + die >>= ONENAND_DDP_SHIFT; + } + switch (cmd) { + case FLEXONENAND_CMD_PI_ACCESS: + pi_operation = 1; + break; + + case ONENAND_CMD_RESET: + pi_operation = 0; + break; + case ONENAND_CMD_READ: src = ONENAND_CORE(flash) + offset; dest = ONENAND_MAIN_AREA(this, main_offset); + if (pi_operation) { + writew(boundary[die], this->base + ONENAND_DATARAM); + break; + } memcpy(dest, src, mtd->writesize); /* Fall through */ @@ -257,6 +309,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, case ONENAND_CMD_PROG: src = ONENAND_MAIN_AREA(this, main_offset); dest = ONENAND_CORE(flash) + offset; + if (pi_operation) { + boundary[die] = readw(this->base + ONENAND_DATARAM); + break; + } /* To handle partial write */ for (i = 0; i < (1 << mtd->subpage_sft); i++) { int off = i * this->subpagesize; @@ -284,9 +340,18 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, break; case ONENAND_CMD_ERASE: - memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); + if (pi_operation) + break; + + if (FLEXONENAND(this)) { + rgn = flexonenand_region(mtd, offset); + erasesize = mtd->eraseregions[rgn].erasesize; + } else + erasesize = mtd->erasesize; + + memset(ONENAND_CORE(flash) + offset, 0xff, erasesize); memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, - (mtd->erasesize >> 5)); + (erasesize >> 5)); break; default: @@ -339,7 +404,7 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd) } if (block != -1) - offset += block << this->erase_shift; + offset = onenand_addr(this, block); if (page != -1) offset += page << this->page_shift; @@ -390,6 +455,7 @@ static int __init flash_init(struct onenand_flash *flash) } density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density &= ONENAND_DEVICE_DENSITY_MASK; size = ((16 << 20) << density); ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); @@ -405,8 +471,9 @@ static int __init flash_init(struct onenand_flash *flash) writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); writew(version_id, flash->base + ONENAND_REG_VERSION_ID); + writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY); - if (density < 2) + if (density < 2 && (!CONFIG_FLEXONENAND)) buffer_size = 0x0400; /* 1KiB page */ else buffer_size = 0x0800; /* 2KiB page */ diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 892a9e4e275..c155bd3ec9f 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1725,6 +1725,7 @@ config TLAN config KS8842 tristate "Micrel KSZ8842" + depends on HAS_IOMEM help This platform driver is for Micrel KSZ8842 chip. @@ -2443,6 +2444,17 @@ config JME To compile this driver as a module, choose M here. The module will be called jme. +config S6GMAC + tristate "S6105 GMAC ethernet support" + depends on XTENSA_VARIANT_S6000 + select PHYLIB + help + This driver supports the on chip ethernet device on the + S6105 xtensa processor. + + To compile this driver as a module, choose M here. The module + will be called s6gmac. + endif # NETDEV_1000 # diff --git a/drivers/net/Makefile b/drivers/net/Makefile index d366fb2b40e..4b58a59f211 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -245,6 +245,7 @@ obj-$(CONFIG_XTENSA_XT2000_SONIC) += xtsonic.o obj-$(CONFIG_DNET) += dnet.o obj-$(CONFIG_MACB) += macb.o +obj-$(CONFIG_S6GMAC) += s6gmac.o obj-$(CONFIG_ARM) += arm/ obj-$(CONFIG_DEV_APPLETALK) += appletalk/ diff --git a/drivers/net/benet/be.h b/drivers/net/benet/be.h index b4bb06fdf30..f703758f0a6 100644 --- a/drivers/net/benet/be.h +++ b/drivers/net/benet/be.h @@ -65,7 +65,7 @@ static inline char *nic_name(struct pci_dev *pdev) #define TX_CQ_LEN 1024 #define RX_Q_LEN 1024 /* Does not support any other value */ #define RX_CQ_LEN 1024 -#define MCC_Q_LEN 64 /* total size not to exceed 8 pages */ +#define MCC_Q_LEN 128 /* total size not to exceed 8 pages */ #define MCC_CQ_LEN 256 #define BE_NAPI_WEIGHT 64 @@ -91,6 +91,61 @@ struct be_queue_info { atomic_t used; /* Number of valid elements in the queue */ }; +static inline u32 MODULO(u16 val, u16 limit) +{ + BUG_ON(limit & (limit - 1)); + return val & (limit - 1); +} + +static inline void index_adv(u16 *index, u16 val, u16 limit) +{ + *index = MODULO((*index + val), limit); +} + +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); +} + + +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 napi_struct napi; +}; + +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 */ @@ -98,11 +153,20 @@ struct be_ctrl_info { int pci_func; /* Mbox used for cmd request/response */ - spinlock_t cmd_lock; /* For serializing cmds to BE card */ + 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" @@ -150,19 +214,6 @@ struct be_stats_obj { struct be_dma_mem cmd; }; -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 napi_struct napi; -}; - struct be_tx_obj { struct be_queue_info q; struct be_queue_info cq; @@ -225,8 +276,9 @@ struct be_adapter { u32 if_handle; /* Used to configure filtering */ u32 pmac_id; /* MAC addr handle used by BE card */ - struct be_link_info link; + bool link_up; u32 port_num; + bool promiscuous; }; extern struct ethtool_ops be_ethtool_ops; @@ -235,22 +287,6 @@ extern struct ethtool_ops be_ethtool_ops; #define BE_SET_NETDEV_OPS(netdev, ops) (netdev->netdev_ops = ops) -static inline u32 MODULO(u16 val, u16 limit) -{ - BUG_ON(limit & (limit - 1)); - return val & (limit - 1); -} - -static inline void index_adv(u16 *index, u16 val, u16 limit) -{ - *index = MODULO((*index + val), limit); -} - -static inline void index_inc(u16 *index, u16 limit) -{ - *index = MODULO((*index + 1), limit); -} - #define PAGE_SHIFT_4K 12 #define PAGE_SIZE_4K (1 << PAGE_SHIFT_4K) @@ -339,4 +375,6 @@ static inline u8 is_udp_pkt(struct sk_buff *skb) return val; } +extern void be_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, + u16 num_popped); #endif /* BE_H */ diff --git a/drivers/net/benet/be_cmds.c b/drivers/net/benet/be_cmds.c index d444aed962b..583517ed56f 100644 --- a/drivers/net/benet/be_cmds.c +++ b/drivers/net/benet/be_cmds.c @@ -17,6 +17,133 @@ #include "be.h" +static void be_mcc_notify(struct be_ctrl_info *ctrl) +{ + struct be_queue_info *mccq = &ctrl->mcc_obj.q; + u32 val = 0; + + val |= mccq->id & DB_MCCQ_RING_ID_MASK; + val |= 1 << DB_MCCQ_NUM_POSTED_SHIFT; + iowrite32(val, ctrl->db + DB_MCCQ_OFFSET); +} + +/* To check if valid bit is set, check the entire word as we don't know + * the endianness of the data (old entry is host endian while a new entry is + * little endian) */ +static inline bool be_mcc_compl_is_new(struct be_mcc_cq_entry *compl) +{ + if (compl->flags != 0) { + compl->flags = le32_to_cpu(compl->flags); + BUG_ON((compl->flags & CQE_FLAGS_VALID_MASK) == 0); + return true; + } else { + return false; + } +} + +/* Need to reset the entire word that houses the valid bit */ +static inline void be_mcc_compl_use(struct be_mcc_cq_entry *compl) +{ + compl->flags = 0; +} + +static int be_mcc_compl_process(struct be_ctrl_info *ctrl, + struct be_mcc_cq_entry *compl) +{ + u16 compl_status, extd_status; + + /* Just swap the status to host endian; mcc tag is opaquely copied + * from mcc_wrb */ + 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; + printk(KERN_WARNING DRV_NAME + " error in cmd completion: status(compl/extd)=%d/%d\n", + compl_status, extd_status); + return -1; + } + return 0; +} + +/* Link state evt is a string of bytes; no need for endian swapping */ +static void be_async_link_state_process(struct be_ctrl_info *ctrl, + struct be_async_event_link_state *evt) +{ + ctrl->async_cb(ctrl->adapter_ctxt, + evt->port_link_status == ASYNC_EVENT_LINK_UP ? true : false); +} + +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); +} + +static struct be_mcc_cq_entry *be_mcc_compl_get(struct be_ctrl_info *ctrl) +{ + struct be_queue_info *mcc_cq = &ctrl->mcc_obj.cq; + struct be_mcc_cq_entry *compl = queue_tail_node(mcc_cq); + + if (be_mcc_compl_is_new(compl)) { + queue_tail_inc(mcc_cq); + return compl; + } + return NULL; +} + +void be_process_mcc(struct be_ctrl_info *ctrl) +{ + struct be_mcc_cq_entry *compl; + int num = 0; + + spin_lock_bh(&ctrl->mcc_cq_lock); + while ((compl = be_mcc_compl_get(ctrl))) { + if (compl->flags & CQE_FLAGS_ASYNC_MASK) { + /* Interpret flags as an async trailer */ + BUG_ON(!is_link_state_evt(compl->flags)); + + /* Interpret compl as a async link evt */ + be_async_link_state_process(ctrl, + (struct be_async_event_link_state *) compl); + } else { + be_mcc_compl_process(ctrl, compl); + atomic_dec(&ctrl->mcc_obj.q.used); + } + be_mcc_compl_use(compl); + num++; + } + if (num) + be_cq_notify(ctrl, ctrl->mcc_obj.cq.id, true, num); + spin_unlock_bh(&ctrl->mcc_cq_lock); +} + +/* Wait till no more pending mcc requests are present */ +static void be_mcc_wait_compl(struct be_ctrl_info *ctrl) +{ +#define mcc_timeout 50000 /* 5s timeout */ + int i; + for (i = 0; i < mcc_timeout; i++) { + be_process_mcc(ctrl); + if (atomic_read(&ctrl->mcc_obj.q.used) == 0) + break; + udelay(100); + } + if (i == mcc_timeout) + printk(KERN_WARNING DRV_NAME "mcc poll timed out\n"); +} + +/* Notify MCC requests and wait for completion */ +static void be_mcc_notify_wait(struct be_ctrl_info *ctrl) +{ + be_mcc_notify(ctrl); + be_mcc_wait_compl(ctrl); +} + static int be_mbox_db_ready_wait(void __iomem *db) { int cnt = 0, wait = 5; @@ -44,11 +171,11 @@ static int be_mbox_db_ready_wait(void __iomem *db) /* * Insert the mailbox address into the doorbell in two steps + * Polls on the mbox doorbell till a command completion (or a timeout) occurs */ static int be_mbox_db_ring(struct be_ctrl_info *ctrl) { int status; - u16 compl_status, extd_status; u32 val = 0; void __iomem *db = ctrl->db + MPU_MAILBOX_DB_OFFSET; struct be_dma_mem *mbox_mem = &ctrl->mbox_mem; @@ -79,24 +206,17 @@ static int be_mbox_db_ring(struct be_ctrl_info *ctrl) if (status != 0) return status; - /* compl entry has been made now */ - be_dws_le_to_cpu(cqe, sizeof(*cqe)); - if (!(cqe->flags & CQE_FLAGS_VALID_MASK)) { - printk(KERN_WARNING DRV_NAME ": ERROR invalid mbox compl\n"); + /* A cq entry has been made now */ + if (be_mcc_compl_is_new(cqe)) { + status = be_mcc_compl_process(ctrl, &mbox->cqe); + be_mcc_compl_use(cqe); + if (status) + return status; + } else { + printk(KERN_WARNING DRV_NAME "invalid mailbox completion\n"); return -1; } - - compl_status = (cqe->status >> CQE_STATUS_COMPL_SHIFT) & - CQE_STATUS_COMPL_MASK; - if (compl_status != MCC_STATUS_SUCCESS) { - extd_status = (cqe->status >> CQE_STATUS_EXTD_SHIFT) & - CQE_STATUS_EXTD_MASK; - printk(KERN_WARNING DRV_NAME - ": ERROR in cmd compl. status(compl/extd)=%d/%d\n", - compl_status, extd_status); - } - - return compl_status; + return 0; } static int be_POST_stage_get(struct be_ctrl_info *ctrl, u16 *stage) @@ -235,6 +355,18 @@ static inline struct be_mcc_wrb *wrb_from_mbox(struct be_dma_mem *mbox_mem) return &((struct be_mcc_mailbox *)(mbox_mem->va))->wrb; } +static inline struct be_mcc_wrb *wrb_from_mcc(struct be_queue_info *mccq) +{ + struct be_mcc_wrb *wrb = NULL; + if (atomic_read(&mccq->used) < mccq->len) { + wrb = queue_head_node(mccq); + queue_head_inc(mccq); + atomic_inc(&mccq->used); + memset(wrb, 0, sizeof(*wrb)); + } + return wrb; +} + int be_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_queue_info *eq, int eq_delay) { @@ -244,7 +376,7 @@ int be_cmd_eq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem = &eq->dma_mem; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -272,7 +404,7 @@ int be_cmd_eq_create(struct be_ctrl_info *ctrl, eq->id = le16_to_cpu(resp->eq_id); eq->created = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -284,7 +416,7 @@ int be_cmd_mac_addr_query(struct be_ctrl_info *ctrl, u8 *mac_addr, struct be_cmd_resp_mac_query *resp = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -304,7 +436,7 @@ int be_cmd_mac_addr_query(struct be_ctrl_info *ctrl, u8 *mac_addr, if (!status) memcpy(mac_addr, resp->mac.addr, ETH_ALEN); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -315,7 +447,7 @@ int be_cmd_pmac_add(struct be_ctrl_info *ctrl, u8 *mac_addr, struct be_cmd_req_pmac_add *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -332,7 +464,7 @@ int be_cmd_pmac_add(struct be_ctrl_info *ctrl, u8 *mac_addr, *pmac_id = le32_to_cpu(resp->pmac_id); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -342,7 +474,7 @@ int be_cmd_pmac_del(struct be_ctrl_info *ctrl, u32 if_id, u32 pmac_id) struct be_cmd_req_pmac_del *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -354,7 +486,7 @@ int be_cmd_pmac_del(struct be_ctrl_info *ctrl, u32 if_id, u32 pmac_id) req->pmac_id = cpu_to_le32(pmac_id); status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -370,7 +502,7 @@ int be_cmd_cq_create(struct be_ctrl_info *ctrl, void *ctxt = &req->context; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -388,7 +520,7 @@ int be_cmd_cq_create(struct be_ctrl_info *ctrl, 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, 0); + AMAP_SET_BITS(struct amap_cq_context, armed, ctxt, 1); AMAP_SET_BITS(struct amap_cq_context, func, ctxt, ctrl->pci_func); be_dws_cpu_to_le(ctxt, sizeof(req->context)); @@ -399,7 +531,56 @@ int be_cmd_cq_create(struct be_ctrl_info *ctrl, cq->id = le16_to_cpu(resp->cq_id); cq->created = true; } - spin_unlock(&ctrl->cmd_lock); + 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 be_cmd_mccq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *mccq, + struct be_queue_info *cq) +{ + struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); + struct be_cmd_req_mcc_create *req = embedded_payload(wrb); + struct be_dma_mem *q_mem = &mccq->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_MCC_CREATE, sizeof(*req)); + + req->num_pages = PAGES_4K_SPANNED(q_mem->va, q_mem->size); + + AMAP_SET_BITS(struct amap_mcc_context, fid, ctxt, ctrl->pci_func); + AMAP_SET_BITS(struct amap_mcc_context, valid, ctxt, 1); + AMAP_SET_BITS(struct amap_mcc_context, ring_size, ctxt, + be_encoded_q_len(mccq->len)); + AMAP_SET_BITS(struct amap_mcc_context, cq_id, 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_db_ring(ctrl); + if (!status) { + struct be_cmd_resp_mcc_create *resp = embedded_payload(wrb); + mccq->id = le16_to_cpu(resp->id); + mccq->created = true; + } + spin_unlock(&ctrl->mbox_lock); return status; } @@ -415,7 +596,7 @@ int be_cmd_txq_create(struct be_ctrl_info *ctrl, int status; u32 len_encoded; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -446,7 +627,7 @@ int be_cmd_txq_create(struct be_ctrl_info *ctrl, txq->id = le16_to_cpu(resp->cid); txq->created = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -460,7 +641,7 @@ int be_cmd_rxq_create(struct be_ctrl_info *ctrl, struct be_dma_mem *q_mem = &rxq->dma_mem; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -482,7 +663,7 @@ int be_cmd_rxq_create(struct be_ctrl_info *ctrl, rxq->id = le16_to_cpu(resp->id); rxq->created = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -496,7 +677,7 @@ int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, u8 subsys = 0, opcode = 0; int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -518,6 +699,10 @@ int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, subsys = CMD_SUBSYSTEM_ETH; opcode = OPCODE_ETH_RX_DESTROY; break; + case QTYPE_MCCQ: + subsys = CMD_SUBSYSTEM_COMMON; + opcode = OPCODE_COMMON_MCC_DESTROY; + break; default: printk(KERN_WARNING DRV_NAME ":bad Q type in Q destroy cmd\n"); status = -1; @@ -528,7 +713,7 @@ int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, status = be_mbox_db_ring(ctrl); err: - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -541,7 +726,7 @@ int be_cmd_if_create(struct be_ctrl_info *ctrl, u32 flags, u8 *mac, struct be_cmd_req_if_create *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -562,7 +747,7 @@ int be_cmd_if_create(struct be_ctrl_info *ctrl, u32 flags, u8 *mac, *pmac_id = le32_to_cpu(resp->pmac_id); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -572,7 +757,7 @@ int be_cmd_if_destroy(struct be_ctrl_info *ctrl, u32 interface_id) struct be_cmd_req_if_destroy *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -583,7 +768,7 @@ int be_cmd_if_destroy(struct be_ctrl_info *ctrl, u32 interface_id) req->interface_id = cpu_to_le32(interface_id); status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -598,7 +783,7 @@ int be_cmd_get_stats(struct be_ctrl_info *ctrl, struct be_dma_mem *nonemb_cmd) struct be_sge *sge = nonembedded_sgl(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); memset(req, 0, sizeof(*req)); @@ -617,18 +802,20 @@ int be_cmd_get_stats(struct be_ctrl_info *ctrl, struct be_dma_mem *nonemb_cmd) be_dws_le_to_cpu(&resp->hw_stats, sizeof(resp->hw_stats)); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } int be_cmd_link_status_query(struct be_ctrl_info *ctrl, - struct be_link_info *link) + bool *link_up) { struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); struct be_cmd_req_link_status *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); + + *link_up = false; memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -639,14 +826,11 @@ int be_cmd_link_status_query(struct be_ctrl_info *ctrl, status = be_mbox_db_ring(ctrl); if (!status) { struct be_cmd_resp_link_status *resp = embedded_payload(wrb); - link->speed = resp->mac_speed; - link->duplex = resp->mac_duplex; - link->fault = resp->mac_fault; - } else { - link->speed = PHY_LINK_SPEED_ZERO; + if (resp->mac_speed != PHY_LINK_SPEED_ZERO) + *link_up = true; } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -656,7 +840,7 @@ int be_cmd_get_fw_ver(struct be_ctrl_info *ctrl, char *fw_ver) struct be_cmd_req_get_fw_version *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -670,7 +854,7 @@ int be_cmd_get_fw_ver(struct be_ctrl_info *ctrl, char *fw_ver) strncpy(fw_ver, resp->firmware_version_string, FW_VER_LEN); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -681,7 +865,7 @@ int be_cmd_modify_eqd(struct be_ctrl_info *ctrl, u32 eq_id, u32 eqd) struct be_cmd_req_modify_eq_delay *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -696,7 +880,7 @@ int be_cmd_modify_eqd(struct be_ctrl_info *ctrl, u32 eq_id, u32 eqd) status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -707,7 +891,7 @@ int be_cmd_vlan_config(struct be_ctrl_info *ctrl, u32 if_id, u16 *vtag_array, struct be_cmd_req_vlan_config *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -726,18 +910,22 @@ int be_cmd_vlan_config(struct be_ctrl_info *ctrl, u32 if_id, u16 *vtag_array, status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } +/* Use MCC for this command as it may be called in BH context */ int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en) { - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_cmd_req_promiscuous_config *req = embedded_payload(wrb); - int status; + struct be_mcc_wrb *wrb; + struct be_cmd_req_promiscuous_config *req; - spin_lock(&ctrl->cmd_lock); - memset(wrb, 0, sizeof(*wrb)); + spin_lock_bh(&ctrl->mcc_lock); + + wrb = wrb_from_mcc(&ctrl->mcc_obj.q); + BUG_ON(!wrb); + + req = embedded_payload(wrb); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -749,21 +937,29 @@ int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en) else req->port0_promiscuous = en; - status = be_mbox_db_ring(ctrl); + be_mcc_notify_wait(ctrl); - spin_unlock(&ctrl->cmd_lock); - return status; + spin_unlock_bh(&ctrl->mcc_lock); + return 0; } -int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, - u32 num, bool promiscuous) +/* + * Use MCC for this command as it may be called in BH context + * (mc == NULL) => multicast promiscous + */ +int be_cmd_multicast_set(struct be_ctrl_info *ctrl, u32 if_id, + struct dev_mc_list *mc_list, u32 mc_count) { - struct be_mcc_wrb *wrb = wrb_from_mbox(&ctrl->mbox_mem); - struct be_cmd_req_mcast_mac_config *req = embedded_payload(wrb); - int status; +#define BE_MAX_MC 32 /* set mcast promisc if > 32 */ + struct be_mcc_wrb *wrb; + struct be_cmd_req_mcast_mac_config *req; - spin_lock(&ctrl->cmd_lock); - memset(wrb, 0, sizeof(*wrb)); + spin_lock_bh(&ctrl->mcc_lock); + + wrb = wrb_from_mcc(&ctrl->mcc_obj.q); + BUG_ON(!wrb); + + req = embedded_payload(wrb); be_wrb_hdr_prepare(wrb, sizeof(*req), true, 0); @@ -771,17 +967,23 @@ int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, u8 *mac_table, OPCODE_COMMON_NTWK_MULTICAST_SET, sizeof(*req)); req->interface_id = if_id; - req->promiscuous = promiscuous; - if (!promiscuous) { - req->num_mac = cpu_to_le16(num); - if (num) - memcpy(req->mac, mac_table, ETH_ALEN * num); + if (mc_list && mc_count <= BE_MAX_MC) { + int i; + struct dev_mc_list *mc; + + req->num_mac = cpu_to_le16(mc_count); + + for (mc = mc_list, i = 0; mc; mc = mc->next, i++) + memcpy(req->mac[i].byte, mc->dmi_addr, ETH_ALEN); + } else { + req->promiscuous = 1; } - status = be_mbox_db_ring(ctrl); + be_mcc_notify_wait(ctrl); - spin_unlock(&ctrl->cmd_lock); - return status; + spin_unlock_bh(&ctrl->mcc_lock); + + return 0; } int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc) @@ -790,7 +992,7 @@ int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc) struct be_cmd_req_set_flow_control *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -804,7 +1006,7 @@ int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc) status = be_mbox_db_ring(ctrl); - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -814,7 +1016,7 @@ int be_cmd_get_flow_control(struct be_ctrl_info *ctrl, u32 *tx_fc, u32 *rx_fc) struct be_cmd_req_get_flow_control *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -831,7 +1033,7 @@ int be_cmd_get_flow_control(struct be_ctrl_info *ctrl, u32 *tx_fc, u32 *rx_fc) *rx_fc = le16_to_cpu(resp->rx_flow_control); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } @@ -841,7 +1043,7 @@ int be_cmd_query_fw_cfg(struct be_ctrl_info *ctrl, u32 *port_num) struct be_cmd_req_query_fw_cfg *req = embedded_payload(wrb); int status; - spin_lock(&ctrl->cmd_lock); + spin_lock(&ctrl->mbox_lock); memset(wrb, 0, sizeof(*wrb)); @@ -856,6 +1058,6 @@ int be_cmd_query_fw_cfg(struct be_ctrl_info *ctrl, u32 *port_num) *port_num = le32_to_cpu(resp->phys_port); } - spin_unlock(&ctrl->cmd_lock); + spin_unlock(&ctrl->mbox_lock); return status; } diff --git a/drivers/net/benet/be_cmds.h b/drivers/net/benet/be_cmds.h index e499e2d5b8c..747626da7b4 100644 --- a/drivers/net/benet/be_cmds.h +++ b/drivers/net/benet/be_cmds.h @@ -76,6 +76,34 @@ struct be_mcc_cq_entry { u32 flags; /* dword 3 */ }; +/* 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_cq_entry cqe; @@ -101,6 +129,7 @@ struct be_mcc_mailbox { #define OPCODE_COMMON_FIRMWARE_CONFIG 42 #define OPCODE_COMMON_NTWK_INTERFACE_CREATE 50 #define OPCODE_COMMON_NTWK_INTERFACE_DESTROY 51 +#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 @@ -269,6 +298,38 @@ struct be_cmd_resp_cq_create { 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; + /******************** Create TxQ ***************************/ #define BE_ETH_TX_RING_TYPE_STANDARD 2 #define BE_ULP1_NUM 1 @@ -341,7 +402,8 @@ enum { QTYPE_EQ = 1, QTYPE_CQ, QTYPE_TXQ, - QTYPE_RXQ + QTYPE_RXQ, + QTYPE_MCCQ }; struct be_cmd_req_q_destroy { @@ -546,12 +608,6 @@ struct be_cmd_req_link_status { u32 rsvd; }; -struct be_link_info { - u8 duplex; - u8 speed; - u8 fault; -}; - enum { PHY_LINK_DUPLEX_NONE = 0x0, PHY_LINK_DUPLEX_HALF = 0x1, @@ -657,6 +713,9 @@ extern int be_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); +extern int be_cmd_mccq_create(struct be_ctrl_info *ctrl, + struct be_queue_info *mccq, + struct be_queue_info *cq); extern int be_cmd_txq_create(struct be_ctrl_info *ctrl, struct be_queue_info *txq, struct be_queue_info *cq); @@ -667,7 +726,7 @@ extern int be_cmd_rxq_create(struct be_ctrl_info *ctrl, extern int be_cmd_q_destroy(struct be_ctrl_info *ctrl, struct be_queue_info *q, int type); extern int be_cmd_link_status_query(struct be_ctrl_info *ctrl, - struct be_link_info *link); + bool *link_up); extern int be_cmd_reset(struct be_ctrl_info *ctrl); extern int be_cmd_get_stats(struct be_ctrl_info *ctrl, struct be_dma_mem *nonemb_cmd); @@ -679,10 +738,11 @@ extern int be_cmd_vlan_config(struct be_ctrl_info *ctrl, u32 if_id, bool promiscuous); extern int be_cmd_promiscuous_config(struct be_ctrl_info *ctrl, u8 port_num, bool en); -extern int be_cmd_mcast_mac_set(struct be_ctrl_info *ctrl, u32 if_id, - u8 *mac_table, u32 num, bool promiscuous); +extern int be_cmd_multicast_set(struct be_ctrl_info *ctrl, u32 if_id, + struct dev_mc_list *mc_list, u32 mc_count); extern int be_cmd_set_flow_control(struct be_ctrl_info *ctrl, u32 tx_fc, u32 rx_fc); extern int be_cmd_get_flow_control(struct be_ctrl_info *ctrl, u32 *tx_fc, u32 *rx_fc); extern int be_cmd_query_fw_cfg(struct be_ctrl_info *ctrl, u32 *port_num); +extern void be_process_mcc(struct be_ctrl_info *ctrl); diff --git a/drivers/net/benet/be_hw.h b/drivers/net/benet/be_hw.h index b132aa4893c..b02e805c1db 100644 --- a/drivers/net/benet/be_hw.h +++ b/drivers/net/benet/be_hw.h @@ -61,7 +61,7 @@ /* 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 */ +#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 */ @@ -88,6 +88,12 @@ /* Number of rx frags posted */ #define DB_RQ_NUM_POSTED_SHIFT (24) /* bits 24 - 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 */ + /* * BE descriptors: host memory data structures whose formats * are hardwired in BE silicon. diff --git a/drivers/net/benet/be_main.c b/drivers/net/benet/be_main.c index 66bb56874d9..66c10c87f51 100644 --- a/drivers/net/benet/be_main.c +++ b/drivers/net/benet/be_main.c @@ -60,26 +60,6 @@ static int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q, return 0; } -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); -} - static void be_intr_set(struct be_ctrl_info *ctrl, bool enable) { u8 __iomem *addr = ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET; @@ -127,7 +107,7 @@ static void be_eq_notify(struct be_ctrl_info *ctrl, u16 qid, iowrite32(val, ctrl->db + DB_EQ_OFFSET); } -static void be_cq_notify(struct be_ctrl_info *ctrl, u16 qid, +void be_cq_notify(struct be_ctrl_info *ctrl, u16 qid, bool arm, u16 num_popped) { u32 val = 0; @@ -234,28 +214,24 @@ static void netdev_stats_update(struct be_adapter *adapter) dev_stats->tx_window_errors = 0; } -static void be_link_status_update(struct be_adapter *adapter) +void be_link_status_update(void *ctxt, bool link_up) { - struct be_link_info *prev = &adapter->link; - struct be_link_info now = { 0 }; + struct be_adapter *adapter = ctxt; struct net_device *netdev = adapter->netdev; - be_cmd_link_status_query(&adapter->ctrl, &now); - /* If link came up or went down */ - if (now.speed != prev->speed && (now.speed == PHY_LINK_SPEED_ZERO || - prev->speed == PHY_LINK_SPEED_ZERO)) { - if (now.speed == PHY_LINK_SPEED_ZERO) { - netif_stop_queue(netdev); - netif_carrier_off(netdev); - printk(KERN_INFO "%s: Link down\n", netdev->name); - } else { + if (adapter->link_up != link_up) { + if (link_up) { netif_start_queue(netdev); netif_carrier_on(netdev); printk(KERN_INFO "%s: Link up\n", netdev->name); + } else { + netif_stop_queue(netdev); + netif_carrier_off(netdev); + printk(KERN_INFO "%s: Link down\n", netdev->name); } + adapter->link_up = link_up; } - *prev = now; } /* Update the EQ delay n BE based on the RX frags consumed / sec */ @@ -569,47 +545,32 @@ static void be_vlan_rem_vid(struct net_device *netdev, u16 vid) be_vid_config(netdev); } -static void be_set_multicast_filter(struct net_device *netdev) +static void be_set_multicast_list(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); - struct dev_mc_list *mc_ptr; - u8 mac_addr[32][ETH_ALEN]; - int i = 0; + struct be_ctrl_info *ctrl = &adapter->ctrl; - if (netdev->flags & IFF_ALLMULTI) { - /* set BE in Multicast promiscuous */ - be_cmd_mcast_mac_set(&adapter->ctrl, - adapter->if_handle, NULL, 0, true); - return; + if (netdev->flags & IFF_PROMISC) { + be_cmd_promiscuous_config(ctrl, adapter->port_num, 1); + adapter->promiscuous = true; + goto done; } - for (mc_ptr = netdev->mc_list; mc_ptr; mc_ptr = mc_ptr->next) { - memcpy(&mac_addr[i][0], mc_ptr->dmi_addr, ETH_ALEN); - if (++i >= 32) { - be_cmd_mcast_mac_set(&adapter->ctrl, - adapter->if_handle, &mac_addr[0][0], i, false); - i = 0; - } - + /* BE was previously in promiscous mode; disable it */ + if (adapter->promiscuous) { + adapter->promiscuous = false; + be_cmd_promiscuous_config(ctrl, adapter->port_num, 0); } - if (i) { - /* reset the promiscuous mode also. */ - be_cmd_mcast_mac_set(&adapter->ctrl, - adapter->if_handle, &mac_addr[0][0], i, false); + if (netdev->flags & IFF_ALLMULTI) { + be_cmd_multicast_set(ctrl, adapter->if_handle, NULL, 0); + goto done; } -} -static void be_set_multicast_list(struct net_device *netdev) -{ - struct be_adapter *adapter = netdev_priv(netdev); - - if (netdev->flags & IFF_PROMISC) { - be_cmd_promiscuous_config(&adapter->ctrl, adapter->port_num, 1); - } else { - be_cmd_promiscuous_config(&adapter->ctrl, adapter->port_num, 0); - be_set_multicast_filter(netdev); - } + be_cmd_multicast_set(ctrl, adapter->if_handle, netdev->mc_list, + netdev->mc_count); +done: + return; } static void be_rx_rate_update(struct be_adapter *adapter) @@ -960,10 +921,8 @@ static void be_post_rx_frags(struct be_adapter *adapter) return; } -static struct be_eth_tx_compl * -be_tx_compl_get(struct be_adapter *adapter) +static struct be_eth_tx_compl *be_tx_compl_get(struct be_queue_info *tx_cq) { - struct be_queue_info *tx_cq = &adapter->tx_obj.cq; struct be_eth_tx_compl *txcp = queue_tail_node(tx_cq); if (txcp->dw[offsetof(struct amap_eth_tx_compl, valid) / 32] == 0) @@ -1051,6 +1010,59 @@ static void be_tx_q_clean(struct be_adapter *adapter) } } +static void be_mcc_queues_destroy(struct be_adapter *adapter) +{ + struct be_queue_info *q; + struct be_ctrl_info *ctrl = &adapter->ctrl; + + q = &ctrl->mcc_obj.q; + if (q->created) + be_cmd_q_destroy(ctrl, q, QTYPE_MCCQ); + be_queue_free(adapter, q); + + q = &ctrl->mcc_obj.cq; + if (q->created) + be_cmd_q_destroy(ctrl, q, QTYPE_CQ); + be_queue_free(adapter, q); +} + +/* Must be called only after TX qs are created as MCC shares TX EQ */ +static int be_mcc_queues_create(struct be_adapter *adapter) +{ + struct be_queue_info *q, *cq; + struct be_ctrl_info *ctrl = &adapter->ctrl; + + /* Alloc MCC compl queue */ + cq = &ctrl->mcc_obj.cq; + if (be_queue_alloc(adapter, cq, MCC_CQ_LEN, + sizeof(struct be_mcc_cq_entry))) + goto err; + + /* Ask BE to create MCC compl queue; share TX's eq */ + if (be_cmd_cq_create(ctrl, cq, &adapter->tx_eq.q, false, true, 0)) + goto mcc_cq_free; + + /* Alloc MCC queue */ + q = &ctrl->mcc_obj.q; + if (be_queue_alloc(adapter, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb))) + goto mcc_cq_destroy; + + /* Ask BE to create MCC queue */ + if (be_cmd_mccq_create(ctrl, q, cq)) + goto mcc_q_free; + + return 0; + +mcc_q_free: + be_queue_free(adapter, q); +mcc_cq_destroy: + be_cmd_q_destroy(ctrl, cq, QTYPE_CQ); +mcc_cq_free: + be_queue_free(adapter, cq); +err: + return -1; +} + static void be_tx_queues_destroy(struct be_adapter *adapter) { struct be_queue_info *q; @@ -1263,7 +1275,7 @@ static irqreturn_t be_msix_rx(int irq, void *dev) return IRQ_HANDLED; } -static irqreturn_t be_msix_tx(int irq, void *dev) +static irqreturn_t be_msix_tx_mcc(int irq, void *dev) { struct be_adapter *adapter = dev; @@ -1324,40 +1336,51 @@ int be_poll_rx(struct napi_struct *napi, int budget) return work_done; } -/* For TX we don't honour budget; consume everything */ -int be_poll_tx(struct napi_struct *napi, int budget) +void be_process_tx(struct be_adapter *adapter) { - struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi); - struct be_adapter *adapter = - container_of(tx_eq, struct be_adapter, tx_eq); - struct be_tx_obj *tx_obj = &adapter->tx_obj; - struct be_queue_info *tx_cq = &tx_obj->cq; - struct be_queue_info *txq = &tx_obj->q; + struct be_queue_info *txq = &adapter->tx_obj.q; + struct be_queue_info *tx_cq = &adapter->tx_obj.cq; struct be_eth_tx_compl *txcp; u32 num_cmpl = 0; u16 end_idx; - while ((txcp = be_tx_compl_get(adapter))) { + while ((txcp = be_tx_compl_get(tx_cq))) { end_idx = AMAP_GET_BITS(struct amap_eth_tx_compl, wrb_index, txcp); be_tx_compl_process(adapter, end_idx); num_cmpl++; } - /* As Tx wrbs have been freed up, wake up netdev queue if - * it was stopped due to lack of tx wrbs. - */ - if (netif_queue_stopped(adapter->netdev) && + if (num_cmpl) { + be_cq_notify(&adapter->ctrl, tx_cq->id, true, num_cmpl); + + /* As Tx wrbs have been freed up, wake up netdev queue if + * it was stopped due to lack of tx wrbs. + */ + if (netif_queue_stopped(adapter->netdev) && atomic_read(&txq->used) < txq->len / 2) { - netif_wake_queue(adapter->netdev); + netif_wake_queue(adapter->netdev); + } + + drvr_stats(adapter)->be_tx_events++; + drvr_stats(adapter)->be_tx_compl += num_cmpl; } +} + +/* As TX and MCC share the same EQ check for both TX and MCC completions. + * For TX/MCC we don't honour budget; consume everything + */ +static int be_poll_tx_mcc(struct napi_struct *napi, int budget) +{ + struct be_eq_obj *tx_eq = container_of(napi, struct be_eq_obj, napi); + struct be_adapter *adapter = + container_of(tx_eq, struct be_adapter, tx_eq); napi_complete(napi); - be_cq_notify(&adapter->ctrl, tx_cq->id, true, num_cmpl); + be_process_tx(adapter); - drvr_stats(adapter)->be_tx_events++; - drvr_stats(adapter)->be_tx_compl += num_cmpl; + be_process_mcc(&adapter->ctrl); return 1; } @@ -1368,9 +1391,6 @@ static void be_worker(struct work_struct *work) container_of(work, struct be_adapter, work.work); int status; - /* Check link */ - be_link_status_update(adapter); - /* Get Stats */ status = be_cmd_get_stats(&adapter->ctrl, &adapter->stats.cmd); if (!status) @@ -1419,7 +1439,7 @@ static int be_msix_register(struct be_adapter *adapter) sprintf(tx_eq->desc, "%s-tx", netdev->name); vec = be_msix_vec_get(adapter, tx_eq->q.id); - status = request_irq(vec, be_msix_tx, 0, tx_eq->desc, adapter); + status = request_irq(vec, be_msix_tx_mcc, 0, tx_eq->desc, adapter); if (status) goto err; @@ -1495,6 +1515,39 @@ static int be_open(struct net_device *netdev) struct be_ctrl_info *ctrl = &adapter->ctrl; struct be_eq_obj *rx_eq = &adapter->rx_eq; struct be_eq_obj *tx_eq = &adapter->tx_eq; + bool link_up; + int status; + + /* First time posting */ + be_post_rx_frags(adapter); + + napi_enable(&rx_eq->napi); + napi_enable(&tx_eq->napi); + + be_irq_register(adapter); + + be_intr_set(ctrl, true); + + /* The evt queues are created in unarmed state; arm them */ + be_eq_notify(ctrl, rx_eq->q.id, true, false, 0); + be_eq_notify(ctrl, tx_eq->q.id, true, false, 0); + + /* Rx compl queue may be in unarmed state; rearm it */ + be_cq_notify(ctrl, adapter->rx_obj.cq.id, true, 0); + + status = be_cmd_link_status_query(ctrl, &link_up); + if (status) + return status; + be_link_status_update(adapter, link_up); + + schedule_delayed_work(&adapter->work, msecs_to_jiffies(100)); + return 0; +} + +static int be_setup(struct be_adapter *adapter) +{ + struct be_ctrl_info *ctrl = &adapter->ctrl; + struct net_device *netdev = adapter->netdev; u32 if_flags; int status; @@ -1521,29 +1574,14 @@ static int be_open(struct net_device *netdev) if (status != 0) goto tx_qs_destroy; - /* First time posting */ - be_post_rx_frags(adapter); - - napi_enable(&rx_eq->napi); - napi_enable(&tx_eq->napi); - - be_irq_register(adapter); - - be_intr_set(ctrl, true); - - /* The evt queues are created in the unarmed state; arm them */ - be_eq_notify(ctrl, rx_eq->q.id, true, false, 0); - be_eq_notify(ctrl, tx_eq->q.id, true, false, 0); - - /* The compl queues are created in the unarmed state; arm them */ - be_cq_notify(ctrl, adapter->rx_obj.cq.id, true, 0); - be_cq_notify(ctrl, adapter->tx_obj.cq.id, true, 0); - - be_link_status_update(adapter); + status = be_mcc_queues_create(adapter); + if (status != 0) + goto rx_qs_destroy; - schedule_delayed_work(&adapter->work, msecs_to_jiffies(100)); return 0; +rx_qs_destroy: + be_rx_queues_destroy(adapter); tx_qs_destroy: be_tx_queues_destroy(adapter); if_destroy: @@ -1552,6 +1590,19 @@ do_none: return status; } +static int be_clear(struct be_adapter *adapter) +{ + struct be_ctrl_info *ctrl = &adapter->ctrl; + + be_rx_queues_destroy(adapter); + be_tx_queues_destroy(adapter); + + be_cmd_if_destroy(ctrl, adapter->if_handle); + + be_mcc_queues_destroy(adapter); + return 0; +} + static int be_close(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); @@ -1564,7 +1615,7 @@ static int be_close(struct net_device *netdev) netif_stop_queue(netdev); netif_carrier_off(netdev); - adapter->link.speed = PHY_LINK_SPEED_ZERO; + adapter->link_up = false; be_intr_set(ctrl, false); @@ -1581,10 +1632,6 @@ static int be_close(struct net_device *netdev) napi_disable(&rx_eq->napi); napi_disable(&tx_eq->napi); - be_rx_queues_destroy(adapter); - be_tx_queues_destroy(adapter); - - be_cmd_if_destroy(ctrl, adapter->if_handle); return 0; } @@ -1673,7 +1720,7 @@ static void be_netdev_init(struct net_device *netdev) netif_napi_add(netdev, &adapter->rx_eq.napi, be_poll_rx, BE_NAPI_WEIGHT); - netif_napi_add(netdev, &adapter->tx_eq.napi, be_poll_tx, + netif_napi_add(netdev, &adapter->tx_eq.napi, be_poll_tx_mcc, BE_NAPI_WEIGHT); netif_carrier_off(netdev); @@ -1755,7 +1802,12 @@ static int be_ctrl_init(struct be_adapter *adapter) 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->cmd_lock); + spin_lock_init(&ctrl->mbox_lock); + spin_lock_init(&ctrl->mcc_lock); + spin_lock_init(&ctrl->mcc_cq_lock); + + ctrl->async_cb = be_link_status_update; + ctrl->adapter_ctxt = adapter; val = ioread32(ctrl->pcicfg + PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET); ctrl->pci_func = (val >> MEMBAR_CTRL_INT_CTRL_PFUNC_SHIFT) & @@ -1793,6 +1845,8 @@ static void __devexit be_remove(struct pci_dev *pdev) unregister_netdev(adapter->netdev); + be_clear(adapter); + be_stats_cleanup(adapter); be_ctrl_cleanup(adapter); @@ -1890,13 +1944,18 @@ static int __devinit be_probe(struct pci_dev *pdev, be_netdev_init(netdev); SET_NETDEV_DEV(netdev, &adapter->pdev->dev); + status = be_setup(adapter); + if (status) + goto stats_clean; status = register_netdev(netdev); if (status != 0) - goto stats_clean; + goto unsetup; dev_info(&pdev->dev, "%s port %d\n", nic_name(pdev), adapter->port_num); return 0; +unsetup: + be_clear(adapter); stats_clean: be_stats_cleanup(adapter); ctrl_clean: @@ -1921,6 +1980,7 @@ static int be_suspend(struct pci_dev *pdev, pm_message_t state) if (netif_running(netdev)) { rtnl_lock(); be_close(netdev); + be_clear(adapter); rtnl_unlock(); } @@ -1947,6 +2007,7 @@ static int be_resume(struct pci_dev *pdev) if (netif_running(netdev)) { rtnl_lock(); + be_setup(adapter); be_open(netdev); rtnl_unlock(); } diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 38f1c3375d7..b70cc99962f 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -6825,6 +6825,14 @@ bnx2_nway_reset(struct net_device *dev) return 0; } +static u32 +bnx2_get_link(struct net_device *dev) +{ + struct bnx2 *bp = netdev_priv(dev); + + return bp->link_up; +} + static int bnx2_get_eeprom_len(struct net_device *dev) { @@ -7392,7 +7400,7 @@ static const struct ethtool_ops bnx2_ethtool_ops = { .get_wol = bnx2_get_wol, .set_wol = bnx2_set_wol, .nway_reset = bnx2_nway_reset, - .get_link = ethtool_op_get_link, + .get_link = bnx2_get_link, .get_eeprom_len = bnx2_get_eeprom_len, .get_eeprom = bnx2_get_eeprom, .set_eeprom = bnx2_set_eeprom, diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d5e18812bf4..33821a81cbf 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -36,7 +36,7 @@ config CAN_CALC_BITTIMING If unsure, say Y. config CAN_SJA1000 - depends on CAN_DEV + depends on CAN_DEV && HAS_IOMEM tristate "Philips SJA1000" ---help--- Driver for the SJA1000 CAN controllers from Philips or NXP diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 44f77eb1180..4d1515f45ba 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -25,8 +25,6 @@ #include <linux/delay.h> #include <linux/ethtool.h> #include <linux/if_vlan.h> -#include <linux/module.h> - #if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE) #define BCM_VLAN 1 #endif @@ -2521,9 +2519,9 @@ static struct cnic_dev *init_bnx2_cnic(struct net_device *dev) struct cnic_dev *cdev; struct cnic_local *cp; struct cnic_eth_dev *ethdev = NULL; - struct cnic_eth_dev *(*probe)(void *) = NULL; + struct cnic_eth_dev *(*probe)(struct net_device *) = NULL; - probe = __symbol_get("bnx2_cnic_probe"); + probe = symbol_get(bnx2_cnic_probe); if (probe) { ethdev = (*probe)(dev); symbol_put_addr(probe); diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h index 06380963a34..d1bce27ee99 100644 --- a/drivers/net/cnic_if.h +++ b/drivers/net/cnic_if.h @@ -296,4 +296,6 @@ extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops); extern int cnic_unregister_driver(int ulp_type); +extern struct cnic_eth_dev *bnx2_cnic_probe(struct net_device *dev); + #endif diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 677f60490f6..679885a122b 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -1997,7 +1997,7 @@ static int e1000_clean(struct napi_struct *napi, int budget) struct e1000_adapter *adapter = container_of(napi, struct e1000_adapter, napi); struct e1000_hw *hw = &adapter->hw; struct net_device *poll_dev = adapter->netdev; - int tx_cleaned = 0, work_done = 0; + int tx_cleaned = 1, work_done = 0; adapter = netdev_priv(poll_dev); diff --git a/drivers/net/mlx4/en_netdev.c b/drivers/net/mlx4/en_netdev.c index e02bafdd368..93f4abd990a 100644 --- a/drivers/net/mlx4/en_netdev.c +++ b/drivers/net/mlx4/en_netdev.c @@ -668,7 +668,7 @@ int mlx4_en_start_port(struct net_device *dev) queue_work(mdev->workqueue, &priv->mcast_task); priv->port_up = true; - netif_start_queue(dev); + netif_tx_start_all_queues(dev); return 0; mac_err: @@ -700,14 +700,14 @@ void mlx4_en_stop_port(struct net_device *dev) en_dbg(DRV, priv, "stop port called while port already down\n"); return; } - netif_stop_queue(dev); /* Synchronize with tx routine */ netif_tx_lock_bh(dev); - priv->port_up = false; + netif_tx_stop_all_queues(dev); netif_tx_unlock_bh(dev); /* close port*/ + priv->port_up = false; mlx4_CLOSE_PORT(mdev->dev, priv->port); /* Unregister Mac address for the port */ @@ -881,7 +881,6 @@ void mlx4_en_destroy_netdev(struct net_device *dev) mlx4_free_hwq_res(mdev->dev, &priv->res, MLX4_EN_PAGE_SIZE); cancel_delayed_work(&priv->stats_task); - cancel_delayed_work(&priv->refill_task); /* flush any pending task for this netdev */ flush_workqueue(mdev->workqueue); @@ -986,7 +985,6 @@ int mlx4_en_init_netdev(struct mlx4_en_dev *mdev, int port, spin_lock_init(&priv->stats_lock); INIT_WORK(&priv->mcast_task, mlx4_en_do_set_multicast); INIT_WORK(&priv->mac_task, mlx4_en_do_set_mac); - INIT_DELAYED_WORK(&priv->refill_task, mlx4_en_rx_refill); INIT_WORK(&priv->watchdog_task, mlx4_en_restart); INIT_WORK(&priv->linkstate_task, mlx4_en_linkstate); INIT_DELAYED_WORK(&priv->stats_task, mlx4_en_do_get_stats); diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c index 5a14899c1e2..91bdfdfd431 100644 --- a/drivers/net/mlx4/en_rx.c +++ b/drivers/net/mlx4/en_rx.c @@ -269,31 +269,6 @@ reduce_rings: return 0; } -static int mlx4_en_fill_rx_buf(struct net_device *dev, - struct mlx4_en_rx_ring *ring) -{ - struct mlx4_en_priv *priv = netdev_priv(dev); - int num = 0; - int err; - - while ((u32) (ring->prod - ring->cons) < ring->actual_size) { - err = mlx4_en_prepare_rx_desc(priv, ring, ring->prod & - ring->size_mask); - if (err) { - if (netif_msg_rx_err(priv)) - en_warn(priv, "Failed preparing rx descriptor\n"); - priv->port_stats.rx_alloc_failed++; - break; - } - ++num; - ++ring->prod; - } - if ((u32) (ring->prod - ring->cons) == ring->actual_size) - ring->full = 1; - - return num; -} - static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring) { @@ -312,42 +287,6 @@ static void mlx4_en_free_rx_buf(struct mlx4_en_priv *priv, } } - -void mlx4_en_rx_refill(struct work_struct *work) -{ - struct delayed_work *delay = to_delayed_work(work); - struct mlx4_en_priv *priv = container_of(delay, struct mlx4_en_priv, - refill_task); - struct mlx4_en_dev *mdev = priv->mdev; - struct net_device *dev = priv->dev; - struct mlx4_en_rx_ring *ring; - int need_refill = 0; - int i; - - mutex_lock(&mdev->state_lock); - if (!mdev->device_up || !priv->port_up) - goto out; - - /* We only get here if there are no receive buffers, so we can't race - * with Rx interrupts while filling buffers */ - for (i = 0; i < priv->rx_ring_num; i++) { - ring = &priv->rx_ring[i]; - if (ring->need_refill) { - if (mlx4_en_fill_rx_buf(dev, ring)) { - ring->need_refill = 0; - mlx4_en_update_rx_prod_db(ring); - } else - need_refill = 1; - } - } - if (need_refill) - queue_delayed_work(mdev->workqueue, &priv->refill_task, HZ); - -out: - mutex_unlock(&mdev->state_lock); -} - - int mlx4_en_create_rx_ring(struct mlx4_en_priv *priv, struct mlx4_en_rx_ring *ring, u32 size, u16 stride) { @@ -457,9 +396,6 @@ int mlx4_en_activate_rx_rings(struct mlx4_en_priv *priv) ring_ind--; goto err_allocator; } - - /* Fill Rx buffers */ - ring->full = 0; } err = mlx4_en_fill_rx_buffers(priv); if (err) @@ -647,33 +583,6 @@ static struct sk_buff *mlx4_en_rx_skb(struct mlx4_en_priv *priv, return skb; } -static void mlx4_en_copy_desc(struct mlx4_en_priv *priv, - struct mlx4_en_rx_ring *ring, - int from, int to, int num) -{ - struct skb_frag_struct *skb_frags_from; - struct skb_frag_struct *skb_frags_to; - struct mlx4_en_rx_desc *rx_desc_from; - struct mlx4_en_rx_desc *rx_desc_to; - int from_index, to_index; - int nr, i; - - for (i = 0; i < num; i++) { - from_index = (from + i) & ring->size_mask; - to_index = (to + i) & ring->size_mask; - skb_frags_from = ring->rx_info + (from_index << priv->log_rx_info); - skb_frags_to = ring->rx_info + (to_index << priv->log_rx_info); - rx_desc_from = ring->buf + (from_index << ring->log_stride); - rx_desc_to = ring->buf + (to_index << ring->log_stride); - - for (nr = 0; nr < priv->num_frags; nr++) { - skb_frags_to[nr].page = skb_frags_from[nr].page; - skb_frags_to[nr].page_offset = skb_frags_from[nr].page_offset; - rx_desc_to->data[nr].addr = rx_desc_from->data[nr].addr; - } - } -} - int mlx4_en_process_rx_cq(struct net_device *dev, struct mlx4_en_cq *cq, int budget) { @@ -821,11 +730,6 @@ out: wmb(); /* ensure HW sees CQ consumer before we post new buffers */ ring->cons = cq->mcq.cons_index; ring->prod += polled; /* Polled descriptors were realocated in place */ - if (unlikely(!ring->full)) { - mlx4_en_copy_desc(priv, ring, ring->cons - polled, - ring->prod - polled, polled); - mlx4_en_fill_rx_buf(dev, ring); - } mlx4_en_update_rx_prod_db(ring); return polled; } diff --git a/drivers/net/mlx4/en_tx.c b/drivers/net/mlx4/en_tx.c index 5dc7466ad03..08c43f2ae72 100644 --- a/drivers/net/mlx4/en_tx.c +++ b/drivers/net/mlx4/en_tx.c @@ -515,16 +515,9 @@ static int get_real_size(struct sk_buff *skb, struct net_device *dev, else { if (netif_msg_tx_err(priv)) en_warn(priv, "Non-linear headers\n"); - dev_kfree_skb_any(skb); return 0; } } - if (unlikely(*lso_header_size > MAX_LSO_HDR_SIZE)) { - if (netif_msg_tx_err(priv)) - en_warn(priv, "LSO header size too big\n"); - dev_kfree_skb_any(skb); - return 0; - } } else { *lso_header_size = 0; if (!is_inline(skb, NULL)) @@ -616,13 +609,9 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) int lso_header_size; void *fragptr; - if (unlikely(!skb->len)) { - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } real_size = get_real_size(skb, dev, &lso_header_size); if (unlikely(!real_size)) - return NETDEV_TX_OK; + goto tx_drop; /* Allign descriptor to TXBB size */ desc_size = ALIGN(real_size, TXBB_SIZE); @@ -630,8 +619,7 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) if (unlikely(nr_txbb > MAX_DESC_TXBBS)) { if (netif_msg_tx_err(priv)) en_warn(priv, "Oversized header or SG list\n"); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; + goto tx_drop; } tx_ind = skb->queue_mapping; @@ -653,14 +641,6 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) return NETDEV_TX_BUSY; } - /* Now that we know what Tx ring to use */ - if (unlikely(!priv->port_up)) { - if (netif_msg_tx_err(priv)) - en_warn(priv, "xmit: port down!\n"); - dev_kfree_skb_any(skb); - return NETDEV_TX_OK; - } - /* Track current inflight packets for performance analysis */ AVG_PERF_COUNTER(priv->pstats.inflight_avg, (u32) (ring->prod - ring->cons - 1)); @@ -785,5 +765,10 @@ int mlx4_en_xmit(struct sk_buff *skb, struct net_device *dev) mlx4_en_xmit_poll(priv, tx_ind); return 0; + +tx_drop: + dev_kfree_skb_any(skb); + priv->stats.tx_dropped++; + return NETDEV_TX_OK; } diff --git a/drivers/net/mlx4/mlx4_en.h b/drivers/net/mlx4/mlx4_en.h index d43a9e4c2ae..c7c5e86804f 100644 --- a/drivers/net/mlx4/mlx4_en.h +++ b/drivers/net/mlx4/mlx4_en.h @@ -99,7 +99,6 @@ #define RSS_FACTOR 2 #define TXBB_SIZE 64 #define HEADROOM (2048 / TXBB_SIZE + 1) -#define MAX_LSO_HDR_SIZE 92 #define STAMP_STRIDE 64 #define STAMP_DWORDS (STAMP_STRIDE / 4) #define STAMP_SHIFT 31 @@ -296,8 +295,6 @@ struct mlx4_en_rx_ring { u32 prod; u32 cons; u32 buf_size; - int need_refill; - int full; void *buf; void *rx_info; unsigned long bytes; @@ -495,7 +492,6 @@ struct mlx4_en_priv { struct mlx4_en_cq rx_cq[MAX_RX_RINGS]; struct work_struct mcast_task; struct work_struct mac_task; - struct delayed_work refill_task; struct work_struct watchdog_task; struct work_struct linkstate_task; struct delayed_work stats_task; @@ -565,7 +561,6 @@ void mlx4_en_set_default_rss_map(struct mlx4_en_priv *priv, int mlx4_en_config_rss_steer(struct mlx4_en_priv *priv); void mlx4_en_release_rss_steer(struct mlx4_en_priv *priv); int mlx4_en_free_tx_buf(struct net_device *dev, struct mlx4_en_tx_ring *ring); -void mlx4_en_rx_refill(struct work_struct *work); void mlx4_en_rx_irq(struct mlx4_cq *mcq); int mlx4_SET_MCAST_FLTR(struct mlx4_dev *dev, u8 port, u64 mac, u64 clear, u8 mode); diff --git a/drivers/net/mlx4/mr.c b/drivers/net/mlx4/mr.c index 5887e4764d2..f96948be0a4 100644 --- a/drivers/net/mlx4/mr.c +++ b/drivers/net/mlx4/mr.c @@ -399,11 +399,14 @@ static int mlx4_write_mtt_chunk(struct mlx4_dev *dev, struct mlx4_mtt *mtt, if (!mtts) return -ENOMEM; + dma_sync_single_for_cpu(&dev->pdev->dev, dma_handle, + npages * sizeof (u64), DMA_TO_DEVICE); + for (i = 0; i < npages; ++i) mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); - dma_sync_single_for_cpu(&dev->pdev->dev, dma_handle, - npages * sizeof (u64), DMA_TO_DEVICE); + dma_sync_single_for_device(&dev->pdev->dev, dma_handle, + npages * sizeof (u64), DMA_TO_DEVICE); return 0; } @@ -547,11 +550,14 @@ int mlx4_map_phys_fmr(struct mlx4_dev *dev, struct mlx4_fmr *fmr, u64 *page_list /* Make sure MPT status is visible before writing MTT entries */ wmb(); + dma_sync_single_for_cpu(&dev->pdev->dev, fmr->dma_handle, + npages * sizeof(u64), DMA_TO_DEVICE); + for (i = 0; i < npages; ++i) fmr->mtts[i] = cpu_to_be64(page_list[i] | MLX4_MTT_FLAG_PRESENT); - dma_sync_single_for_cpu(&dev->pdev->dev, fmr->dma_handle, - npages * sizeof(u64), DMA_TO_DEVICE); + dma_sync_single_for_device(&dev->pdev->dev, fmr->dma_handle, + npages * sizeof(u64), DMA_TO_DEVICE); fmr->mpt->key = cpu_to_be32(key); fmr->mpt->lkey = cpu_to_be32(key); diff --git a/drivers/net/mv643xx_eth.c b/drivers/net/mv643xx_eth.c index 745ae8b4a2e..0f32db3e92a 100644 --- a/drivers/net/mv643xx_eth.c +++ b/drivers/net/mv643xx_eth.c @@ -1750,12 +1750,12 @@ static void mv643xx_eth_program_unicast_filter(struct net_device *dev) uc_addr_set(mp, dev->dev_addr); - port_config = rdlp(mp, PORT_CONFIG); + port_config = rdlp(mp, PORT_CONFIG) & ~UNICAST_PROMISCUOUS_MODE; + nibbles = uc_addr_filter_mask(dev); if (!nibbles) { port_config |= UNICAST_PROMISCUOUS_MODE; - wrlp(mp, PORT_CONFIG, port_config); - return; + nibbles = 0xffff; } for (i = 0; i < 16; i += 4) { @@ -1776,7 +1776,6 @@ static void mv643xx_eth_program_unicast_filter(struct net_device *dev) wrl(mp, off, v); } - port_config &= ~UNICAST_PROMISCUOUS_MODE; wrlp(mp, PORT_CONFIG, port_config); } diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index bdb143d2b5c..055bb61d6e7 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -944,28 +944,31 @@ int netxen_phantom_init(struct netxen_adapter *adapter, int pegtune_val) u32 val = 0; int retries = 60; - if (!pegtune_val) { - do { - val = NXRD32(adapter, CRB_CMDPEG_STATE); + if (pegtune_val) + return 0; - if (val == PHAN_INITIALIZE_COMPLETE || - val == PHAN_INITIALIZE_ACK) - return 0; + do { + val = NXRD32(adapter, CRB_CMDPEG_STATE); - msleep(500); + switch (val) { + case PHAN_INITIALIZE_COMPLETE: + case PHAN_INITIALIZE_ACK: + return 0; + case PHAN_INITIALIZE_FAILED: + goto out_err; + default: + break; + } - } while (--retries); + msleep(500); - if (!retries) { - pegtune_val = NXRD32(adapter, - NETXEN_ROMUSB_GLB_PEGTUNE_DONE); - printk(KERN_WARNING "netxen_phantom_init: init failed, " - "pegtune_val=%x\n", pegtune_val); - return -1; - } - } + } while (--retries); - return 0; + NXWR32(adapter, CRB_CMDPEG_STATE, PHAN_INITIALIZE_FAILED); + +out_err: + dev_warn(&adapter->pdev->dev, "firmware init failed\n"); + return -EIO; } static int diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 71daa3d5f11..2919a2d12bf 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -705,7 +705,7 @@ netxen_start_firmware(struct netxen_adapter *adapter, int request_fw) first_driver = (adapter->ahw.pci_func == 0); if (!first_driver) - return 0; + goto wait_init; first_boot = NXRD32(adapter, NETXEN_CAM_RAM(0x1fc)); @@ -752,6 +752,7 @@ netxen_start_firmware(struct netxen_adapter *adapter, int request_fw) | (_NETXEN_NIC_LINUX_SUBVERSION); NXWR32(adapter, CRB_DRIVER_VERSION, val); +wait_init: /* Handshake with the card before we register the devices. */ err = netxen_phantom_init(adapter, NETXEN_NIC_PEG_TUNE); if (err) { @@ -1178,6 +1179,7 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) free_netdev(netdev); } +#ifdef CONFIG_PM static int netxen_nic_suspend(struct pci_dev *pdev, pm_message_t state) { @@ -1242,6 +1244,7 @@ netxen_nic_resume(struct pci_dev *pdev) return 0; } +#endif static int netxen_nic_open(struct net_device *netdev) { @@ -1771,8 +1774,10 @@ static struct pci_driver netxen_driver = { .id_table = netxen_pci_tbl, .probe = netxen_nic_probe, .remove = __devexit_p(netxen_nic_remove), +#ifdef CONFIG_PM .suspend = netxen_nic_suspend, .resume = netxen_nic_resume +#endif }; /* Driver Registration on NetXen card */ diff --git a/drivers/net/ppp_async.c b/drivers/net/ppp_async.c index 6de8399d6dd..17c116bb332 100644 --- a/drivers/net/ppp_async.c +++ b/drivers/net/ppp_async.c @@ -356,7 +356,6 @@ ppp_asynctty_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); ap_put(ap); - tty_unthrottle(tty); } static void diff --git a/drivers/net/ppp_synctty.c b/drivers/net/ppp_synctty.c index d2fa2db1358..aa3d39f38e2 100644 --- a/drivers/net/ppp_synctty.c +++ b/drivers/net/ppp_synctty.c @@ -397,7 +397,6 @@ ppp_sync_receive(struct tty_struct *tty, const unsigned char *buf, if (!skb_queue_empty(&ap->rqueue)) tasklet_schedule(&ap->tsk); sp_put(ap); - tty_unthrottle(tty); } static void diff --git a/drivers/net/qla3xxx.c b/drivers/net/qla3xxx.c index 8a823ecc99a..3e4b67aaa6e 100644 --- a/drivers/net/qla3xxx.c +++ b/drivers/net/qla3xxx.c @@ -3142,6 +3142,7 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) (void __iomem *)port_regs; u32 delay = 10; int status = 0; + unsigned long hw_flags = 0; if(ql_mii_setup(qdev)) return -1; @@ -3150,7 +3151,8 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) ql_write_common_reg(qdev, &port_regs->CommonRegs.serialPortInterfaceReg, (ISP_SERIAL_PORT_IF_WE | (ISP_SERIAL_PORT_IF_WE << 16))); - + /* Give the PHY time to come out of reset. */ + mdelay(100); qdev->port_link_state = LS_DOWN; netif_carrier_off(qdev->ndev); @@ -3350,7 +3352,9 @@ static int ql_adapter_initialize(struct ql3_adapter *qdev) value = ql_read_page0_reg(qdev, &port_regs->portStatus); if (value & PORT_STATUS_IC) break; + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); msleep(500); + spin_lock_irqsave(&qdev->hw_lock, hw_flags); } while (--delay); if (delay == 0) { @@ -3837,7 +3841,9 @@ static void ql_reset_work(struct work_struct *work) 16) | ISP_CONTROL_RI)); } + spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); ssleep(1); + spin_lock_irqsave(&qdev->hw_lock, hw_flags); } while (--max_wait_time); spin_unlock_irqrestore(&qdev->hw_lock, hw_flags); diff --git a/drivers/net/r8169.c b/drivers/net/r8169.c index 4e22462684c..4b53b58d75f 100644 --- a/drivers/net/r8169.c +++ b/drivers/net/r8169.c @@ -51,9 +51,6 @@ #define TX_BUFFS_AVAIL(tp) \ (tp->dirty_tx + NUM_TX_DESC - tp->cur_tx - 1) -/* Maximum events (Rx packets, etc.) to handle at each interrupt. */ -static const int max_interrupt_work = 20; - /* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). The RTL chips use a 64 element hash table based on the Ethernet CRC. */ static const int multicast_filter_limit = 32; diff --git a/drivers/net/s6gmac.c b/drivers/net/s6gmac.c new file mode 100644 index 00000000000..5345e47b35a --- /dev/null +++ b/drivers/net/s6gmac.c @@ -0,0 +1,1073 @@ +/* + * Ethernet driver for S6105 on chip network device + * (c)2008 emlix GmbH http://www.emlix.com + * Authors: Oskar Schirmer <os@emlix.com> + * Daniel Gloeckner <dg@emlix.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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/netdevice.h> +#include <linux/etherdevice.h> +#include <linux/if.h> +#include <linux/stddef.h> +#include <linux/mii.h> +#include <linux/phy.h> +#include <linux/platform_device.h> +#include <variant/hardware.h> +#include <variant/dmac.h> + +#define DRV_NAME "s6gmac" +#define DRV_PRMT DRV_NAME ": " + + +/* register declarations */ + +#define S6_GMAC_MACCONF1 0x000 +#define S6_GMAC_MACCONF1_TXENA 0 +#define S6_GMAC_MACCONF1_SYNCTX 1 +#define S6_GMAC_MACCONF1_RXENA 2 +#define S6_GMAC_MACCONF1_SYNCRX 3 +#define S6_GMAC_MACCONF1_TXFLOWCTRL 4 +#define S6_GMAC_MACCONF1_RXFLOWCTRL 5 +#define S6_GMAC_MACCONF1_LOOPBACK 8 +#define S6_GMAC_MACCONF1_RESTXFUNC 16 +#define S6_GMAC_MACCONF1_RESRXFUNC 17 +#define S6_GMAC_MACCONF1_RESTXMACCTRL 18 +#define S6_GMAC_MACCONF1_RESRXMACCTRL 19 +#define S6_GMAC_MACCONF1_SIMULRES 30 +#define S6_GMAC_MACCONF1_SOFTRES 31 +#define S6_GMAC_MACCONF2 0x004 +#define S6_GMAC_MACCONF2_FULL 0 +#define S6_GMAC_MACCONF2_CRCENA 1 +#define S6_GMAC_MACCONF2_PADCRCENA 2 +#define S6_GMAC_MACCONF2_LENGTHFCHK 4 +#define S6_GMAC_MACCONF2_HUGEFRAMENA 5 +#define S6_GMAC_MACCONF2_IFMODE 8 +#define S6_GMAC_MACCONF2_IFMODE_NIBBLE 1 +#define S6_GMAC_MACCONF2_IFMODE_BYTE 2 +#define S6_GMAC_MACCONF2_IFMODE_MASK 3 +#define S6_GMAC_MACCONF2_PREAMBLELEN 12 +#define S6_GMAC_MACCONF2_PREAMBLELEN_MASK 0x0F +#define S6_GMAC_MACIPGIFG 0x008 +#define S6_GMAC_MACIPGIFG_B2BINTERPGAP 0 +#define S6_GMAC_MACIPGIFG_B2BINTERPGAP_MASK 0x7F +#define S6_GMAC_MACIPGIFG_MINIFGENFORCE 8 +#define S6_GMAC_MACIPGIFG_B2BINTERPGAP2 16 +#define S6_GMAC_MACIPGIFG_B2BINTERPGAP1 24 +#define S6_GMAC_MACHALFDUPLEX 0x00C +#define S6_GMAC_MACHALFDUPLEX_COLLISWIN 0 +#define S6_GMAC_MACHALFDUPLEX_COLLISWIN_MASK 0x3F +#define S6_GMAC_MACHALFDUPLEX_RETXMAX 12 +#define S6_GMAC_MACHALFDUPLEX_RETXMAX_MASK 0x0F +#define S6_GMAC_MACHALFDUPLEX_EXCESSDEF 16 +#define S6_GMAC_MACHALFDUPLEX_NOBACKOFF 17 +#define S6_GMAC_MACHALFDUPLEX_BPNOBCKOF 18 +#define S6_GMAC_MACHALFDUPLEX_ALTBEBENA 19 +#define S6_GMAC_MACHALFDUPLEX_ALTBEBTRN 20 +#define S6_GMAC_MACHALFDUPLEX_ALTBEBTR_MASK 0x0F +#define S6_GMAC_MACMAXFRAMELEN 0x010 +#define S6_GMAC_MACMIICONF 0x020 +#define S6_GMAC_MACMIICONF_CSEL 0 +#define S6_GMAC_MACMIICONF_CSEL_DIV10 0 +#define S6_GMAC_MACMIICONF_CSEL_DIV12 1 +#define S6_GMAC_MACMIICONF_CSEL_DIV14 2 +#define S6_GMAC_MACMIICONF_CSEL_DIV18 3 +#define S6_GMAC_MACMIICONF_CSEL_DIV24 4 +#define S6_GMAC_MACMIICONF_CSEL_DIV34 5 +#define S6_GMAC_MACMIICONF_CSEL_DIV68 6 +#define S6_GMAC_MACMIICONF_CSEL_DIV168 7 +#define S6_GMAC_MACMIICONF_CSEL_MASK 7 +#define S6_GMAC_MACMIICONF_PREAMBLESUPR 4 +#define S6_GMAC_MACMIICONF_SCANAUTOINCR 5 +#define S6_GMAC_MACMIICMD 0x024 +#define S6_GMAC_MACMIICMD_READ 0 +#define S6_GMAC_MACMIICMD_SCAN 1 +#define S6_GMAC_MACMIIADDR 0x028 +#define S6_GMAC_MACMIIADDR_REG 0 +#define S6_GMAC_MACMIIADDR_REG_MASK 0x1F +#define S6_GMAC_MACMIIADDR_PHY 8 +#define S6_GMAC_MACMIIADDR_PHY_MASK 0x1F +#define S6_GMAC_MACMIICTRL 0x02C +#define S6_GMAC_MACMIISTAT 0x030 +#define S6_GMAC_MACMIIINDI 0x034 +#define S6_GMAC_MACMIIINDI_BUSY 0 +#define S6_GMAC_MACMIIINDI_SCAN 1 +#define S6_GMAC_MACMIIINDI_INVAL 2 +#define S6_GMAC_MACINTERFSTAT 0x03C +#define S6_GMAC_MACINTERFSTAT_LINKFAIL 3 +#define S6_GMAC_MACINTERFSTAT_EXCESSDEF 9 +#define S6_GMAC_MACSTATADDR1 0x040 +#define S6_GMAC_MACSTATADDR2 0x044 + +#define S6_GMAC_FIFOCONF0 0x048 +#define S6_GMAC_FIFOCONF0_HSTRSTWT 0 +#define S6_GMAC_FIFOCONF0_HSTRSTSR 1 +#define S6_GMAC_FIFOCONF0_HSTRSTFR 2 +#define S6_GMAC_FIFOCONF0_HSTRSTST 3 +#define S6_GMAC_FIFOCONF0_HSTRSTFT 4 +#define S6_GMAC_FIFOCONF0_WTMENREQ 8 +#define S6_GMAC_FIFOCONF0_SRFENREQ 9 +#define S6_GMAC_FIFOCONF0_FRFENREQ 10 +#define S6_GMAC_FIFOCONF0_STFENREQ 11 +#define S6_GMAC_FIFOCONF0_FTFENREQ 12 +#define S6_GMAC_FIFOCONF0_WTMENRPLY 16 +#define S6_GMAC_FIFOCONF0_SRFENRPLY 17 +#define S6_GMAC_FIFOCONF0_FRFENRPLY 18 +#define S6_GMAC_FIFOCONF0_STFENRPLY 19 +#define S6_GMAC_FIFOCONF0_FTFENRPLY 20 +#define S6_GMAC_FIFOCONF1 0x04C +#define S6_GMAC_FIFOCONF2 0x050 +#define S6_GMAC_FIFOCONF2_CFGLWM 0 +#define S6_GMAC_FIFOCONF2_CFGHWM 16 +#define S6_GMAC_FIFOCONF3 0x054 +#define S6_GMAC_FIFOCONF3_CFGFTTH 0 +#define S6_GMAC_FIFOCONF3_CFGHWMFT 16 +#define S6_GMAC_FIFOCONF4 0x058 +#define S6_GMAC_FIFOCONF_RSV_PREVDROP 0 +#define S6_GMAC_FIFOCONF_RSV_RUNT 1 +#define S6_GMAC_FIFOCONF_RSV_FALSECAR 2 +#define S6_GMAC_FIFOCONF_RSV_CODEERR 3 +#define S6_GMAC_FIFOCONF_RSV_CRCERR 4 +#define S6_GMAC_FIFOCONF_RSV_LENGTHERR 5 +#define S6_GMAC_FIFOCONF_RSV_LENRANGE 6 +#define S6_GMAC_FIFOCONF_RSV_OK 7 +#define S6_GMAC_FIFOCONF_RSV_MULTICAST 8 +#define S6_GMAC_FIFOCONF_RSV_BROADCAST 9 +#define S6_GMAC_FIFOCONF_RSV_DRIBBLE 10 +#define S6_GMAC_FIFOCONF_RSV_CTRLFRAME 11 +#define S6_GMAC_FIFOCONF_RSV_PAUSECTRL 12 +#define S6_GMAC_FIFOCONF_RSV_UNOPCODE 13 +#define S6_GMAC_FIFOCONF_RSV_VLANTAG 14 +#define S6_GMAC_FIFOCONF_RSV_LONGEVENT 15 +#define S6_GMAC_FIFOCONF_RSV_TRUNCATED 16 +#define S6_GMAC_FIFOCONF_RSV_MASK 0x3FFFF +#define S6_GMAC_FIFOCONF5 0x05C +#define S6_GMAC_FIFOCONF5_DROPLT64 18 +#define S6_GMAC_FIFOCONF5_CFGBYTM 19 +#define S6_GMAC_FIFOCONF5_RXDROPSIZE 20 +#define S6_GMAC_FIFOCONF5_RXDROPSIZE_MASK 0xF + +#define S6_GMAC_STAT_REGS 0x080 +#define S6_GMAC_STAT_SIZE_MIN 12 +#define S6_GMAC_STATTR64 0x080 +#define S6_GMAC_STATTR64_SIZE 18 +#define S6_GMAC_STATTR127 0x084 +#define S6_GMAC_STATTR127_SIZE 18 +#define S6_GMAC_STATTR255 0x088 +#define S6_GMAC_STATTR255_SIZE 18 +#define S6_GMAC_STATTR511 0x08C +#define S6_GMAC_STATTR511_SIZE 18 +#define S6_GMAC_STATTR1K 0x090 +#define S6_GMAC_STATTR1K_SIZE 18 +#define S6_GMAC_STATTRMAX 0x094 +#define S6_GMAC_STATTRMAX_SIZE 18 +#define S6_GMAC_STATTRMGV 0x098 +#define S6_GMAC_STATTRMGV_SIZE 18 +#define S6_GMAC_STATRBYT 0x09C +#define S6_GMAC_STATRBYT_SIZE 24 +#define S6_GMAC_STATRPKT 0x0A0 +#define S6_GMAC_STATRPKT_SIZE 18 +#define S6_GMAC_STATRFCS 0x0A4 +#define S6_GMAC_STATRFCS_SIZE 12 +#define S6_GMAC_STATRMCA 0x0A8 +#define S6_GMAC_STATRMCA_SIZE 18 +#define S6_GMAC_STATRBCA 0x0AC +#define S6_GMAC_STATRBCA_SIZE 22 +#define S6_GMAC_STATRXCF 0x0B0 +#define S6_GMAC_STATRXCF_SIZE 18 +#define S6_GMAC_STATRXPF 0x0B4 +#define S6_GMAC_STATRXPF_SIZE 12 +#define S6_GMAC_STATRXUO 0x0B8 +#define S6_GMAC_STATRXUO_SIZE 12 +#define S6_GMAC_STATRALN 0x0BC +#define S6_GMAC_STATRALN_SIZE 12 +#define S6_GMAC_STATRFLR 0x0C0 +#define S6_GMAC_STATRFLR_SIZE 16 +#define S6_GMAC_STATRCDE 0x0C4 +#define S6_GMAC_STATRCDE_SIZE 12 +#define S6_GMAC_STATRCSE 0x0C8 +#define S6_GMAC_STATRCSE_SIZE 12 +#define S6_GMAC_STATRUND 0x0CC +#define S6_GMAC_STATRUND_SIZE 12 +#define S6_GMAC_STATROVR 0x0D0 +#define S6_GMAC_STATROVR_SIZE 12 +#define S6_GMAC_STATRFRG 0x0D4 +#define S6_GMAC_STATRFRG_SIZE 12 +#define S6_GMAC_STATRJBR 0x0D8 +#define S6_GMAC_STATRJBR_SIZE 12 +#define S6_GMAC_STATRDRP 0x0DC +#define S6_GMAC_STATRDRP_SIZE 12 +#define S6_GMAC_STATTBYT 0x0E0 +#define S6_GMAC_STATTBYT_SIZE 24 +#define S6_GMAC_STATTPKT 0x0E4 +#define S6_GMAC_STATTPKT_SIZE 18 +#define S6_GMAC_STATTMCA 0x0E8 +#define S6_GMAC_STATTMCA_SIZE 18 +#define S6_GMAC_STATTBCA 0x0EC +#define S6_GMAC_STATTBCA_SIZE 18 +#define S6_GMAC_STATTXPF 0x0F0 +#define S6_GMAC_STATTXPF_SIZE 12 +#define S6_GMAC_STATTDFR 0x0F4 +#define S6_GMAC_STATTDFR_SIZE 12 +#define S6_GMAC_STATTEDF 0x0F8 +#define S6_GMAC_STATTEDF_SIZE 12 +#define S6_GMAC_STATTSCL 0x0FC +#define S6_GMAC_STATTSCL_SIZE 12 +#define S6_GMAC_STATTMCL 0x100 +#define S6_GMAC_STATTMCL_SIZE 12 +#define S6_GMAC_STATTLCL 0x104 +#define S6_GMAC_STATTLCL_SIZE 12 +#define S6_GMAC_STATTXCL 0x108 +#define S6_GMAC_STATTXCL_SIZE 12 +#define S6_GMAC_STATTNCL 0x10C +#define S6_GMAC_STATTNCL_SIZE 13 +#define S6_GMAC_STATTPFH 0x110 +#define S6_GMAC_STATTPFH_SIZE 12 +#define S6_GMAC_STATTDRP 0x114 +#define S6_GMAC_STATTDRP_SIZE 12 +#define S6_GMAC_STATTJBR 0x118 +#define S6_GMAC_STATTJBR_SIZE 12 +#define S6_GMAC_STATTFCS 0x11C +#define S6_GMAC_STATTFCS_SIZE 12 +#define S6_GMAC_STATTXCF 0x120 +#define S6_GMAC_STATTXCF_SIZE 12 +#define S6_GMAC_STATTOVR 0x124 +#define S6_GMAC_STATTOVR_SIZE 12 +#define S6_GMAC_STATTUND 0x128 +#define S6_GMAC_STATTUND_SIZE 12 +#define S6_GMAC_STATTFRG 0x12C +#define S6_GMAC_STATTFRG_SIZE 12 +#define S6_GMAC_STATCARRY(n) (0x130 + 4*(n)) +#define S6_GMAC_STATCARRYMSK(n) (0x138 + 4*(n)) +#define S6_GMAC_STATCARRY1_RDRP 0 +#define S6_GMAC_STATCARRY1_RJBR 1 +#define S6_GMAC_STATCARRY1_RFRG 2 +#define S6_GMAC_STATCARRY1_ROVR 3 +#define S6_GMAC_STATCARRY1_RUND 4 +#define S6_GMAC_STATCARRY1_RCSE 5 +#define S6_GMAC_STATCARRY1_RCDE 6 +#define S6_GMAC_STATCARRY1_RFLR 7 +#define S6_GMAC_STATCARRY1_RALN 8 +#define S6_GMAC_STATCARRY1_RXUO 9 +#define S6_GMAC_STATCARRY1_RXPF 10 +#define S6_GMAC_STATCARRY1_RXCF 11 +#define S6_GMAC_STATCARRY1_RBCA 12 +#define S6_GMAC_STATCARRY1_RMCA 13 +#define S6_GMAC_STATCARRY1_RFCS 14 +#define S6_GMAC_STATCARRY1_RPKT 15 +#define S6_GMAC_STATCARRY1_RBYT 16 +#define S6_GMAC_STATCARRY1_TRMGV 25 +#define S6_GMAC_STATCARRY1_TRMAX 26 +#define S6_GMAC_STATCARRY1_TR1K 27 +#define S6_GMAC_STATCARRY1_TR511 28 +#define S6_GMAC_STATCARRY1_TR255 29 +#define S6_GMAC_STATCARRY1_TR127 30 +#define S6_GMAC_STATCARRY1_TR64 31 +#define S6_GMAC_STATCARRY2_TDRP 0 +#define S6_GMAC_STATCARRY2_TPFH 1 +#define S6_GMAC_STATCARRY2_TNCL 2 +#define S6_GMAC_STATCARRY2_TXCL 3 +#define S6_GMAC_STATCARRY2_TLCL 4 +#define S6_GMAC_STATCARRY2_TMCL 5 +#define S6_GMAC_STATCARRY2_TSCL 6 +#define S6_GMAC_STATCARRY2_TEDF 7 +#define S6_GMAC_STATCARRY2_TDFR 8 +#define S6_GMAC_STATCARRY2_TXPF 9 +#define S6_GMAC_STATCARRY2_TBCA 10 +#define S6_GMAC_STATCARRY2_TMCA 11 +#define S6_GMAC_STATCARRY2_TPKT 12 +#define S6_GMAC_STATCARRY2_TBYT 13 +#define S6_GMAC_STATCARRY2_TFRG 14 +#define S6_GMAC_STATCARRY2_TUND 15 +#define S6_GMAC_STATCARRY2_TOVR 16 +#define S6_GMAC_STATCARRY2_TXCF 17 +#define S6_GMAC_STATCARRY2_TFCS 18 +#define S6_GMAC_STATCARRY2_TJBR 19 + +#define S6_GMAC_HOST_PBLKCTRL 0x140 +#define S6_GMAC_HOST_PBLKCTRL_TXENA 0 +#define S6_GMAC_HOST_PBLKCTRL_RXENA 1 +#define S6_GMAC_HOST_PBLKCTRL_TXSRES 2 +#define S6_GMAC_HOST_PBLKCTRL_RXSRES 3 +#define S6_GMAC_HOST_PBLKCTRL_TXBSIZ 8 +#define S6_GMAC_HOST_PBLKCTRL_RXBSIZ 12 +#define S6_GMAC_HOST_PBLKCTRL_SIZ_16 4 +#define S6_GMAC_HOST_PBLKCTRL_SIZ_32 5 +#define S6_GMAC_HOST_PBLKCTRL_SIZ_64 6 +#define S6_GMAC_HOST_PBLKCTRL_SIZ_128 7 +#define S6_GMAC_HOST_PBLKCTRL_SIZ_MASK 0xF +#define S6_GMAC_HOST_PBLKCTRL_STATENA 16 +#define S6_GMAC_HOST_PBLKCTRL_STATAUTOZ 17 +#define S6_GMAC_HOST_PBLKCTRL_STATCLEAR 18 +#define S6_GMAC_HOST_PBLKCTRL_RGMII 19 +#define S6_GMAC_HOST_INTMASK 0x144 +#define S6_GMAC_HOST_INTSTAT 0x148 +#define S6_GMAC_HOST_INT_TXBURSTOVER 3 +#define S6_GMAC_HOST_INT_TXPREWOVER 4 +#define S6_GMAC_HOST_INT_RXBURSTUNDER 5 +#define S6_GMAC_HOST_INT_RXPOSTRFULL 6 +#define S6_GMAC_HOST_INT_RXPOSTRUNDER 7 +#define S6_GMAC_HOST_RXFIFOHWM 0x14C +#define S6_GMAC_HOST_CTRLFRAMXP 0x150 +#define S6_GMAC_HOST_DSTADDRLO(n) (0x160 + 8*(n)) +#define S6_GMAC_HOST_DSTADDRHI(n) (0x164 + 8*(n)) +#define S6_GMAC_HOST_DSTMASKLO(n) (0x180 + 8*(n)) +#define S6_GMAC_HOST_DSTMASKHI(n) (0x184 + 8*(n)) + +#define S6_GMAC_BURST_PREWR 0x1B0 +#define S6_GMAC_BURST_PREWR_LEN 0 +#define S6_GMAC_BURST_PREWR_LEN_MASK ((1 << 20) - 1) +#define S6_GMAC_BURST_PREWR_CFE 20 +#define S6_GMAC_BURST_PREWR_PPE 21 +#define S6_GMAC_BURST_PREWR_FCS 22 +#define S6_GMAC_BURST_PREWR_PAD 23 +#define S6_GMAC_BURST_POSTRD 0x1D0 +#define S6_GMAC_BURST_POSTRD_LEN 0 +#define S6_GMAC_BURST_POSTRD_LEN_MASK ((1 << 20) - 1) +#define S6_GMAC_BURST_POSTRD_DROP 20 + + +/* data handling */ + +#define S6_NUM_TX_SKB 8 /* must be larger than TX fifo size */ +#define S6_NUM_RX_SKB 16 +#define S6_MAX_FRLEN 1536 + +struct s6gmac { + u32 reg; + u32 tx_dma; + u32 rx_dma; + u32 io; + u8 tx_chan; + u8 rx_chan; + spinlock_t lock; + u8 tx_skb_i, tx_skb_o; + u8 rx_skb_i, rx_skb_o; + struct sk_buff *tx_skb[S6_NUM_TX_SKB]; + struct sk_buff *rx_skb[S6_NUM_RX_SKB]; + unsigned long carry[sizeof(struct net_device_stats) / sizeof(long)]; + unsigned long stats[sizeof(struct net_device_stats) / sizeof(long)]; + struct phy_device *phydev; + struct { + struct mii_bus *bus; + int irq[PHY_MAX_ADDR]; + } mii; + struct { + unsigned int mbit; + u8 giga; + u8 isup; + u8 full; + } link; +}; + +static void s6gmac_rx_fillfifo(struct s6gmac *pd) +{ + struct sk_buff *skb; + while ((((u8)(pd->rx_skb_i - pd->rx_skb_o)) < S6_NUM_RX_SKB) + && (!s6dmac_fifo_full(pd->rx_dma, pd->rx_chan)) + && (skb = dev_alloc_skb(S6_MAX_FRLEN + 2))) { + pd->rx_skb[(pd->rx_skb_i++) % S6_NUM_RX_SKB] = skb; + s6dmac_put_fifo_cache(pd->rx_dma, pd->rx_chan, + pd->io, (u32)skb->data, S6_MAX_FRLEN); + } +} + +static void s6gmac_rx_interrupt(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + u32 pfx; + struct sk_buff *skb; + while (((u8)(pd->rx_skb_i - pd->rx_skb_o)) > + s6dmac_pending_count(pd->rx_dma, pd->rx_chan)) { + skb = pd->rx_skb[(pd->rx_skb_o++) % S6_NUM_RX_SKB]; + pfx = readl(pd->reg + S6_GMAC_BURST_POSTRD); + if (pfx & (1 << S6_GMAC_BURST_POSTRD_DROP)) { + dev_kfree_skb_irq(skb); + } else { + skb_put(skb, (pfx >> S6_GMAC_BURST_POSTRD_LEN) + & S6_GMAC_BURST_POSTRD_LEN_MASK); + skb->dev = dev; + skb->protocol = eth_type_trans(skb, dev); + skb->ip_summed = CHECKSUM_UNNECESSARY; + netif_rx(skb); + } + } +} + +static void s6gmac_tx_interrupt(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + while (((u8)(pd->tx_skb_i - pd->tx_skb_o)) > + s6dmac_pending_count(pd->tx_dma, pd->tx_chan)) { + dev_kfree_skb_irq(pd->tx_skb[(pd->tx_skb_o++) % S6_NUM_TX_SKB]); + } + if (!s6dmac_fifo_full(pd->tx_dma, pd->tx_chan)) + netif_wake_queue(dev); +} + +struct s6gmac_statinf { + unsigned reg_size : 4; /* 0: unused */ + unsigned reg_off : 6; + unsigned net_index : 6; +}; + +#define S6_STATS_B (8 * sizeof(u32)) +#define S6_STATS_C(b, r, f) [b] = { \ + BUILD_BUG_ON_ZERO(r##_SIZE < S6_GMAC_STAT_SIZE_MIN) + \ + BUILD_BUG_ON_ZERO((r##_SIZE - (S6_GMAC_STAT_SIZE_MIN - 1)) \ + >= (1<<4)) + \ + r##_SIZE - (S6_GMAC_STAT_SIZE_MIN - 1), \ + BUILD_BUG_ON_ZERO(((unsigned)((r - S6_GMAC_STAT_REGS) / sizeof(u32))) \ + >= ((1<<6)-1)) + \ + (r - S6_GMAC_STAT_REGS) / sizeof(u32), \ + BUILD_BUG_ON_ZERO((offsetof(struct net_device_stats, f)) \ + % sizeof(unsigned long)) + \ + BUILD_BUG_ON_ZERO((((unsigned)(offsetof(struct net_device_stats, f)) \ + / sizeof(unsigned long)) >= (1<<6))) + \ + BUILD_BUG_ON_ZERO((sizeof(((struct net_device_stats *)0)->f) \ + != sizeof(unsigned long))) + \ + (offsetof(struct net_device_stats, f)) / sizeof(unsigned long)}, + +static const struct s6gmac_statinf statinf[2][S6_STATS_B] = { { + S6_STATS_C(S6_GMAC_STATCARRY1_RBYT, S6_GMAC_STATRBYT, rx_bytes) + S6_STATS_C(S6_GMAC_STATCARRY1_RPKT, S6_GMAC_STATRPKT, rx_packets) + S6_STATS_C(S6_GMAC_STATCARRY1_RFCS, S6_GMAC_STATRFCS, rx_crc_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_RMCA, S6_GMAC_STATRMCA, multicast) + S6_STATS_C(S6_GMAC_STATCARRY1_RALN, S6_GMAC_STATRALN, rx_frame_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_RFLR, S6_GMAC_STATRFLR, rx_length_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_RCDE, S6_GMAC_STATRCDE, rx_missed_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_RUND, S6_GMAC_STATRUND, rx_length_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_ROVR, S6_GMAC_STATROVR, rx_length_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_RFRG, S6_GMAC_STATRFRG, rx_crc_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_RJBR, S6_GMAC_STATRJBR, rx_crc_errors) + S6_STATS_C(S6_GMAC_STATCARRY1_RDRP, S6_GMAC_STATRDRP, rx_dropped) +}, { + S6_STATS_C(S6_GMAC_STATCARRY2_TBYT, S6_GMAC_STATTBYT, tx_bytes) + S6_STATS_C(S6_GMAC_STATCARRY2_TPKT, S6_GMAC_STATTPKT, tx_packets) + S6_STATS_C(S6_GMAC_STATCARRY2_TEDF, S6_GMAC_STATTEDF, tx_aborted_errors) + S6_STATS_C(S6_GMAC_STATCARRY2_TXCL, S6_GMAC_STATTXCL, tx_aborted_errors) + S6_STATS_C(S6_GMAC_STATCARRY2_TNCL, S6_GMAC_STATTNCL, collisions) + S6_STATS_C(S6_GMAC_STATCARRY2_TDRP, S6_GMAC_STATTDRP, tx_dropped) + S6_STATS_C(S6_GMAC_STATCARRY2_TJBR, S6_GMAC_STATTJBR, tx_errors) + S6_STATS_C(S6_GMAC_STATCARRY2_TFCS, S6_GMAC_STATTFCS, tx_errors) + S6_STATS_C(S6_GMAC_STATCARRY2_TOVR, S6_GMAC_STATTOVR, tx_errors) + S6_STATS_C(S6_GMAC_STATCARRY2_TUND, S6_GMAC_STATTUND, tx_errors) + S6_STATS_C(S6_GMAC_STATCARRY2_TFRG, S6_GMAC_STATTFRG, tx_errors) +} }; + +static void s6gmac_stats_collect(struct s6gmac *pd, + const struct s6gmac_statinf *inf) +{ + int b; + for (b = 0; b < S6_STATS_B; b++) { + if (inf[b].reg_size) { + pd->stats[inf[b].net_index] += + readl(pd->reg + S6_GMAC_STAT_REGS + + sizeof(u32) * inf[b].reg_off); + } + } +} + +static void s6gmac_stats_carry(struct s6gmac *pd, + const struct s6gmac_statinf *inf, u32 mask) +{ + int b; + while (mask) { + b = fls(mask) - 1; + mask &= ~(1 << b); + pd->carry[inf[b].net_index] += (1 << inf[b].reg_size); + } +} + +static inline u32 s6gmac_stats_pending(struct s6gmac *pd, int carry) +{ + int r = readl(pd->reg + S6_GMAC_STATCARRY(carry)) & + ~readl(pd->reg + S6_GMAC_STATCARRYMSK(carry)); + return r; +} + +static inline void s6gmac_stats_interrupt(struct s6gmac *pd, int carry) +{ + u32 mask; + mask = s6gmac_stats_pending(pd, carry); + if (mask) { + writel(mask, pd->reg + S6_GMAC_STATCARRY(carry)); + s6gmac_stats_carry(pd, &statinf[carry][0], mask); + } +} + +static irqreturn_t s6gmac_interrupt(int irq, void *dev_id) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct s6gmac *pd = netdev_priv(dev); + if (!dev) + return IRQ_NONE; + spin_lock(&pd->lock); + if (s6dmac_termcnt_irq(pd->rx_dma, pd->rx_chan)) + s6gmac_rx_interrupt(dev); + s6gmac_rx_fillfifo(pd); + if (s6dmac_termcnt_irq(pd->tx_dma, pd->tx_chan)) + s6gmac_tx_interrupt(dev); + s6gmac_stats_interrupt(pd, 0); + s6gmac_stats_interrupt(pd, 1); + spin_unlock(&pd->lock); + return IRQ_HANDLED; +} + +static inline void s6gmac_set_dstaddr(struct s6gmac *pd, int n, + u32 addrlo, u32 addrhi, u32 masklo, u32 maskhi) +{ + writel(addrlo, pd->reg + S6_GMAC_HOST_DSTADDRLO(n)); + writel(addrhi, pd->reg + S6_GMAC_HOST_DSTADDRHI(n)); + writel(masklo, pd->reg + S6_GMAC_HOST_DSTMASKLO(n)); + writel(maskhi, pd->reg + S6_GMAC_HOST_DSTMASKHI(n)); +} + +static inline void s6gmac_stop_device(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + writel(0, pd->reg + S6_GMAC_MACCONF1); +} + +static inline void s6gmac_init_device(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + int is_rgmii = !!(pd->phydev->supported + & (SUPPORTED_1000baseT_Full | SUPPORTED_1000baseT_Half)); +#if 0 + writel(1 << S6_GMAC_MACCONF1_SYNCTX | + 1 << S6_GMAC_MACCONF1_SYNCRX | + 1 << S6_GMAC_MACCONF1_TXFLOWCTRL | + 1 << S6_GMAC_MACCONF1_RXFLOWCTRL | + 1 << S6_GMAC_MACCONF1_RESTXFUNC | + 1 << S6_GMAC_MACCONF1_RESRXFUNC | + 1 << S6_GMAC_MACCONF1_RESTXMACCTRL | + 1 << S6_GMAC_MACCONF1_RESRXMACCTRL, + pd->reg + S6_GMAC_MACCONF1); +#endif + writel(1 << S6_GMAC_MACCONF1_SOFTRES, pd->reg + S6_GMAC_MACCONF1); + udelay(1000); + writel(1 << S6_GMAC_MACCONF1_TXENA | 1 << S6_GMAC_MACCONF1_RXENA, + pd->reg + S6_GMAC_MACCONF1); + writel(1 << S6_GMAC_HOST_PBLKCTRL_TXSRES | + 1 << S6_GMAC_HOST_PBLKCTRL_RXSRES, + pd->reg + S6_GMAC_HOST_PBLKCTRL); + writel(S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_TXBSIZ | + S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_RXBSIZ | + 1 << S6_GMAC_HOST_PBLKCTRL_STATENA | + 1 << S6_GMAC_HOST_PBLKCTRL_STATCLEAR | + is_rgmii << S6_GMAC_HOST_PBLKCTRL_RGMII, + pd->reg + S6_GMAC_HOST_PBLKCTRL); + writel(1 << S6_GMAC_MACCONF1_TXENA | + 1 << S6_GMAC_MACCONF1_RXENA | + (dev->flags & IFF_LOOPBACK ? 1 : 0) + << S6_GMAC_MACCONF1_LOOPBACK, + pd->reg + S6_GMAC_MACCONF1); + writel(dev->mtu && (dev->mtu < (S6_MAX_FRLEN - ETH_HLEN-ETH_FCS_LEN)) ? + dev->mtu+ETH_HLEN+ETH_FCS_LEN : S6_MAX_FRLEN, + pd->reg + S6_GMAC_MACMAXFRAMELEN); + writel((pd->link.full ? 1 : 0) << S6_GMAC_MACCONF2_FULL | + 1 << S6_GMAC_MACCONF2_PADCRCENA | + 1 << S6_GMAC_MACCONF2_LENGTHFCHK | + (pd->link.giga ? + S6_GMAC_MACCONF2_IFMODE_BYTE : + S6_GMAC_MACCONF2_IFMODE_NIBBLE) + << S6_GMAC_MACCONF2_IFMODE | + 7 << S6_GMAC_MACCONF2_PREAMBLELEN, + pd->reg + S6_GMAC_MACCONF2); + writel(0, pd->reg + S6_GMAC_MACSTATADDR1); + writel(0, pd->reg + S6_GMAC_MACSTATADDR2); + writel(1 << S6_GMAC_FIFOCONF0_WTMENREQ | + 1 << S6_GMAC_FIFOCONF0_SRFENREQ | + 1 << S6_GMAC_FIFOCONF0_FRFENREQ | + 1 << S6_GMAC_FIFOCONF0_STFENREQ | + 1 << S6_GMAC_FIFOCONF0_FTFENREQ, + pd->reg + S6_GMAC_FIFOCONF0); + writel(128 << S6_GMAC_FIFOCONF3_CFGFTTH | + 128 << S6_GMAC_FIFOCONF3_CFGHWMFT, + pd->reg + S6_GMAC_FIFOCONF3); + writel((S6_GMAC_FIFOCONF_RSV_MASK & ~( + 1 << S6_GMAC_FIFOCONF_RSV_RUNT | + 1 << S6_GMAC_FIFOCONF_RSV_CRCERR | + 1 << S6_GMAC_FIFOCONF_RSV_OK | + 1 << S6_GMAC_FIFOCONF_RSV_DRIBBLE | + 1 << S6_GMAC_FIFOCONF_RSV_CTRLFRAME | + 1 << S6_GMAC_FIFOCONF_RSV_PAUSECTRL | + 1 << S6_GMAC_FIFOCONF_RSV_UNOPCODE | + 1 << S6_GMAC_FIFOCONF_RSV_TRUNCATED)) | + 1 << S6_GMAC_FIFOCONF5_DROPLT64 | + pd->link.giga << S6_GMAC_FIFOCONF5_CFGBYTM | + 1 << S6_GMAC_FIFOCONF5_RXDROPSIZE, + pd->reg + S6_GMAC_FIFOCONF5); + writel(1 << S6_GMAC_FIFOCONF_RSV_RUNT | + 1 << S6_GMAC_FIFOCONF_RSV_CRCERR | + 1 << S6_GMAC_FIFOCONF_RSV_DRIBBLE | + 1 << S6_GMAC_FIFOCONF_RSV_CTRLFRAME | + 1 << S6_GMAC_FIFOCONF_RSV_PAUSECTRL | + 1 << S6_GMAC_FIFOCONF_RSV_UNOPCODE | + 1 << S6_GMAC_FIFOCONF_RSV_TRUNCATED, + pd->reg + S6_GMAC_FIFOCONF4); + s6gmac_set_dstaddr(pd, 0, + 0xFFFFFFFF, 0x0000FFFF, 0xFFFFFFFF, 0x0000FFFF); + s6gmac_set_dstaddr(pd, 1, + dev->dev_addr[5] | + dev->dev_addr[4] << 8 | + dev->dev_addr[3] << 16 | + dev->dev_addr[2] << 24, + dev->dev_addr[1] | + dev->dev_addr[0] << 8, + 0xFFFFFFFF, 0x0000FFFF); + s6gmac_set_dstaddr(pd, 2, + 0x00000000, 0x00000100, 0x00000000, 0x00000100); + s6gmac_set_dstaddr(pd, 3, + 0x00000000, 0x00000000, 0x00000000, 0x00000000); + writel(1 << S6_GMAC_HOST_PBLKCTRL_TXENA | + 1 << S6_GMAC_HOST_PBLKCTRL_RXENA | + S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_TXBSIZ | + S6_GMAC_HOST_PBLKCTRL_SIZ_128 << S6_GMAC_HOST_PBLKCTRL_RXBSIZ | + 1 << S6_GMAC_HOST_PBLKCTRL_STATENA | + 1 << S6_GMAC_HOST_PBLKCTRL_STATCLEAR | + is_rgmii << S6_GMAC_HOST_PBLKCTRL_RGMII, + pd->reg + S6_GMAC_HOST_PBLKCTRL); +} + +static void s6mii_enable(struct s6gmac *pd) +{ + writel(readl(pd->reg + S6_GMAC_MACCONF1) & + ~(1 << S6_GMAC_MACCONF1_SOFTRES), + pd->reg + S6_GMAC_MACCONF1); + writel((readl(pd->reg + S6_GMAC_MACMIICONF) + & ~(S6_GMAC_MACMIICONF_CSEL_MASK << S6_GMAC_MACMIICONF_CSEL)) + | (S6_GMAC_MACMIICONF_CSEL_DIV168 << S6_GMAC_MACMIICONF_CSEL), + pd->reg + S6_GMAC_MACMIICONF); +} + +static int s6mii_busy(struct s6gmac *pd, int tmo) +{ + while (readl(pd->reg + S6_GMAC_MACMIIINDI)) { + if (--tmo == 0) + return -ETIME; + udelay(64); + } + return 0; +} + +static int s6mii_read(struct mii_bus *bus, int phy_addr, int regnum) +{ + struct s6gmac *pd = bus->priv; + s6mii_enable(pd); + if (s6mii_busy(pd, 256)) + return -ETIME; + writel(phy_addr << S6_GMAC_MACMIIADDR_PHY | + regnum << S6_GMAC_MACMIIADDR_REG, + pd->reg + S6_GMAC_MACMIIADDR); + writel(1 << S6_GMAC_MACMIICMD_READ, pd->reg + S6_GMAC_MACMIICMD); + writel(0, pd->reg + S6_GMAC_MACMIICMD); + if (s6mii_busy(pd, 256)) + return -ETIME; + return (u16)readl(pd->reg + S6_GMAC_MACMIISTAT); +} + +static int s6mii_write(struct mii_bus *bus, int phy_addr, int regnum, u16 value) +{ + struct s6gmac *pd = bus->priv; + s6mii_enable(pd); + if (s6mii_busy(pd, 256)) + return -ETIME; + writel(phy_addr << S6_GMAC_MACMIIADDR_PHY | + regnum << S6_GMAC_MACMIIADDR_REG, + pd->reg + S6_GMAC_MACMIIADDR); + writel(value, pd->reg + S6_GMAC_MACMIICTRL); + if (s6mii_busy(pd, 256)) + return -ETIME; + return 0; +} + +static int s6mii_reset(struct mii_bus *bus) +{ + struct s6gmac *pd = bus->priv; + s6mii_enable(pd); + if (s6mii_busy(pd, PHY_INIT_TIMEOUT)) + return -ETIME; + return 0; +} + +static void s6gmac_set_rgmii_txclock(struct s6gmac *pd) +{ + u32 pllsel = readl(S6_REG_GREG1 + S6_GREG1_PLLSEL); + pllsel &= ~(S6_GREG1_PLLSEL_GMAC_MASK << S6_GREG1_PLLSEL_GMAC); + switch (pd->link.mbit) { + case 10: + pllsel |= S6_GREG1_PLLSEL_GMAC_2500KHZ << S6_GREG1_PLLSEL_GMAC; + break; + case 100: + pllsel |= S6_GREG1_PLLSEL_GMAC_25MHZ << S6_GREG1_PLLSEL_GMAC; + break; + case 1000: + pllsel |= S6_GREG1_PLLSEL_GMAC_125MHZ << S6_GREG1_PLLSEL_GMAC; + break; + default: + return; + } + writel(pllsel, S6_REG_GREG1 + S6_GREG1_PLLSEL); +} + +static inline void s6gmac_linkisup(struct net_device *dev, int isup) +{ + struct s6gmac *pd = netdev_priv(dev); + struct phy_device *phydev = pd->phydev; + + pd->link.full = phydev->duplex; + pd->link.giga = (phydev->speed == 1000); + if (pd->link.mbit != phydev->speed) { + pd->link.mbit = phydev->speed; + s6gmac_set_rgmii_txclock(pd); + } + pd->link.isup = isup; + if (isup) + netif_carrier_on(dev); + phy_print_status(phydev); +} + +static void s6gmac_adjust_link(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + struct phy_device *phydev = pd->phydev; + if (pd->link.isup && + (!phydev->link || + (pd->link.mbit != phydev->speed) || + (pd->link.full != phydev->duplex))) { + pd->link.isup = 0; + netif_tx_disable(dev); + if (!phydev->link) { + netif_carrier_off(dev); + phy_print_status(phydev); + } + } + if (!pd->link.isup && phydev->link) { + if (pd->link.full != phydev->duplex) { + u32 maccfg = readl(pd->reg + S6_GMAC_MACCONF2); + if (phydev->duplex) + maccfg |= 1 << S6_GMAC_MACCONF2_FULL; + else + maccfg &= ~(1 << S6_GMAC_MACCONF2_FULL); + writel(maccfg, pd->reg + S6_GMAC_MACCONF2); + } + + if (pd->link.giga != (phydev->speed == 1000)) { + u32 fifocfg = readl(pd->reg + S6_GMAC_FIFOCONF5); + u32 maccfg = readl(pd->reg + S6_GMAC_MACCONF2); + maccfg &= ~(S6_GMAC_MACCONF2_IFMODE_MASK + << S6_GMAC_MACCONF2_IFMODE); + if (phydev->speed == 1000) { + fifocfg |= 1 << S6_GMAC_FIFOCONF5_CFGBYTM; + maccfg |= S6_GMAC_MACCONF2_IFMODE_BYTE + << S6_GMAC_MACCONF2_IFMODE; + } else { + fifocfg &= ~(1 << S6_GMAC_FIFOCONF5_CFGBYTM); + maccfg |= S6_GMAC_MACCONF2_IFMODE_NIBBLE + << S6_GMAC_MACCONF2_IFMODE; + } + writel(fifocfg, pd->reg + S6_GMAC_FIFOCONF5); + writel(maccfg, pd->reg + S6_GMAC_MACCONF2); + } + + if (!s6dmac_fifo_full(pd->tx_dma, pd->tx_chan)) + netif_wake_queue(dev); + s6gmac_linkisup(dev, 1); + } +} + +static inline int s6gmac_phy_start(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + int i = 0; + struct phy_device *p = NULL; + while ((!(p = pd->mii.bus->phy_map[i])) && (i < PHY_MAX_ADDR)) + i++; + p = phy_connect(dev, dev_name(&p->dev), &s6gmac_adjust_link, 0, + PHY_INTERFACE_MODE_RGMII); + if (IS_ERR(p)) { + printk(KERN_ERR "%s: Could not attach to PHY\n", dev->name); + return PTR_ERR(p); + } + p->supported &= PHY_GBIT_FEATURES; + p->advertising = p->supported; + pd->phydev = p; + return 0; +} + +static inline void s6gmac_init_stats(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + u32 mask; + mask = 1 << S6_GMAC_STATCARRY1_RDRP | + 1 << S6_GMAC_STATCARRY1_RJBR | + 1 << S6_GMAC_STATCARRY1_RFRG | + 1 << S6_GMAC_STATCARRY1_ROVR | + 1 << S6_GMAC_STATCARRY1_RUND | + 1 << S6_GMAC_STATCARRY1_RCDE | + 1 << S6_GMAC_STATCARRY1_RFLR | + 1 << S6_GMAC_STATCARRY1_RALN | + 1 << S6_GMAC_STATCARRY1_RMCA | + 1 << S6_GMAC_STATCARRY1_RFCS | + 1 << S6_GMAC_STATCARRY1_RPKT | + 1 << S6_GMAC_STATCARRY1_RBYT; + writel(mask, pd->reg + S6_GMAC_STATCARRY(0)); + writel(~mask, pd->reg + S6_GMAC_STATCARRYMSK(0)); + mask = 1 << S6_GMAC_STATCARRY2_TDRP | + 1 << S6_GMAC_STATCARRY2_TNCL | + 1 << S6_GMAC_STATCARRY2_TXCL | + 1 << S6_GMAC_STATCARRY2_TEDF | + 1 << S6_GMAC_STATCARRY2_TPKT | + 1 << S6_GMAC_STATCARRY2_TBYT | + 1 << S6_GMAC_STATCARRY2_TFRG | + 1 << S6_GMAC_STATCARRY2_TUND | + 1 << S6_GMAC_STATCARRY2_TOVR | + 1 << S6_GMAC_STATCARRY2_TFCS | + 1 << S6_GMAC_STATCARRY2_TJBR; + writel(mask, pd->reg + S6_GMAC_STATCARRY(1)); + writel(~mask, pd->reg + S6_GMAC_STATCARRYMSK(1)); +} + +static inline void s6gmac_init_dmac(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + s6dmac_disable_chan(pd->tx_dma, pd->tx_chan); + s6dmac_disable_chan(pd->rx_dma, pd->rx_chan); + s6dmac_disable_error_irqs(pd->tx_dma, 1 << S6_HIFDMA_GMACTX); + s6dmac_disable_error_irqs(pd->rx_dma, 1 << S6_HIFDMA_GMACRX); +} + +static int s6gmac_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + unsigned long flags; + spin_lock_irqsave(&pd->lock, flags); + dev->trans_start = jiffies; + writel(skb->len << S6_GMAC_BURST_PREWR_LEN | + 0 << S6_GMAC_BURST_PREWR_CFE | + 1 << S6_GMAC_BURST_PREWR_PPE | + 1 << S6_GMAC_BURST_PREWR_FCS | + ((skb->len < ETH_ZLEN) ? 1 : 0) << S6_GMAC_BURST_PREWR_PAD, + pd->reg + S6_GMAC_BURST_PREWR); + s6dmac_put_fifo_cache(pd->tx_dma, pd->tx_chan, + (u32)skb->data, pd->io, skb->len); + if (s6dmac_fifo_full(pd->tx_dma, pd->tx_chan)) + netif_stop_queue(dev); + if (((u8)(pd->tx_skb_i - pd->tx_skb_o)) >= S6_NUM_TX_SKB) { + printk(KERN_ERR "GMAC BUG: skb tx ring overflow [%x, %x]\n", + pd->tx_skb_o, pd->tx_skb_i); + BUG(); + } + pd->tx_skb[(pd->tx_skb_i++) % S6_NUM_TX_SKB] = skb; + spin_unlock_irqrestore(&pd->lock, flags); + return 0; +} + +static void s6gmac_tx_timeout(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + unsigned long flags; + spin_lock_irqsave(&pd->lock, flags); + s6gmac_tx_interrupt(dev); + spin_unlock_irqrestore(&pd->lock, flags); +} + +static int s6gmac_open(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + unsigned long flags; + phy_read_status(pd->phydev); + spin_lock_irqsave(&pd->lock, flags); + pd->link.mbit = 0; + s6gmac_linkisup(dev, pd->phydev->link); + s6gmac_init_device(dev); + s6gmac_init_stats(dev); + s6gmac_init_dmac(dev); + s6gmac_rx_fillfifo(pd); + s6dmac_enable_chan(pd->rx_dma, pd->rx_chan, + 2, 1, 0, 1, 0, 0, 0, 7, -1, 2, 0, 1); + s6dmac_enable_chan(pd->tx_dma, pd->tx_chan, + 2, 0, 1, 0, 0, 0, 0, 7, -1, 2, 0, 1); + writel(0 << S6_GMAC_HOST_INT_TXBURSTOVER | + 0 << S6_GMAC_HOST_INT_TXPREWOVER | + 0 << S6_GMAC_HOST_INT_RXBURSTUNDER | + 0 << S6_GMAC_HOST_INT_RXPOSTRFULL | + 0 << S6_GMAC_HOST_INT_RXPOSTRUNDER, + pd->reg + S6_GMAC_HOST_INTMASK); + spin_unlock_irqrestore(&pd->lock, flags); + phy_start(pd->phydev); + netif_start_queue(dev); + return 0; +} + +static int s6gmac_stop(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + unsigned long flags; + netif_stop_queue(dev); + phy_stop(pd->phydev); + spin_lock_irqsave(&pd->lock, flags); + s6gmac_init_dmac(dev); + s6gmac_stop_device(dev); + while (pd->tx_skb_i != pd->tx_skb_o) + dev_kfree_skb(pd->tx_skb[(pd->tx_skb_o++) % S6_NUM_TX_SKB]); + while (pd->rx_skb_i != pd->rx_skb_o) + dev_kfree_skb(pd->rx_skb[(pd->rx_skb_o++) % S6_NUM_RX_SKB]); + spin_unlock_irqrestore(&pd->lock, flags); + return 0; +} + +static struct net_device_stats *s6gmac_stats(struct net_device *dev) +{ + struct s6gmac *pd = netdev_priv(dev); + struct net_device_stats *st = (struct net_device_stats *)&pd->stats; + int i; + do { + unsigned long flags; + spin_lock_irqsave(&pd->lock, flags); + for (i = 0; i < sizeof(pd->stats) / sizeof(unsigned long); i++) + pd->stats[i] = + pd->carry[i] << (S6_GMAC_STAT_SIZE_MIN - 1); + s6gmac_stats_collect(pd, &statinf[0][0]); + s6gmac_stats_collect(pd, &statinf[1][0]); + i = s6gmac_stats_pending(pd, 0) | + s6gmac_stats_pending(pd, 1); + spin_unlock_irqrestore(&pd->lock, flags); + } while (i); + st->rx_errors = st->rx_crc_errors + + st->rx_frame_errors + + st->rx_length_errors + + st->rx_missed_errors; + st->tx_errors += st->tx_aborted_errors; + return st; +} + +static int __devinit s6gmac_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct s6gmac *pd; + int res; + unsigned long i; + struct mii_bus *mb; + dev = alloc_etherdev(sizeof(*pd)); + if (!dev) { + printk(KERN_ERR DRV_PRMT "etherdev alloc failed, aborting.\n"); + return -ENOMEM; + } + dev->open = s6gmac_open; + dev->stop = s6gmac_stop; + dev->hard_start_xmit = s6gmac_tx; + dev->tx_timeout = s6gmac_tx_timeout; + dev->watchdog_timeo = HZ; + dev->get_stats = s6gmac_stats; + dev->irq = platform_get_irq(pdev, 0); + pd = netdev_priv(dev); + memset(pd, 0, sizeof(*pd)); + spin_lock_init(&pd->lock); + pd->reg = platform_get_resource(pdev, IORESOURCE_MEM, 0)->start; + i = platform_get_resource(pdev, IORESOURCE_DMA, 0)->start; + pd->tx_dma = DMA_MASK_DMAC(i); + pd->tx_chan = DMA_INDEX_CHNL(i); + i = platform_get_resource(pdev, IORESOURCE_DMA, 1)->start; + pd->rx_dma = DMA_MASK_DMAC(i); + pd->rx_chan = DMA_INDEX_CHNL(i); + pd->io = platform_get_resource(pdev, IORESOURCE_IO, 0)->start; + res = request_irq(dev->irq, &s6gmac_interrupt, 0, dev->name, dev); + if (res) { + printk(KERN_ERR DRV_PRMT "irq request failed: %d\n", dev->irq); + goto errirq; + } + res = register_netdev(dev); + if (res) { + printk(KERN_ERR DRV_PRMT "error registering device %s\n", + dev->name); + goto errdev; + } + mb = mdiobus_alloc(); + if (!mb) { + printk(KERN_ERR DRV_PRMT "error allocating mii bus\n"); + goto errmii; + } + mb->name = "s6gmac_mii"; + mb->read = s6mii_read; + mb->write = s6mii_write; + mb->reset = s6mii_reset; + mb->priv = pd; + snprintf(mb->id, MII_BUS_ID_SIZE, "0"); + mb->phy_mask = ~(1 << 0); + mb->irq = &pd->mii.irq[0]; + for (i = 0; i < PHY_MAX_ADDR; i++) { + int n = platform_get_irq(pdev, i + 1); + if (n < 0) + n = PHY_POLL; + pd->mii.irq[i] = n; + } + mdiobus_register(mb); + pd->mii.bus = mb; + res = s6gmac_phy_start(dev); + if (res) + return res; + platform_set_drvdata(pdev, dev); + return 0; +errmii: + unregister_netdev(dev); +errdev: + free_irq(dev->irq, dev); +errirq: + free_netdev(dev); + return res; +} + +static int __devexit s6gmac_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + if (dev) { + struct s6gmac *pd = netdev_priv(dev); + mdiobus_unregister(pd->mii.bus); + unregister_netdev(dev); + free_irq(dev->irq, dev); + free_netdev(dev); + platform_set_drvdata(pdev, NULL); + } + return 0; +} + +static struct platform_driver s6gmac_driver = { + .probe = s6gmac_probe, + .remove = __devexit_p(s6gmac_remove), + .driver = { + .name = "s6gmac", + .owner = THIS_MODULE, + }, +}; + +static int __init s6gmac_init(void) +{ + printk(KERN_INFO DRV_PRMT "S6 GMAC ethernet driver\n"); + return platform_driver_register(&s6gmac_driver); +} + + +static void __exit s6gmac_exit(void) +{ + platform_driver_unregister(&s6gmac_driver); +} + +module_init(s6gmac_init); +module_exit(s6gmac_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("S6105 on chip Ethernet driver"); +MODULE_AUTHOR("Oskar Schirmer <os@emlix.com>"); diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig index 3717569828b..a906d399813 100644 --- a/drivers/net/usb/Kconfig +++ b/drivers/net/usb/Kconfig @@ -169,10 +169,12 @@ config USB_NET_CDCETHER The Linux-USB CDC Ethernet Gadget driver is an open implementation. This driver should work with at least the following devices: + * Dell Wireless 5530 HSPA * Ericsson PipeRider (all variants) + * Ericsson Mobile Broadband Module (all variants) * Motorola (DM100 and SB4100) * Broadcom Cable Modem (reference design) - * Toshiba PCX1100U + * Toshiba (PCX1100U and F3507g) * ... This driver creates an interface named "ethX", where X depends on diff --git a/drivers/net/usb/cdc_ether.c b/drivers/net/usb/cdc_ether.c index 01fd528306e..4a6aff57940 100644 --- a/drivers/net/usb/cdc_ether.c +++ b/drivers/net/usb/cdc_ether.c @@ -533,6 +533,31 @@ static const struct usb_device_id products [] = { USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1900, USB_CLASS_COMM, USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson F3507g ver. 2 */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1902, USB_CLASS_COMM, + USB_CDC_SUBCLASS_MDLM, USB_CDC_PROTO_NONE), + .driver_info = (unsigned long) &cdc_info, +}, { + /* Ericsson F3607gw */ + USB_DEVICE_AND_INTERFACE_INFO(0x0bdb, 0x1904, 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, 0x1906, 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, +}, { + /* 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, }, { }, // END }; diff --git a/drivers/net/usb/cdc_subset.c b/drivers/net/usb/cdc_subset.c index c66b9c324f5..ca39ace0b0e 100644 --- a/drivers/net/usb/cdc_subset.c +++ b/drivers/net/usb/cdc_subset.c @@ -307,9 +307,10 @@ static const struct usb_device_id products [] = { USB_DEVICE (0x1286, 0x8001), // "blob" bootloader .driver_info = (unsigned long) &blob_info, }, { - // Linux Ethernet/RNDIS gadget on pxa210/25x/26x, second config - // e.g. Gumstix, current OpenZaurus, ... - USB_DEVICE_VER (0x0525, 0xa4a2, 0x0203, 0x0203), + // Linux Ethernet/RNDIS gadget, mostly on PXA, second config + // e.g. Gumstix, current OpenZaurus, ... or anything else + // that just enables this gadget option. + USB_DEVICE (0x0525, 0xa4a2), .driver_info = (unsigned long) &linuxdev_info, }, #endif diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c index 2138535f233..73acbd244aa 100644 --- a/drivers/net/usb/pegasus.c +++ b/drivers/net/usb/pegasus.c @@ -297,7 +297,7 @@ static int update_eth_regs_async(pegasus_t * pegasus) pegasus->dr.bRequestType = PEGASUS_REQT_WRITE; pegasus->dr.bRequest = PEGASUS_REQ_SET_REGS; - pegasus->dr.wValue = 0; + pegasus->dr.wValue = cpu_to_le16(0); pegasus->dr.wIndex = cpu_to_le16(EthCtrl0); pegasus->dr.wLength = cpu_to_le16(3); pegasus->ctrl_urb->transfer_buffer_length = 3; @@ -446,11 +446,12 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data) int i; __u8 tmp, d[4] = { 0x3f, 0, 0, EPROM_WRITE }; int ret; + __le16 le_data = cpu_to_le16(data); set_registers(pegasus, EpromOffset, 4, d); enable_eprom_write(pegasus); set_register(pegasus, EpromOffset, index); - set_registers(pegasus, EpromData, 2, &data); + set_registers(pegasus, EpromData, 2, &le_data); set_register(pegasus, EpromCtrl, EPROM_WRITE); for (i = 0; i < REG_TIMEOUT; i++) { @@ -923,29 +924,32 @@ static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) static inline void disable_net_traffic(pegasus_t * pegasus) { - int tmp = 0; + __le16 tmp = cpu_to_le16(0); - set_registers(pegasus, EthCtrl0, 2, &tmp); + set_registers(pegasus, EthCtrl0, sizeof(tmp), &tmp); } static inline void get_interrupt_interval(pegasus_t * pegasus) { - __u8 data[2]; + u16 data; + u8 interval; - read_eprom_word(pegasus, 4, (__u16 *) data); + read_eprom_word(pegasus, 4, &data); + interval = data >> 8; if (pegasus->usb->speed != USB_SPEED_HIGH) { - if (data[1] < 0x80) { + if (interval < 0x80) { if (netif_msg_timer(pegasus)) dev_info(&pegasus->intf->dev, "intr interval " "changed from %ums to %ums\n", - data[1], 0x80); - data[1] = 0x80; + interval, 0x80); + interval = 0x80; + data = (data & 0x00FF) | ((u16)interval << 8); #ifdef PEGASUS_WRITE_EEPROM - write_eprom_word(pegasus, 4, *(__u16 *) data); + write_eprom_word(pegasus, 4, data); #endif } } - pegasus->intr_interval = data[1]; + pegasus->intr_interval = interval; } static void set_carrier(struct net_device *net) @@ -1299,7 +1303,8 @@ static int pegasus_blacklisted(struct usb_device *udev) /* Special quirk to keep the driver from handling the Belkin Bluetooth * dongle which happens to have the same ID. */ - if ((udd->idVendor == VENDOR_BELKIN && udd->idProduct == 0x0121) && + if ((udd->idVendor == cpu_to_le16(VENDOR_BELKIN)) && + (udd->idProduct == cpu_to_le16(0x0121)) && (udd->bDeviceClass == USB_CLASS_WIRELESS_CONTROLLER) && (udd->bDeviceProtocol == 1)) return 1; diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index b02f7adff5d..3ba35956327 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -1847,7 +1847,7 @@ static void velocity_free_tx_buf(struct velocity_info *vptr, struct velocity_td_ */ if (tdinfo->skb_dma) { - pktlen = (skb->len > ETH_ZLEN ? : ETH_ZLEN); + pktlen = max_t(unsigned int, skb->len, ETH_ZLEN); for (i = 0; i < tdinfo->nskb_dma; i++) { #ifdef VELOCITY_ZERO_COPY_SUPPORT pci_unmap_single(vptr->pdev, tdinfo->skb_dma[i], le16_to_cpu(td->tdesc1.len), PCI_DMA_TODEVICE); diff --git a/drivers/net/wireless/ath/ath5k/base.c b/drivers/net/wireless/ath/ath5k/base.c index 55f7de09d13..ea045151f95 100644 --- a/drivers/net/wireless/ath/ath5k/base.c +++ b/drivers/net/wireless/ath/ath5k/base.c @@ -538,6 +538,7 @@ ath5k_pci_probe(struct pci_dev *pdev, sc->iobase = mem; /* So we can unmap it on detach */ sc->cachelsz = csz * sizeof(u32); /* convert to bytes */ sc->opmode = NL80211_IFTYPE_STATION; + sc->bintval = 1000; mutex_init(&sc->lock); spin_lock_init(&sc->rxbuflock); spin_lock_init(&sc->txbuflock); @@ -686,6 +687,13 @@ ath5k_pci_resume(struct pci_dev *pdev) if (err) return err; + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state + */ + pci_write_config_byte(pdev, 0x41, 0); + err = request_irq(pdev->irq, ath5k_intr, IRQF_SHARED, "ath", sc); if (err) { ATH5K_ERR(sc, "request_irq failed\n"); @@ -2748,9 +2756,6 @@ static int ath5k_add_interface(struct ieee80211_hw *hw, goto end; } - /* Set to a reasonable value. Note that this will - * be set to mac80211's value at ath5k_config(). */ - sc->bintval = 1000; ath5k_hw_set_lladdr(sc->ah, conf->mac_addr); ret = 0; diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index 9f49a3251d4..66a6c1f5022 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -1196,8 +1196,8 @@ void ath_radio_disable(struct ath_softc *sc) ath9k_hw_phy_disable(ah); ath9k_hw_configpcipowersave(ah, 1); - ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); ath9k_ps_restore(sc); + ath9k_hw_setpower(ah, ATH9K_PM_FULL_SLEEP); } /*******************/ diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index ccdf20a2e9b..170c5b32e49 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -87,6 +87,7 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct ath_softc *sc; struct ieee80211_hw *hw; u8 csz; + u32 val; int ret = 0; struct ath_hw *ah; @@ -133,6 +134,14 @@ static int ath_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_master(pdev); + /* + * Disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state. + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); + ret = pci_request_region(pdev, 0, "ath9k"); if (ret) { dev_err(&pdev->dev, "PCI memory region reserve error\n"); @@ -239,12 +248,21 @@ static int ath_pci_resume(struct pci_dev *pdev) struct ieee80211_hw *hw = pci_get_drvdata(pdev); struct ath_wiphy *aphy = hw->priv; struct ath_softc *sc = aphy->sc; + u32 val; int err; err = pci_enable_device(pdev); if (err) return err; pci_restore_state(pdev); + /* + * Suspend/Resume resets the PCI configuration space, so we have to + * re-disable the RETRY_TIMEOUT register (0x41) to keep + * PCI Tx retries from interfering with C3 CPU state + */ + pci_read_config_dword(pdev, 0x40, &val); + if ((val & 0x0000ff00) != 0) + pci_write_config_dword(pdev, 0x40, val & 0xffff00ff); /* Enable LED */ ath9k_hw_cfg_output(sc->sc_ah, ATH_LED_PIN, diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c index f99f3a76df3..cece1c4c6bd 100644 --- a/drivers/net/wireless/ath/ath9k/recv.c +++ b/drivers/net/wireless/ath/ath9k/recv.c @@ -539,11 +539,14 @@ static void ath_rx_ps_beacon(struct ath_softc *sc, struct sk_buff *skb) if (ath_beacon_dtim_pending_cab(skb)) { /* * Remain awake waiting for buffered broadcast/multicast - * frames. + * frames. If the last broadcast/multicast frame is not + * received properly, the next beacon frame will work as + * a backup trigger for returning into NETWORK SLEEP state, + * so we are waiting for it as well. */ DPRINTF(sc, ATH_DBG_PS, "Received DTIM beacon indicating " "buffered broadcast/multicast frame(s)\n"); - sc->sc_flags |= SC_OP_WAIT_FOR_CAB; + sc->sc_flags |= SC_OP_WAIT_FOR_CAB | SC_OP_WAIT_FOR_BEACON; return; } diff --git a/drivers/net/wireless/iwmc3200wifi/iwm.h b/drivers/net/wireless/iwmc3200wifi/iwm.h index 635c16ee618..77c339f8516 100644 --- a/drivers/net/wireless/iwmc3200wifi/iwm.h +++ b/drivers/net/wireless/iwmc3200wifi/iwm.h @@ -288,6 +288,7 @@ struct iwm_priv { u8 *eeprom; struct timer_list watchdog; struct work_struct reset_worker; + struct mutex mutex; struct rfkill *rfkill; char private[0] __attribute__((__aligned__(NETDEV_ALIGN))); @@ -315,8 +316,11 @@ extern const struct iw_handler_def iwm_iw_handler_def; void *iwm_if_alloc(int sizeof_bus, struct device *dev, struct iwm_if_ops *if_ops); void iwm_if_free(struct iwm_priv *iwm); +int iwm_if_add(struct iwm_priv *iwm); +void iwm_if_remove(struct iwm_priv *iwm); int iwm_mode_to_nl80211_iftype(int mode); int iwm_priv_init(struct iwm_priv *iwm); +void iwm_priv_deinit(struct iwm_priv *iwm); void iwm_reset(struct iwm_priv *iwm); void iwm_tx_credit_init_pools(struct iwm_priv *iwm, struct iwm_umac_notif_alive *alive); diff --git a/drivers/net/wireless/iwmc3200wifi/main.c b/drivers/net/wireless/iwmc3200wifi/main.c index 6a2640f16b6..8be206d5822 100644 --- a/drivers/net/wireless/iwmc3200wifi/main.c +++ b/drivers/net/wireless/iwmc3200wifi/main.c @@ -112,6 +112,9 @@ static void iwm_statistics_request(struct work_struct *work) iwm_send_umac_stats_req(iwm, 0); } +int __iwm_up(struct iwm_priv *iwm); +int __iwm_down(struct iwm_priv *iwm); + static void iwm_reset_worker(struct work_struct *work) { struct iwm_priv *iwm; @@ -120,6 +123,19 @@ static void iwm_reset_worker(struct work_struct *work) iwm = container_of(work, struct iwm_priv, reset_worker); + /* + * XXX: The iwm->mutex is introduced purely for this reset work, + * because the other users for iwm_up and iwm_down are only netdev + * ndo_open and ndo_stop which are already protected by rtnl. + * Please remove iwm->mutex together if iwm_reset_worker() is not + * required in the future. + */ + if (!mutex_trylock(&iwm->mutex)) { + IWM_WARN(iwm, "We are in the middle of interface bringing " + "UP/DOWN. Skip driver resetting.\n"); + return; + } + if (iwm->umac_profile_active) { profile = kmalloc(sizeof(struct iwm_umac_profile), GFP_KERNEL); if (profile) @@ -128,10 +144,10 @@ static void iwm_reset_worker(struct work_struct *work) IWM_ERR(iwm, "Couldn't alloc memory for profile\n"); } - iwm_down(iwm); + __iwm_down(iwm); while (retry++ < 3) { - ret = iwm_up(iwm); + ret = __iwm_up(iwm); if (!ret) break; @@ -142,7 +158,7 @@ static void iwm_reset_worker(struct work_struct *work) IWM_WARN(iwm, "iwm_up() failed: %d\n", ret); kfree(profile); - return; + goto out; } if (profile) { @@ -151,6 +167,9 @@ static void iwm_reset_worker(struct work_struct *work) iwm_send_mlme_profile(iwm); kfree(profile); } + + out: + mutex_unlock(&iwm->mutex); } static void iwm_watchdog(unsigned long data) @@ -215,10 +234,21 @@ int iwm_priv_init(struct iwm_priv *iwm) init_timer(&iwm->watchdog); iwm->watchdog.function = iwm_watchdog; iwm->watchdog.data = (unsigned long)iwm; + mutex_init(&iwm->mutex); return 0; } +void iwm_priv_deinit(struct iwm_priv *iwm) +{ + int i; + + for (i = 0; i < IWM_TX_QUEUES; i++) + destroy_workqueue(iwm->txq[i].wq); + + destroy_workqueue(iwm->rx_wq); +} + /* * We reset all the structures, and we reset the UMAC. * After calling this routine, you're expected to reload @@ -466,7 +496,7 @@ void iwm_link_off(struct iwm_priv *iwm) iwm_rx_free(iwm); - cancel_delayed_work(&iwm->stats_request); + cancel_delayed_work_sync(&iwm->stats_request); memset(wstats, 0, sizeof(struct iw_statistics)); wstats->qual.updated = IW_QUAL_ALL_INVALID; @@ -511,7 +541,7 @@ static int iwm_channels_init(struct iwm_priv *iwm) return 0; } -int iwm_up(struct iwm_priv *iwm) +int __iwm_up(struct iwm_priv *iwm) { int ret; struct iwm_notif *notif_reboot, *notif_ack = NULL; @@ -647,7 +677,18 @@ int iwm_up(struct iwm_priv *iwm) return -EIO; } -int iwm_down(struct iwm_priv *iwm) +int iwm_up(struct iwm_priv *iwm) +{ + int ret; + + mutex_lock(&iwm->mutex); + ret = __iwm_up(iwm); + mutex_unlock(&iwm->mutex); + + return ret; +} + +int __iwm_down(struct iwm_priv *iwm) { int ret; @@ -678,3 +719,14 @@ int iwm_down(struct iwm_priv *iwm) return 0; } + +int iwm_down(struct iwm_priv *iwm) +{ + int ret; + + mutex_lock(&iwm->mutex); + ret = __iwm_down(iwm); + mutex_unlock(&iwm->mutex); + + return ret; +} diff --git a/drivers/net/wireless/iwmc3200wifi/netdev.c b/drivers/net/wireless/iwmc3200wifi/netdev.c index 68e2c3b6c7a..aaa20c6885c 100644 --- a/drivers/net/wireless/iwmc3200wifi/netdev.c +++ b/drivers/net/wireless/iwmc3200wifi/netdev.c @@ -114,32 +114,31 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, iwm = wdev_to_iwm(wdev); iwm->bus_ops = if_ops; iwm->wdev = wdev; - iwm_priv_init(iwm); + + ret = iwm_priv_init(iwm); + if (ret) { + dev_err(dev, "failed to init iwm_priv\n"); + goto out_wdev; + } + wdev->iftype = iwm_mode_to_nl80211_iftype(iwm->conf.mode); - ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, - IWM_TX_QUEUES); + ndev = alloc_netdev_mq(0, "wlan%d", ether_setup, IWM_TX_QUEUES); if (!ndev) { dev_err(dev, "no memory for network device instance\n"); - goto out_wdev; + goto out_priv; } ndev->netdev_ops = &iwm_netdev_ops; ndev->wireless_handlers = &iwm_iw_handler_def; ndev->ieee80211_ptr = wdev; SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy)); - ret = register_netdev(ndev); - if (ret < 0) { - dev_err(dev, "Failed to register netdev: %d\n", ret); - goto out_ndev; - } - wdev->netdev = ndev; return iwm; - out_ndev: - free_netdev(ndev); + out_priv: + iwm_priv_deinit(iwm); out_wdev: iwm_wdev_free(iwm); @@ -148,15 +147,29 @@ void *iwm_if_alloc(int sizeof_bus, struct device *dev, void iwm_if_free(struct iwm_priv *iwm) { - int i; - if (!iwm_to_ndev(iwm)) return; - unregister_netdev(iwm_to_ndev(iwm)); free_netdev(iwm_to_ndev(iwm)); iwm_wdev_free(iwm); - destroy_workqueue(iwm->rx_wq); - for (i = 0; i < IWM_TX_QUEUES; i++) - destroy_workqueue(iwm->txq[i].wq); + iwm_priv_deinit(iwm); +} + +int iwm_if_add(struct iwm_priv *iwm) +{ + struct net_device *ndev = iwm_to_ndev(iwm); + int ret; + + ret = register_netdev(ndev); + if (ret < 0) { + dev_err(&ndev->dev, "Failed to register netdev: %d\n", ret); + return ret; + } + + return 0; +} + +void iwm_if_remove(struct iwm_priv *iwm) +{ + unregister_netdev(iwm_to_ndev(iwm)); } diff --git a/drivers/net/wireless/iwmc3200wifi/sdio.c b/drivers/net/wireless/iwmc3200wifi/sdio.c index b54da677b37..916681837fd 100644 --- a/drivers/net/wireless/iwmc3200wifi/sdio.c +++ b/drivers/net/wireless/iwmc3200wifi/sdio.c @@ -454,10 +454,18 @@ static int iwm_sdio_probe(struct sdio_func *func, INIT_WORK(&hw->isr_worker, iwm_sdio_isr_worker); + ret = iwm_if_add(iwm); + if (ret) { + dev_err(dev, "add SDIO interface failed\n"); + goto destroy_wq; + } + dev_info(dev, "IWM SDIO probe\n"); return 0; + destroy_wq: + destroy_workqueue(hw->isr_wq); debugfs_exit: iwm_debugfs_exit(iwm); if_free: @@ -471,9 +479,10 @@ static void iwm_sdio_remove(struct sdio_func *func) struct iwm_priv *iwm = hw_to_iwm(hw); struct device *dev = &func->dev; + iwm_if_remove(iwm); + destroy_workqueue(hw->isr_wq); iwm_debugfs_exit(iwm); iwm_if_free(iwm); - destroy_workqueue(hw->isr_wq); sdio_set_drvdata(func, NULL); diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c index f0e5e943f6e..14a19baff21 100644 --- a/drivers/net/wireless/zd1211rw/zd_usb.c +++ b/drivers/net/wireless/zd1211rw/zd_usb.c @@ -67,6 +67,7 @@ static struct usb_device_id usb_ids[] = { { USB_DEVICE(0x079b, 0x0062), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x1582, 0x6003), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x050d, 0x705c), .driver_info = DEVICE_ZD1211B }, + { USB_DEVICE(0x083a, 0xe503), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0xe506), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x083a, 0x4505), .driver_info = DEVICE_ZD1211B }, { USB_DEVICE(0x0471, 0x1236), .driver_info = DEVICE_ZD1211B }, diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c index 151bf5bc8af..1032d5fdbd4 100644 --- a/drivers/parport/parport_pc.c +++ b/drivers/parport/parport_pc.c @@ -1471,11 +1471,13 @@ static void __devinit decode_smsc(int efer, int key, int devid, int devrev) static void __devinit winbond_check(int io, int key) { - int devid, devrev, oldid, x_devid, x_devrev, x_oldid; + int origval, devid, devrev, oldid, x_devid, x_devrev, x_oldid; if (!request_region(io, 3, __func__)) return; + origval = inb(io); /* Save original value */ + /* First probe without key */ outb(0x20, io); x_devid = inb(io + 1); @@ -1495,6 +1497,8 @@ static void __devinit winbond_check(int io, int key) oldid = inb(io + 1); outb(0xaa, io); /* Magic Seal */ + outb(origval, io); /* in case we poked some entirely different hardware */ + if ((x_devid == devid) && (x_devrev == devrev) && (x_oldid == oldid)) goto out; /* protection against false positives */ @@ -1505,11 +1509,15 @@ out: static void __devinit winbond_check2(int io, int key) { - int devid, devrev, oldid, x_devid, x_devrev, x_oldid; + int origval[3], devid, devrev, oldid, x_devid, x_devrev, x_oldid; if (!request_region(io, 3, __func__)) return; + origval[0] = inb(io); /* Save original values */ + origval[1] = inb(io + 1); + origval[2] = inb(io + 2); + /* First probe without the key */ outb(0x20, io + 2); x_devid = inb(io + 2); @@ -1528,6 +1536,10 @@ static void __devinit winbond_check2(int io, int key) oldid = inb(io + 2); outb(0xaa, io); /* Magic Seal */ + outb(origval[0], io); /* in case we poked some entirely different hardware */ + outb(origval[1], io + 1); + outb(origval[2], io + 2); + if (x_devid == devid && x_devrev == devrev && x_oldid == oldid) goto out; /* protection against false positives */ @@ -1538,11 +1550,13 @@ out: static void __devinit smsc_check(int io, int key) { - int id, rev, oldid, oldrev, x_id, x_rev, x_oldid, x_oldrev; + int origval, id, rev, oldid, oldrev, x_id, x_rev, x_oldid, x_oldrev; if (!request_region(io, 3, __func__)) return; + origval = inb(io); /* Save original value */ + /* First probe without the key */ outb(0x0d, io); x_oldid = inb(io + 1); @@ -1566,6 +1580,8 @@ static void __devinit smsc_check(int io, int key) rev = inb(io + 1); outb(0xaa, io); /* Magic Seal */ + outb(origval, io); /* in case we poked some entirely different hardware */ + if (x_id == id && x_oldrev == oldrev && x_oldid == oldid && x_rev == rev) goto out; /* protection against false positives */ @@ -1602,11 +1618,12 @@ static void __devinit detect_and_report_smsc(void) static void __devinit detect_and_report_it87(void) { u16 dev; - u8 r; + u8 origval, r; if (verbose_probing) printk(KERN_DEBUG "IT8705 Super-IO detection, now testing port 2E ...\n"); - if (!request_region(0x2e, 1, __func__)) + if (!request_region(0x2e, 2, __func__)) return; + origval = inb(0x2e); /* Save original value */ outb(0x87, 0x2e); outb(0x01, 0x2e); outb(0x55, 0x2e); @@ -1626,8 +1643,10 @@ static void __devinit detect_and_report_it87(void) outb(r | 8, 0x2F); outb(0x02, 0x2E); /* Lock */ outb(0x02, 0x2F); + } else { + outb(origval, 0x2e); /* Oops, sorry to disturb */ } - release_region(0x2e, 1); + release_region(0x2e, 2); } #endif /* CONFIG_PARPORT_PC_SUPERIO */ @@ -2271,6 +2290,9 @@ struct parport *parport_pc_probe_port(unsigned long int base, if (IS_ERR(pdev)) return NULL; dev = &pdev->dev; + + dev->coherent_dma_mask = DMA_BIT_MASK(24); + dev->dma_mask = &dev->coherent_dma_mask; } ops = kmalloc(sizeof(struct parport_operations), GFP_KERNEL); diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index b77ae679427..1ebd6b4c743 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -2,10 +2,11 @@ # Makefile for the PCI bus specific drivers. # -obj-y += access.o bus.o probe.o remove.o pci.o quirks.o slot.o \ +obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \ pci-driver.o search.o pci-sysfs.o rom.o setup-res.o \ irq.o obj-$(CONFIG_PROC_FS) += proc.o +obj-$(CONFIG_SYSFS) += slot.o # Build PCI Express stuff if needed obj-$(CONFIG_PCIEPORTBUS) += pcie/ diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 0f370651268..db23200c487 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -66,6 +66,25 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); EXPORT_SYMBOL(pci_bus_write_config_word); EXPORT_SYMBOL(pci_bus_write_config_dword); +/** + * pci_bus_set_ops - Set raw operations of pci bus + * @bus: pci bus struct + * @ops: new raw operations + * + * Return previous raw operations + */ +struct pci_ops *pci_bus_set_ops(struct pci_bus *bus, struct pci_ops *ops) +{ + struct pci_ops *old_ops; + unsigned long flags; + + spin_lock_irqsave(&pci_lock, flags); + old_ops = bus->ops; + bus->ops = ops; + spin_unlock_irqrestore(&pci_lock, flags); + return old_ops; +} +EXPORT_SYMBOL(pci_bus_set_ops); /** * pci_read_vpd - Read one entry from Vital Product Data diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 97a8194063b..cef28a79103 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -41,9 +41,14 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, void *alignf_data) { int i, ret = -ENOMEM; + resource_size_t max = -1; type_mask |= IORESOURCE_IO | IORESOURCE_MEM; + /* don't allocate too high if the pref mem doesn't support 64bit*/ + if (!(res->flags & IORESOURCE_MEM_64)) + max = PCIBIOS_MAX_MEM_32; + for (i = 0; i < PCI_BUS_NUM_RESOURCES; i++) { struct resource *r = bus->resource[i]; if (!r) @@ -62,7 +67,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, /* Ok, try it out.. */ ret = allocate_resource(r, res, size, r->start ? : min, - -1, align, + max, align, alignf, alignf_data); if (ret == 0) break; @@ -201,13 +206,18 @@ void pci_enable_bridges(struct pci_bus *bus) * Walk the given bus, including any bridged devices * on buses under this bus. Call the provided callback * on each device found. + * + * We check the return of @cb each time. If it returns anything + * other than 0, we break out. + * */ -void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), +void pci_walk_bus(struct pci_bus *top, int (*cb)(struct pci_dev *, void *), void *userdata) { struct pci_dev *dev; struct pci_bus *bus; struct list_head *next; + int retval; bus = top; down_read(&pci_bus_sem); @@ -231,8 +241,10 @@ void pci_walk_bus(struct pci_bus *top, void (*cb)(struct pci_dev *, void *), /* Run device routines with the device locked */ down(&dev->dev.sem); - cb(dev, userdata); + retval = cb(dev, userdata); up(&dev->dev.sem); + if (retval) + break; } up_read(&pci_bus_sem); } diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index fa3a11365ec..7b287cb38b7 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -267,6 +267,84 @@ rmrr_parse_dev(struct dmar_rmrr_unit *rmrru) } return ret; } + +static LIST_HEAD(dmar_atsr_units); + +static int __init dmar_parse_one_atsr(struct acpi_dmar_header *hdr) +{ + struct acpi_dmar_atsr *atsr; + struct dmar_atsr_unit *atsru; + + atsr = container_of(hdr, struct acpi_dmar_atsr, header); + atsru = kzalloc(sizeof(*atsru), GFP_KERNEL); + if (!atsru) + return -ENOMEM; + + atsru->hdr = hdr; + atsru->include_all = atsr->flags & 0x1; + + list_add(&atsru->list, &dmar_atsr_units); + + return 0; +} + +static int __init atsr_parse_dev(struct dmar_atsr_unit *atsru) +{ + int rc; + struct acpi_dmar_atsr *atsr; + + if (atsru->include_all) + return 0; + + atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header); + rc = dmar_parse_dev_scope((void *)(atsr + 1), + (void *)atsr + atsr->header.length, + &atsru->devices_cnt, &atsru->devices, + atsr->segment); + if (rc || !atsru->devices_cnt) { + list_del(&atsru->list); + kfree(atsru); + } + + return rc; +} + +int dmar_find_matched_atsr_unit(struct pci_dev *dev) +{ + int i; + struct pci_bus *bus; + struct acpi_dmar_atsr *atsr; + struct dmar_atsr_unit *atsru; + + list_for_each_entry(atsru, &dmar_atsr_units, list) { + atsr = container_of(atsru->hdr, struct acpi_dmar_atsr, header); + if (atsr->segment == pci_domain_nr(dev->bus)) + goto found; + } + + return 0; + +found: + for (bus = dev->bus; bus; bus = bus->parent) { + struct pci_dev *bridge = bus->self; + + if (!bridge || !bridge->is_pcie || + bridge->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) + return 0; + + if (bridge->pcie_type == PCI_EXP_TYPE_ROOT_PORT) { + for (i = 0; i < atsru->devices_cnt; i++) + if (atsru->devices[i] == bridge) + return 1; + break; + } + } + + if (atsru->include_all) + return 1; + + return 0; +} #endif static void __init @@ -274,22 +352,28 @@ 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; switch (header->type) { case ACPI_DMAR_TYPE_HARDWARE_UNIT: - drhd = (struct acpi_dmar_hardware_unit *)header; + drhd = container_of(header, struct acpi_dmar_hardware_unit, + header); printk (KERN_INFO PREFIX - "DRHD (flags: 0x%08x)base: 0x%016Lx\n", - drhd->flags, (unsigned long long)drhd->address); + "DRHD base: %#016Lx flags: %#x\n", + (unsigned long long)drhd->address, drhd->flags); break; case ACPI_DMAR_TYPE_RESERVED_MEMORY: - rmrr = (struct acpi_dmar_reserved_memory *)header; - + rmrr = container_of(header, struct acpi_dmar_reserved_memory, + header); printk (KERN_INFO PREFIX - "RMRR base: 0x%016Lx end: 0x%016Lx\n", + "RMRR base: %#016Lx end: %#016Lx\n", (unsigned long long)rmrr->base_address, (unsigned long long)rmrr->end_address); break; + case ACPI_DMAR_TYPE_ATSR: + atsr = container_of(header, struct acpi_dmar_atsr, header); + printk(KERN_INFO PREFIX "ATSR flags: %#x\n", atsr->flags); + break; } } @@ -363,6 +447,11 @@ parse_dmar_table(void) ret = dmar_parse_one_rmrr(entry_header); #endif break; + case ACPI_DMAR_TYPE_ATSR: +#ifdef CONFIG_DMAR + ret = dmar_parse_one_atsr(entry_header); +#endif + break; default: printk(KERN_WARNING PREFIX "Unknown DMAR structure type\n"); @@ -431,11 +520,19 @@ int __init dmar_dev_scope_init(void) #ifdef CONFIG_DMAR { struct dmar_rmrr_unit *rmrr, *rmrr_n; + struct dmar_atsr_unit *atsr, *atsr_n; + list_for_each_entry_safe(rmrr, rmrr_n, &dmar_rmrr_units, list) { ret = rmrr_parse_dev(rmrr); if (ret) return ret; } + + list_for_each_entry_safe(atsr, atsr_n, &dmar_atsr_units, list) { + ret = atsr_parse_dev(atsr); + if (ret) + return ret; + } } #endif @@ -468,6 +565,9 @@ int __init dmar_table_init(void) #ifdef CONFIG_DMAR if (list_empty(&dmar_rmrr_units)) printk(KERN_INFO PREFIX "No RMRR found\n"); + + if (list_empty(&dmar_atsr_units)) + printk(KERN_INFO PREFIX "No ATSR found\n"); #endif #ifdef CONFIG_INTR_REMAP @@ -515,6 +615,7 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) u32 ver; static int iommu_allocated = 0; int agaw = 0; + int msagaw = 0; iommu = kzalloc(sizeof(*iommu), GFP_KERNEL); if (!iommu) @@ -535,12 +636,20 @@ int alloc_iommu(struct dmar_drhd_unit *drhd) agaw = iommu_calculate_agaw(iommu); if (agaw < 0) { printk(KERN_ERR - "Cannot get a valid agaw for iommu (seq_id = %d)\n", + "Cannot get a valid agaw for iommu (seq_id = %d)\n", + iommu->seq_id); + goto error; + } + 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; } #endif iommu->agaw = agaw; + iommu->msagaw = msagaw; /* the registers might be more than one page */ map_size = max_t(int, ecap_max_iotlb_offset(iommu->ecap), @@ -590,7 +699,8 @@ void free_iommu(struct intel_iommu *iommu) */ static inline void reclaim_free_desc(struct q_inval *qi) { - while (qi->desc_status[qi->free_tail] == QI_DONE) { + while (qi->desc_status[qi->free_tail] == QI_DONE || + qi->desc_status[qi->free_tail] == QI_ABORT) { qi->desc_status[qi->free_tail] = QI_FREE; qi->free_tail = (qi->free_tail + 1) % QI_LENGTH; qi->free_cnt++; @@ -600,10 +710,13 @@ static inline void reclaim_free_desc(struct q_inval *qi) static int qi_check_fault(struct intel_iommu *iommu, int index) { u32 fault; - int head; + int head, tail; struct q_inval *qi = iommu->qi; int wait_index = (index + 1) % QI_LENGTH; + if (qi->desc_status[wait_index] == QI_ABORT) + return -EAGAIN; + fault = readl(iommu->reg + DMAR_FSTS_REG); /* @@ -613,7 +726,11 @@ static int qi_check_fault(struct intel_iommu *iommu, int index) */ if (fault & DMA_FSTS_IQE) { head = readl(iommu->reg + DMAR_IQH_REG); - if ((head >> 4) == index) { + if ((head >> DMAR_IQ_SHIFT) == index) { + printk(KERN_ERR "VT-d detected invalid descriptor: " + "low=%llx, high=%llx\n", + (unsigned long long)qi->desc[index].low, + (unsigned long long)qi->desc[index].high); memcpy(&qi->desc[index], &qi->desc[wait_index], sizeof(struct qi_desc)); __iommu_flush_cache(iommu, &qi->desc[index], @@ -623,6 +740,32 @@ static int qi_check_fault(struct intel_iommu *iommu, int index) } } + /* + * If ITE happens, all pending wait_desc commands are aborted. + * No new descriptors are fetched until the ITE is cleared. + */ + if (fault & DMA_FSTS_ITE) { + head = readl(iommu->reg + DMAR_IQH_REG); + head = ((head >> DMAR_IQ_SHIFT) - 1 + QI_LENGTH) % QI_LENGTH; + head |= 1; + tail = readl(iommu->reg + DMAR_IQT_REG); + tail = ((tail >> DMAR_IQ_SHIFT) - 1 + QI_LENGTH) % QI_LENGTH; + + writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG); + + do { + if (qi->desc_status[head] == QI_IN_USE) + qi->desc_status[head] = QI_ABORT; + head = (head - 2 + QI_LENGTH) % QI_LENGTH; + } while (head != tail); + + if (qi->desc_status[wait_index] == QI_ABORT) + return -EAGAIN; + } + + if (fault & DMA_FSTS_ICE) + writel(DMA_FSTS_ICE, iommu->reg + DMAR_FSTS_REG); + return 0; } @@ -632,7 +775,7 @@ static int qi_check_fault(struct intel_iommu *iommu, int index) */ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) { - int rc = 0; + int rc; struct q_inval *qi = iommu->qi; struct qi_desc *hw, wait_desc; int wait_index, index; @@ -643,6 +786,9 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) hw = qi->desc; +restart: + rc = 0; + spin_lock_irqsave(&qi->q_lock, flags); while (qi->free_cnt < 3) { spin_unlock_irqrestore(&qi->q_lock, flags); @@ -673,7 +819,7 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) * update the HW tail register indicating the presence of * new descriptors. */ - writel(qi->free_head << 4, iommu->reg + DMAR_IQT_REG); + writel(qi->free_head << DMAR_IQ_SHIFT, iommu->reg + DMAR_IQT_REG); while (qi->desc_status[wait_index] != QI_DONE) { /* @@ -685,18 +831,21 @@ int qi_submit_sync(struct qi_desc *desc, struct intel_iommu *iommu) */ rc = qi_check_fault(iommu, index); if (rc) - goto out; + break; spin_unlock(&qi->q_lock); cpu_relax(); spin_lock(&qi->q_lock); } -out: - qi->desc_status[index] = qi->desc_status[wait_index] = QI_DONE; + + qi->desc_status[index] = QI_DONE; reclaim_free_desc(qi); spin_unlock_irqrestore(&qi->q_lock, flags); + if (rc == -EAGAIN) + goto restart; + return rc; } @@ -714,41 +863,26 @@ void qi_global_iec(struct intel_iommu *iommu) qi_submit_sync(&desc, iommu); } -int qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm, - u64 type, int non_present_entry_flush) +void qi_flush_context(struct intel_iommu *iommu, u16 did, u16 sid, u8 fm, + u64 type) { struct qi_desc desc; - if (non_present_entry_flush) { - if (!cap_caching_mode(iommu->cap)) - return 1; - else - did = 0; - } - desc.low = QI_CC_FM(fm) | QI_CC_SID(sid) | QI_CC_DID(did) | QI_CC_GRAN(type) | QI_CC_TYPE; desc.high = 0; - return qi_submit_sync(&desc, iommu); + qi_submit_sync(&desc, iommu); } -int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, - unsigned int size_order, u64 type, - int non_present_entry_flush) +void qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, + unsigned int size_order, u64 type) { u8 dw = 0, dr = 0; struct qi_desc desc; int ih = 0; - if (non_present_entry_flush) { - if (!cap_caching_mode(iommu->cap)) - return 1; - else - did = 0; - } - if (cap_write_drain(iommu->cap)) dw = 1; @@ -760,7 +894,28 @@ int qi_flush_iotlb(struct intel_iommu *iommu, u16 did, u64 addr, desc.high = QI_IOTLB_ADDR(addr) | QI_IOTLB_IH(ih) | QI_IOTLB_AM(size_order); - return qi_submit_sync(&desc, iommu); + qi_submit_sync(&desc, iommu); +} + +void qi_flush_dev_iotlb(struct intel_iommu *iommu, u16 sid, u16 qdep, + u64 addr, unsigned mask) +{ + struct qi_desc desc; + + if (mask) { + BUG_ON(addr & ((1 << (VTD_PAGE_SHIFT + mask)) - 1)); + addr |= (1 << (VTD_PAGE_SHIFT + mask - 1)) - 1; + desc.high = QI_DEV_IOTLB_ADDR(addr) | QI_DEV_IOTLB_SIZE; + } else + desc.high = QI_DEV_IOTLB_ADDR(addr); + + if (qdep >= QI_DEV_IOTLB_MAX_INVS) + qdep = 0; + + desc.low = QI_DEV_IOTLB_SID(sid) | QI_DEV_IOTLB_QDEP(qdep) | + QI_DIOTLB_TYPE; + + qi_submit_sync(&desc, iommu); } /* @@ -790,7 +945,6 @@ void dmar_disable_qi(struct intel_iommu *iommu) cpu_relax(); iommu->gcmd &= ~DMA_GCMD_QIE; - writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG); IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, @@ -804,7 +958,7 @@ end: */ static void __dmar_enable_qi(struct intel_iommu *iommu) { - u32 cmd, sts; + u32 sts; unsigned long flags; struct q_inval *qi = iommu->qi; @@ -818,9 +972,8 @@ static void __dmar_enable_qi(struct intel_iommu *iommu) dmar_writeq(iommu->reg + DMAR_IQA_REG, virt_to_phys(qi->desc)); - cmd = iommu->gcmd | DMA_GCMD_QIE; iommu->gcmd |= DMA_GCMD_QIE; - writel(cmd, iommu->reg + DMAR_GCMD_REG); + writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG); /* Make sure hardware complete it */ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_QIES), sts); @@ -1096,7 +1249,7 @@ int dmar_set_interrupt(struct intel_iommu *iommu) set_irq_data(irq, NULL); iommu->irq = 0; destroy_irq(irq); - return 0; + return ret; } ret = request_irq(irq, dmar_fault, 0, iommu->name, iommu); diff --git a/drivers/pci/hotplug/Kconfig b/drivers/pci/hotplug/Kconfig index 9aa4fe100a0..66f29bc00be 100644 --- a/drivers/pci/hotplug/Kconfig +++ b/drivers/pci/hotplug/Kconfig @@ -4,7 +4,7 @@ menuconfig HOTPLUG_PCI tristate "Support for PCI Hotplug" - depends on PCI && HOTPLUG + depends on PCI && HOTPLUG && SYSFS ---help--- Say Y here if you have a motherboard with a PCI Hotplug controller. This allows you to add and remove PCI cards while the machine is @@ -41,7 +41,7 @@ config HOTPLUG_PCI_FAKE config HOTPLUG_PCI_COMPAQ tristate "Compaq PCI Hotplug driver" - depends on X86 && PCI_BIOS && PCI_LEGACY + depends on X86 && PCI_BIOS help Say Y here if you have a motherboard with a Compaq PCI Hotplug controller. diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index fbc63d5e459..eb159587d0b 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -354,7 +354,7 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, status = acpi_run_hpp(handle, hpp); if (ACPI_SUCCESS(status)) break; - if (acpi_root_bridge(handle)) + if (acpi_is_root_bridge(handle)) break; status = acpi_get_parent(handle, &phandle); if (ACPI_FAILURE(status)) @@ -428,7 +428,7 @@ int acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev, u32 flags) status = acpi_run_oshp(handle); if (ACPI_SUCCESS(status)) goto got_one; - if (acpi_root_bridge(handle)) + if (acpi_is_root_bridge(handle)) break; chandle = handle; status = acpi_get_parent(chandle, &handle); @@ -449,42 +449,6 @@ got_one: } EXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware); -/* acpi_root_bridge - check to see if this acpi object is a root bridge - * - * @handle - the acpi object in question. - */ -int acpi_root_bridge(acpi_handle handle) -{ - acpi_status status; - struct acpi_device_info *info; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - int i; - - status = acpi_get_object_info(handle, &buffer); - if (ACPI_SUCCESS(status)) { - info = buffer.pointer; - if ((info->valid & ACPI_VALID_HID) && - !strcmp(PCI_ROOT_HID_STRING, - info->hardware_id.value)) { - kfree(buffer.pointer); - return 1; - } - if (info->valid & ACPI_VALID_CID) { - for (i=0; i < info->compatibility_id.count; i++) { - if (!strcmp(PCI_ROOT_HID_STRING, - info->compatibility_id.id[i].value)) { - kfree(buffer.pointer); - return 1; - } - } - } - kfree(buffer.pointer); - } - return 0; -} -EXPORT_SYMBOL_GPL(acpi_root_bridge); - - static int is_ejectable(acpi_handle handle) { acpi_status status; diff --git a/drivers/pci/hotplug/acpiphp_core.c b/drivers/pci/hotplug/acpiphp_core.c index 43c10bd261b..4dd7114964a 100644 --- a/drivers/pci/hotplug/acpiphp_core.c +++ b/drivers/pci/hotplug/acpiphp_core.c @@ -77,7 +77,6 @@ static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); static struct hotplug_slot_ops acpi_hotplug_slot_ops = { - .owner = THIS_MODULE, .enable_slot = enable_slot, .disable_slot = disable_slot, .set_attention_status = set_attention_status, diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 3a6064bce56..0cb0f830a99 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -678,18 +678,9 @@ static void remove_bridge(acpi_handle handle) static struct pci_dev * get_apic_pci_info(acpi_handle handle) { - struct acpi_pci_id id; - struct pci_bus *bus; struct pci_dev *dev; - if (ACPI_FAILURE(acpi_get_pci_id(handle, &id))) - return NULL; - - bus = pci_find_bus(id.segment, id.bus); - if (!bus) - return NULL; - - dev = pci_get_slot(bus, PCI_DEVFN(id.device, id.function)); + dev = acpi_get_pci_dev(handle); if (!dev) return NULL; @@ -1396,19 +1387,16 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) /* Program resources in newly inserted bridge */ static int acpiphp_configure_bridge (acpi_handle handle) { - struct acpi_pci_id pci_id; + struct pci_dev *dev; struct pci_bus *bus; - if (ACPI_FAILURE(acpi_get_pci_id(handle, &pci_id))) { + dev = acpi_get_pci_dev(handle); + if (!dev) { err("cannot get PCI domain and bus number for bridge\n"); return -EINVAL; } - bus = pci_find_bus(pci_id.segment, pci_id.bus); - if (!bus) { - err("cannot find bus %d:%d\n", - pci_id.segment, pci_id.bus); - return -EINVAL; - } + + bus = dev->bus; pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); @@ -1416,6 +1404,7 @@ static int acpiphp_configure_bridge (acpi_handle handle) acpiphp_set_hpp_values(handle, bus); pci_enable_bridges(bus); acpiphp_configure_ioapics(handle); + pci_dev_put(dev); return 0; } @@ -1631,7 +1620,7 @@ find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) { int *count = (int *)context; - if (acpi_root_bridge(handle)) { + if (acpi_is_root_bridge(handle)) { acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, handle_hotplug_event_bridge, NULL); (*count)++; diff --git a/drivers/pci/hotplug/cpci_hotplug_core.c b/drivers/pci/hotplug/cpci_hotplug_core.c index de94f4feef8..a5b9f6ae507 100644 --- a/drivers/pci/hotplug/cpci_hotplug_core.c +++ b/drivers/pci/hotplug/cpci_hotplug_core.c @@ -72,7 +72,6 @@ static int get_adapter_status(struct hotplug_slot *slot, u8 * value); static int get_latch_status(struct hotplug_slot *slot, u8 * value); static struct hotplug_slot_ops cpci_hotplug_slot_ops = { - .owner = THIS_MODULE, .enable_slot = enable_slot, .disable_slot = disable_slot, .set_attention_status = set_attention_status, diff --git a/drivers/pci/hotplug/cpqphp.h b/drivers/pci/hotplug/cpqphp.h index afaf8f69f73..53836001d51 100644 --- a/drivers/pci/hotplug/cpqphp.h +++ b/drivers/pci/hotplug/cpqphp.h @@ -150,25 +150,25 @@ struct ctrl_reg { /* offset */ /* offsets to the controller registers based on the above structure layout */ enum ctrl_offsets { - SLOT_RST = offsetof(struct ctrl_reg, slot_RST), + SLOT_RST = offsetof(struct ctrl_reg, slot_RST), SLOT_ENABLE = offsetof(struct ctrl_reg, slot_enable), MISC = offsetof(struct ctrl_reg, misc), LED_CONTROL = offsetof(struct ctrl_reg, led_control), INT_INPUT_CLEAR = offsetof(struct ctrl_reg, int_input_clear), - INT_MASK = offsetof(struct ctrl_reg, int_mask), - CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0), + INT_MASK = offsetof(struct ctrl_reg, int_mask), + CTRL_RESERVED0 = offsetof(struct ctrl_reg, reserved0), CTRL_RESERVED1 = offsetof(struct ctrl_reg, reserved1), CTRL_RESERVED2 = offsetof(struct ctrl_reg, reserved1), - GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB), - NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input), + GEN_OUTPUT_AB = offsetof(struct ctrl_reg, gen_output_AB), + NON_INT_INPUT = offsetof(struct ctrl_reg, non_int_input), CTRL_RESERVED3 = offsetof(struct ctrl_reg, reserved3), CTRL_RESERVED4 = offsetof(struct ctrl_reg, reserved4), CTRL_RESERVED5 = offsetof(struct ctrl_reg, reserved5), CTRL_RESERVED6 = offsetof(struct ctrl_reg, reserved6), CTRL_RESERVED7 = offsetof(struct ctrl_reg, reserved7), CTRL_RESERVED8 = offsetof(struct ctrl_reg, reserved8), - SLOT_MASK = offsetof(struct ctrl_reg, slot_mask), - CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9), + SLOT_MASK = offsetof(struct ctrl_reg, slot_mask), + CTRL_RESERVED9 = offsetof(struct ctrl_reg, reserved9), CTRL_RESERVED10 = offsetof(struct ctrl_reg, reserved10), CTRL_RESERVED11 = offsetof(struct ctrl_reg, reserved11), SLOT_SERR = offsetof(struct ctrl_reg, slot_SERR), @@ -190,7 +190,9 @@ struct hrt { u32 reserved2; } __attribute__ ((packed)); -/* offsets to the hotplug resource table registers based on the above structure layout */ +/* offsets to the hotplug resource table registers based on the above + * structure layout + */ enum hrt_offsets { SIG0 = offsetof(struct hrt, sig0), SIG1 = offsetof(struct hrt, sig1), @@ -217,18 +219,20 @@ struct slot_rt { u16 pre_mem_length; } __attribute__ ((packed)); -/* offsets to the hotplug slot resource table registers based on the above structure layout */ +/* offsets to the hotplug slot resource table registers based on the above + * structure layout + */ enum slot_rt_offsets { DEV_FUNC = offsetof(struct slot_rt, dev_func), - PRIMARY_BUS = offsetof(struct slot_rt, primary_bus), - SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus), - MAX_BUS = offsetof(struct slot_rt, max_bus), - IO_BASE = offsetof(struct slot_rt, io_base), - IO_LENGTH = offsetof(struct slot_rt, io_length), - MEM_BASE = offsetof(struct slot_rt, mem_base), - MEM_LENGTH = offsetof(struct slot_rt, mem_length), - PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base), - PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length), + PRIMARY_BUS = offsetof(struct slot_rt, primary_bus), + SECONDARY_BUS = offsetof(struct slot_rt, secondary_bus), + MAX_BUS = offsetof(struct slot_rt, max_bus), + IO_BASE = offsetof(struct slot_rt, io_base), + IO_LENGTH = offsetof(struct slot_rt, io_length), + MEM_BASE = offsetof(struct slot_rt, mem_base), + MEM_LENGTH = offsetof(struct slot_rt, mem_length), + PRE_MEM_BASE = offsetof(struct slot_rt, pre_mem_base), + PRE_MEM_LENGTH = offsetof(struct slot_rt, pre_mem_length), }; struct pci_func { @@ -286,8 +290,8 @@ struct event_info { struct controller { struct controller *next; u32 ctrl_int_comp; - struct mutex crit_sect; /* critical section mutex */ - void __iomem *hpc_reg; /* cookie for our pci controller location */ + struct mutex crit_sect; /* critical section mutex */ + void __iomem *hpc_reg; /* cookie for our pci controller location */ struct pci_resource *mem_head; struct pci_resource *p_mem_head; struct pci_resource *io_head; @@ -299,7 +303,7 @@ struct controller { u8 next_event; u8 interrupt; u8 cfgspc_irq; - u8 bus; /* bus number for the pci hotplug controller */ + u8 bus; /* bus number for the pci hotplug controller */ u8 rev; u8 slot_device_offset; u8 first_slot; @@ -401,46 +405,57 @@ struct resource_lists { /* debugfs functions for the hotplug controller info */ -extern void cpqhp_initialize_debugfs (void); -extern void cpqhp_shutdown_debugfs (void); -extern void cpqhp_create_debugfs_files (struct controller *ctrl); -extern void cpqhp_remove_debugfs_files (struct controller *ctrl); +extern void cpqhp_initialize_debugfs(void); +extern void cpqhp_shutdown_debugfs(void); +extern void cpqhp_create_debugfs_files(struct controller *ctrl); +extern void cpqhp_remove_debugfs_files(struct controller *ctrl); /* controller functions */ -extern void cpqhp_pushbutton_thread (unsigned long event_pointer); -extern irqreturn_t cpqhp_ctrl_intr (int IRQ, void *data); -extern int cpqhp_find_available_resources (struct controller *ctrl, void __iomem *rom_start); -extern int cpqhp_event_start_thread (void); -extern void cpqhp_event_stop_thread (void); -extern struct pci_func *cpqhp_slot_create (unsigned char busnumber); -extern struct pci_func *cpqhp_slot_find (unsigned char bus, unsigned char device, unsigned char index); -extern int cpqhp_process_SI (struct controller *ctrl, struct pci_func *func); -extern int cpqhp_process_SS (struct controller *ctrl, struct pci_func *func); -extern int cpqhp_hardware_test (struct controller *ctrl, int test_num); +extern void cpqhp_pushbutton_thread(unsigned long event_pointer); +extern irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data); +extern int cpqhp_find_available_resources(struct controller *ctrl, + void __iomem *rom_start); +extern int cpqhp_event_start_thread(void); +extern void cpqhp_event_stop_thread(void); +extern struct pci_func *cpqhp_slot_create(unsigned char busnumber); +extern struct pci_func *cpqhp_slot_find(unsigned char bus, unsigned char device, + unsigned char index); +extern int cpqhp_process_SI(struct controller *ctrl, struct pci_func *func); +extern int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func); +extern int cpqhp_hardware_test(struct controller *ctrl, int test_num); /* resource functions */ extern int cpqhp_resource_sort_and_combine (struct pci_resource **head); /* pci functions */ -extern int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); -extern int cpqhp_get_bus_dev (struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot); -extern int cpqhp_save_config (struct controller *ctrl, int busnumber, int is_hot_plug); -extern int cpqhp_save_base_addr_length (struct controller *ctrl, struct pci_func * func); -extern int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func); -extern int cpqhp_configure_board (struct controller *ctrl, struct pci_func * func); -extern int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot); -extern int cpqhp_valid_replace (struct controller *ctrl, struct pci_func * func); -extern void cpqhp_destroy_board_resources (struct pci_func * func); -extern int cpqhp_return_board_resources (struct pci_func * func, struct resource_lists * resources); -extern void cpqhp_destroy_resource_list (struct resource_lists * resources); -extern int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func); -extern int cpqhp_unconfigure_device (struct pci_func* func); +extern int cpqhp_set_irq(u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num); +extern int cpqhp_get_bus_dev(struct controller *ctrl, u8 *bus_num, u8 *dev_num, + u8 slot); +extern int cpqhp_save_config(struct controller *ctrl, int busnumber, + int is_hot_plug); +extern int cpqhp_save_base_addr_length(struct controller *ctrl, + struct pci_func *func); +extern int cpqhp_save_used_resources(struct controller *ctrl, + struct pci_func *func); +extern int cpqhp_configure_board(struct controller *ctrl, + struct pci_func *func); +extern int cpqhp_save_slot_config(struct controller *ctrl, + struct pci_func *new_slot); +extern int cpqhp_valid_replace(struct controller *ctrl, struct pci_func *func); +extern void cpqhp_destroy_board_resources(struct pci_func *func); +extern int cpqhp_return_board_resources (struct pci_func *func, + struct resource_lists *resources); +extern void cpqhp_destroy_resource_list(struct resource_lists *resources); +extern int cpqhp_configure_device(struct controller *ctrl, + struct pci_func *func); +extern int cpqhp_unconfigure_device(struct pci_func *func); /* Global variables */ extern int cpqhp_debug; extern int cpqhp_legacy_mode; extern struct controller *cpqhp_ctrl_list; extern struct pci_func *cpqhp_slot_list[256]; +extern struct irq_routing_table *cpqhp_routing_table; /* these can be gotten rid of, but for debugging they are purty */ extern u8 cpqhp_nic_irq; @@ -449,7 +464,7 @@ extern u8 cpqhp_disk_irq; /* inline functions */ -static inline char *slot_name(struct slot *slot) +static inline const char *slot_name(struct slot *slot) { return hotplug_slot_name(slot->hotplug_slot); } @@ -458,9 +473,9 @@ static inline char *slot_name(struct slot *slot) * return_resource * * Puts node back in the resource list pointed to by head - * */ -static inline void return_resource(struct pci_resource **head, struct pci_resource *node) +static inline void return_resource(struct pci_resource **head, + struct pci_resource *node) { if (!node || !head) return; @@ -471,7 +486,7 @@ static inline void return_resource(struct pci_resource **head, struct pci_resour static inline void set_SOGO(struct controller *ctrl) { u16 misc; - + misc = readw(ctrl->hpc_reg + MISC); misc = (misc | 0x0001) & 0xFFFB; writew(misc, ctrl->hpc_reg + MISC); @@ -481,7 +496,7 @@ static inline void set_SOGO(struct controller *ctrl) static inline void amber_LED_on(struct controller *ctrl, u8 slot) { u32 led_control; - + led_control = readl(ctrl->hpc_reg + LED_CONTROL); led_control |= (0x01010000L << slot); writel(led_control, ctrl->hpc_reg + LED_CONTROL); @@ -491,7 +506,7 @@ static inline void amber_LED_on(struct controller *ctrl, u8 slot) static inline void amber_LED_off(struct controller *ctrl, u8 slot) { u32 led_control; - + led_control = readl(ctrl->hpc_reg + LED_CONTROL); led_control &= ~(0x01010000L << slot); writel(led_control, ctrl->hpc_reg + LED_CONTROL); @@ -504,7 +519,7 @@ static inline int read_amber_LED(struct controller *ctrl, u8 slot) led_control = readl(ctrl->hpc_reg + LED_CONTROL); led_control &= (0x01010000L << slot); - + return led_control ? 1 : 0; } @@ -512,7 +527,7 @@ static inline int read_amber_LED(struct controller *ctrl, u8 slot) static inline void green_LED_on(struct controller *ctrl, u8 slot) { u32 led_control; - + led_control = readl(ctrl->hpc_reg + LED_CONTROL); led_control |= 0x0101L << slot; writel(led_control, ctrl->hpc_reg + LED_CONTROL); @@ -521,7 +536,7 @@ static inline void green_LED_on(struct controller *ctrl, u8 slot) static inline void green_LED_off(struct controller *ctrl, u8 slot) { u32 led_control; - + led_control = readl(ctrl->hpc_reg + LED_CONTROL); led_control &= ~(0x0101L << slot); writel(led_control, ctrl->hpc_reg + LED_CONTROL); @@ -531,7 +546,7 @@ static inline void green_LED_off(struct controller *ctrl, u8 slot) static inline void green_LED_blink(struct controller *ctrl, u8 slot) { u32 led_control; - + led_control = readl(ctrl->hpc_reg + LED_CONTROL); led_control &= ~(0x0101L << slot); led_control |= (0x0001L << slot); @@ -575,22 +590,21 @@ static inline u8 read_slot_enable(struct controller *ctrl) } -/* +/** * get_controller_speed - find the current frequency/mode of controller. * * @ctrl: controller to get frequency/mode for. * * Returns controller speed. - * */ static inline u8 get_controller_speed(struct controller *ctrl) { u8 curr_freq; - u16 misc; - + u16 misc; + if (ctrl->pcix_support) { curr_freq = readb(ctrl->hpc_reg + NEXT_CURR_FREQ); - if ((curr_freq & 0xB0) == 0xB0) + if ((curr_freq & 0xB0) == 0xB0) return PCI_SPEED_133MHz_PCIX; if ((curr_freq & 0xA0) == 0xA0) return PCI_SPEED_100MHz_PCIX; @@ -602,19 +616,18 @@ static inline u8 get_controller_speed(struct controller *ctrl) return PCI_SPEED_33MHz; } - misc = readw(ctrl->hpc_reg + MISC); - return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; + misc = readw(ctrl->hpc_reg + MISC); + return (misc & 0x0800) ? PCI_SPEED_66MHz : PCI_SPEED_33MHz; } - -/* + +/** * get_adapter_speed - find the max supported frequency/mode of adapter. * * @ctrl: hotplug controller. * @hp_slot: hotplug slot where adapter is installed. * * Returns adapter speed. - * */ static inline u8 get_adapter_speed(struct controller *ctrl, u8 hp_slot) { @@ -672,7 +685,8 @@ static inline int get_slot_enabled(struct controller *ctrl, struct slot *slot) } -static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slot) +static inline int cpq_get_latch_status(struct controller *ctrl, + struct slot *slot) { u32 status; u8 hp_slot; @@ -687,7 +701,8 @@ static inline int cpq_get_latch_status(struct controller *ctrl, struct slot *slo } -static inline int get_presence_status(struct controller *ctrl, struct slot *slot) +static inline int get_presence_status(struct controller *ctrl, + struct slot *slot) { int presence_save = 0; u8 hp_slot; @@ -696,7 +711,8 @@ static inline int get_presence_status(struct controller *ctrl, struct slot *slot hp_slot = slot->device - ctrl->slot_device_offset; tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); - presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) >> hp_slot) & 0x02; + presence_save = (int) ((((~tempdword) >> 23) | ((~tempdword) >> 15)) + >> hp_slot) & 0x02; return presence_save; } @@ -718,5 +734,12 @@ static inline int wait_for_ctrl_irq(struct controller *ctrl) return retval; } -#endif +#include <asm/pci_x86.h> +static inline int cpqhp_routing_table_length(void) +{ + BUG_ON(cpqhp_routing_table == NULL); + return ((cpqhp_routing_table->size - sizeof(struct irq_routing_table)) / + sizeof(struct irq_info)); +} +#endif diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c index c2e1bcbb28a..075b4f4b6e0 100644 --- a/drivers/pci/hotplug/cpqphp_core.c +++ b/drivers/pci/hotplug/cpqphp_core.c @@ -25,8 +25,7 @@ * Send feedback to <greg@kroah.com> * * Jan 12, 2003 - Added 66/100/133MHz PCI-X support, - * Torben Mathiasen <torben.mathiasen@hp.com> - * + * Torben Mathiasen <torben.mathiasen@hp.com> */ #include <linux/module.h> @@ -45,7 +44,6 @@ #include "cpqphp.h" #include "cpqphp_nvram.h" -#include <asm/pci_x86.h> /* Global variables */ @@ -53,6 +51,7 @@ int cpqhp_debug; int cpqhp_legacy_mode; struct controller *cpqhp_ctrl_list; /* = NULL */ struct pci_func *cpqhp_slot_list[256]; +struct irq_routing_table *cpqhp_routing_table; /* local variables */ static void __iomem *smbios_table; @@ -78,33 +77,6 @@ MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); #define CPQHPC_MODULE_MINOR 208 -static int one_time_init (void); -static int set_attention_status (struct hotplug_slot *slot, u8 value); -static int process_SI (struct hotplug_slot *slot); -static int process_SS (struct hotplug_slot *slot); -static int hardware_test (struct hotplug_slot *slot, u32 value); -static int get_power_status (struct hotplug_slot *slot, u8 *value); -static int get_attention_status (struct hotplug_slot *slot, u8 *value); -static int get_latch_status (struct hotplug_slot *slot, u8 *value); -static int get_adapter_status (struct hotplug_slot *slot, u8 *value); -static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); -static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); - -static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { - .owner = THIS_MODULE, - .set_attention_status = set_attention_status, - .enable_slot = process_SI, - .disable_slot = process_SS, - .hardware_test = hardware_test, - .get_power_status = get_power_status, - .get_attention_status = get_attention_status, - .get_latch_status = get_latch_status, - .get_adapter_status = get_adapter_status, - .get_max_bus_speed = get_max_bus_speed, - .get_cur_bus_speed = get_cur_bus_speed, -}; - - static inline int is_slot64bit(struct slot *slot) { return (readb(slot->p_sm_slot + SMBIOS_SLOT_WIDTH) == 0x06) ? 1 : 0; @@ -144,7 +116,7 @@ static void __iomem * detect_SMBIOS_pointer(void __iomem *begin, void __iomem *e break; } } - + if (!status) fp = NULL; @@ -171,7 +143,7 @@ static int init_SERR(struct controller * ctrl) tempdword = ctrl->first_slot; number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; - // Loop through slots + /* Loop through slots */ while (number_of_slots) { physical_slot = tempdword; writeb(0, ctrl->hpc_reg + SLOT_SERR); @@ -182,41 +154,42 @@ static int init_SERR(struct controller * ctrl) return 0; } - -/* nice debugging output */ -static int pci_print_IRQ_route (void) +static int init_cpqhp_routing_table(void) { - struct irq_routing_table *routing_table; int len; - int loop; - - u8 tbus, tdevice, tslot; - routing_table = pcibios_get_irq_routing_table(); - if (routing_table == NULL) { - err("No BIOS Routing Table??? Not good\n"); + cpqhp_routing_table = pcibios_get_irq_routing_table(); + if (cpqhp_routing_table == NULL) return -ENOMEM; - } - len = (routing_table->size - sizeof(struct irq_routing_table)) / - sizeof(struct irq_info); - // Make sure I got at least one entry + len = cpqhp_routing_table_length(); if (len == 0) { - kfree(routing_table); + kfree(cpqhp_routing_table); + cpqhp_routing_table = NULL; return -1; } - dbg("bus dev func slot\n"); + return 0; +} + +/* nice debugging output */ +static void pci_print_IRQ_route(void) +{ + int len; + int loop; + u8 tbus, tdevice, tslot; + + len = cpqhp_routing_table_length(); + dbg("bus dev func slot\n"); for (loop = 0; loop < len; ++loop) { - tbus = routing_table->slots[loop].bus; - tdevice = routing_table->slots[loop].devfn; - tslot = routing_table->slots[loop].slot; + tbus = cpqhp_routing_table->slots[loop].bus; + tdevice = cpqhp_routing_table->slots[loop].devfn; + tslot = cpqhp_routing_table->slots[loop].slot; dbg("%d %d %d %d\n", tbus, tdevice >> 3, tdevice & 0x7, tslot); } - kfree(routing_table); - return 0; + return; } @@ -242,9 +215,9 @@ static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start, void __iomem *p_max; if (!smbios_table || !curr) - return(NULL); + return NULL; - // set p_max to the end of the table + /* set p_max to the end of the table */ p_max = smbios_start + readw(smbios_table + ST_LENGTH); p_temp = curr; @@ -253,20 +226,19 @@ static void __iomem *get_subsequent_smbios_entry(void __iomem *smbios_start, while ((p_temp < p_max) && !bail) { /* Look for the double NULL terminator * The first condition is the previous byte - * and the second is the curr */ - if (!previous_byte && !(readb(p_temp))) { + * and the second is the curr + */ + if (!previous_byte && !(readb(p_temp))) bail = 1; - } previous_byte = readb(p_temp); p_temp++; } - if (p_temp < p_max) { + if (p_temp < p_max) return p_temp; - } else { + else return NULL; - } } @@ -292,21 +264,18 @@ static void __iomem *get_SMBIOS_entry(void __iomem *smbios_start, if (!smbios_table) return NULL; - if (!previous) { + if (!previous) previous = smbios_start; - } else { + else previous = get_subsequent_smbios_entry(smbios_start, smbios_table, previous); - } - while (previous) { - if (readb(previous + SMBIOS_GENERIC_TYPE) != type) { + while (previous) + if (readb(previous + SMBIOS_GENERIC_TYPE) != type) previous = get_subsequent_smbios_entry(smbios_start, smbios_table, previous); - } else { + else break; - } - } return previous; } @@ -322,144 +291,6 @@ static void release_slot(struct hotplug_slot *hotplug_slot) kfree(slot); } -#define SLOT_NAME_SIZE 10 - -static int ctrl_slot_setup(struct controller *ctrl, - void __iomem *smbios_start, - void __iomem *smbios_table) -{ - struct slot *slot; - struct hotplug_slot *hotplug_slot; - struct hotplug_slot_info *hotplug_slot_info; - u8 number_of_slots; - u8 slot_device; - u8 slot_number; - u8 ctrl_slot; - u32 tempdword; - char name[SLOT_NAME_SIZE]; - void __iomem *slot_entry= NULL; - int result = -ENOMEM; - - dbg("%s\n", __func__); - - tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); - - number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; - slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; - slot_number = ctrl->first_slot; - - while (number_of_slots) { - slot = kzalloc(sizeof(*slot), GFP_KERNEL); - if (!slot) - goto error; - - slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)), - GFP_KERNEL); - if (!slot->hotplug_slot) - goto error_slot; - hotplug_slot = slot->hotplug_slot; - - hotplug_slot->info = - kzalloc(sizeof(*(hotplug_slot->info)), - GFP_KERNEL); - if (!hotplug_slot->info) - goto error_hpslot; - hotplug_slot_info = hotplug_slot->info; - - slot->ctrl = ctrl; - slot->bus = ctrl->bus; - slot->device = slot_device; - slot->number = slot_number; - dbg("slot->number = %u\n", slot->number); - - slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9, - slot_entry); - - while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) != - slot->number)) { - slot_entry = get_SMBIOS_entry(smbios_start, - smbios_table, 9, slot_entry); - } - - slot->p_sm_slot = slot_entry; - - init_timer(&slot->task_event); - slot->task_event.expires = jiffies + 5 * HZ; - slot->task_event.function = cpqhp_pushbutton_thread; - - //FIXME: these capabilities aren't used but if they are - // they need to be correctly implemented - slot->capabilities |= PCISLOT_REPLACE_SUPPORTED; - slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED; - - if (is_slot64bit(slot)) - slot->capabilities |= PCISLOT_64_BIT_SUPPORTED; - if (is_slot66mhz(slot)) - slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED; - if (ctrl->speed == PCI_SPEED_66MHz) - slot->capabilities |= PCISLOT_66_MHZ_OPERATION; - - ctrl_slot = - slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4); - - // Check presence - slot->capabilities |= - ((((~tempdword) >> 23) | - ((~tempdword) >> 15)) >> ctrl_slot) & 0x02; - // Check the switch state - slot->capabilities |= - ((~tempdword & 0xFF) >> ctrl_slot) & 0x01; - // Check the slot enable - slot->capabilities |= - ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; - - /* register this slot with the hotplug pci core */ - hotplug_slot->release = &release_slot; - hotplug_slot->private = slot; - snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); - hotplug_slot->ops = &cpqphp_hotplug_slot_ops; - - hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot); - hotplug_slot_info->attention_status = - cpq_get_attention_status(ctrl, slot); - hotplug_slot_info->latch_status = - cpq_get_latch_status(ctrl, slot); - hotplug_slot_info->adapter_status = - get_presence_status(ctrl, slot); - - dbg("registering bus %d, dev %d, number %d, " - "ctrl->slot_device_offset %d, slot %d\n", - slot->bus, slot->device, - slot->number, ctrl->slot_device_offset, - slot_number); - result = pci_hp_register(hotplug_slot, - ctrl->pci_dev->bus, - slot->device, - name); - if (result) { - err("pci_hp_register failed with error %d\n", result); - goto error_info; - } - - slot->next = ctrl->slot; - ctrl->slot = slot; - - number_of_slots--; - slot_device++; - slot_number++; - } - - return 0; -error_info: - kfree(hotplug_slot_info); -error_hpslot: - kfree(hotplug_slot); -error_slot: - kfree(slot); -error: - return result; -} - static int ctrl_slot_cleanup (struct controller * ctrl) { struct slot *old_slot, *next_slot; @@ -476,36 +307,32 @@ static int ctrl_slot_cleanup (struct controller * ctrl) cpqhp_remove_debugfs_files(ctrl); - //Free IRQ associated with hot plug device + /* Free IRQ associated with hot plug device */ free_irq(ctrl->interrupt, ctrl); - //Unmap the memory + /* Unmap the memory */ iounmap(ctrl->hpc_reg); - //Finally reclaim PCI mem + /* Finally reclaim PCI mem */ release_mem_region(pci_resource_start(ctrl->pci_dev, 0), pci_resource_len(ctrl->pci_dev, 0)); - return(0); + return 0; } -//============================================================================ -// function: get_slot_mapping -// -// Description: Attempts to determine a logical slot mapping for a PCI -// device. Won't work for more than one PCI-PCI bridge -// in a slot. -// -// Input: u8 bus_num - bus number of PCI device -// u8 dev_num - device number of PCI device -// u8 *slot - Pointer to u8 where slot number will -// be returned -// -// Output: SUCCESS or FAILURE -//============================================================================= +/** + * get_slot_mapping - determine logical slot mapping for PCI device + * + * Won't work for more than one PCI-PCI bridge in a slot. + * + * @bus_num - bus number of PCI device + * @dev_num - device number of PCI device + * @slot - Pointer to u8 where slot number will be returned + * + * Output: SUCCESS or FAILURE + */ static int get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot) { - struct irq_routing_table *PCIIRQRoutingInfoLength; u32 work; long len; long loop; @@ -516,36 +343,25 @@ get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot) bridgeSlot = 0xFF; - PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); - if (!PCIIRQRoutingInfoLength) - return -1; - - len = (PCIIRQRoutingInfoLength->size - - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); - // Make sure I got at least one entry - if (len == 0) { - kfree(PCIIRQRoutingInfoLength); - return -1; - } - + len = cpqhp_routing_table_length(); for (loop = 0; loop < len; ++loop) { - tbus = PCIIRQRoutingInfoLength->slots[loop].bus; - tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn >> 3; - tslot = PCIIRQRoutingInfoLength->slots[loop].slot; + tbus = cpqhp_routing_table->slots[loop].bus; + tdevice = cpqhp_routing_table->slots[loop].devfn >> 3; + tslot = cpqhp_routing_table->slots[loop].slot; if ((tbus == bus_num) && (tdevice == dev_num)) { *slot = tslot; - kfree(PCIIRQRoutingInfoLength); return 0; } else { /* Did not get a match on the target PCI device. Check - * if the current IRQ table entry is a PCI-to-PCI bridge - * device. If so, and it's secondary bus matches the - * bus number for the target device, I need to save the - * bridge's slot number. If I can not find an entry for - * the target device, I will have to assume it's on the - * other side of the bridge, and assign it the bridge's - * slot. */ + * if the current IRQ table entry is a PCI-to-PCI + * bridge device. If so, and it's secondary bus + * matches the bus number for the target device, I need + * to save the bridge's slot number. If I can not find + * an entry for the target device, I will have to + * assume it's on the other side of the bridge, and + * assign it the bridge's slot. + */ bus->number = tbus; pci_bus_read_config_dword(bus, PCI_DEVFN(tdevice, 0), PCI_CLASS_REVISION, &work); @@ -555,25 +371,23 @@ get_slot_mapping(struct pci_bus *bus, u8 bus_num, u8 dev_num, u8 *slot) PCI_DEVFN(tdevice, 0), PCI_PRIMARY_BUS, &work); // See if bridge's secondary bus matches target bus. - if (((work >> 8) & 0x000000FF) == (long) bus_num) { + if (((work >> 8) & 0x000000FF) == (long) bus_num) bridgeSlot = tslot; - } } } } - // If we got here, we didn't find an entry in the IRQ mapping table - // for the target PCI device. If we did determine that the target - // device is on the other side of a PCI-to-PCI bridge, return the - // slot number for the bridge. + /* If we got here, we didn't find an entry in the IRQ mapping table for + * the target PCI device. If we did determine that the target device + * is on the other side of a PCI-to-PCI bridge, return the slot number + * for the bridge. + */ if (bridgeSlot != 0xFF) { *slot = bridgeSlot; - kfree(PCIIRQRoutingInfoLength); return 0; } - kfree(PCIIRQRoutingInfoLength); - // Couldn't find an entry in the routing table for this PCI device + /* Couldn't find an entry in the routing table for this PCI device */ return -1; } @@ -591,32 +405,32 @@ cpqhp_set_attention_status(struct controller *ctrl, struct pci_func *func, u8 hp_slot; if (func == NULL) - return(1); + return 1; hp_slot = func->device - ctrl->slot_device_offset; - // Wait for exclusive access to hardware + /* Wait for exclusive access to hardware */ mutex_lock(&ctrl->crit_sect); - if (status == 1) { + if (status == 1) amber_LED_on (ctrl, hp_slot); - } else if (status == 0) { + else if (status == 0) amber_LED_off (ctrl, hp_slot); - } else { - // Done with exclusive hardware access + else { + /* Done with exclusive hardware access */ mutex_unlock(&ctrl->crit_sect); - return(1); + return 1; } set_SOGO(ctrl); - // Wait for SOBS to be unset + /* Wait for SOBS to be unset */ wait_for_ctrl_irq (ctrl); - // Done with exclusive hardware access + /* Done with exclusive hardware access */ mutex_unlock(&ctrl->crit_sect); - return(0); + return 0; } @@ -719,7 +533,7 @@ static int hardware_test(struct hotplug_slot *hotplug_slot, u32 value) dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); - return cpqhp_hardware_test(ctrl, value); + return cpqhp_hardware_test(ctrl, value); } @@ -738,7 +552,7 @@ static int get_attention_status(struct hotplug_slot *hotplug_slot, u8 *value) { struct slot *slot = hotplug_slot->private; struct controller *ctrl = slot->ctrl; - + dbg("%s - physical_slot = %s\n", __func__, slot_name(slot)); *value = cpq_get_attention_status(ctrl, slot); @@ -793,6 +607,230 @@ static int get_cur_bus_speed (struct hotplug_slot *hotplug_slot, enum pci_bus_sp return 0; } +static struct hotplug_slot_ops cpqphp_hotplug_slot_ops = { + .set_attention_status = set_attention_status, + .enable_slot = process_SI, + .disable_slot = process_SS, + .hardware_test = hardware_test, + .get_power_status = get_power_status, + .get_attention_status = get_attention_status, + .get_latch_status = get_latch_status, + .get_adapter_status = get_adapter_status, + .get_max_bus_speed = get_max_bus_speed, + .get_cur_bus_speed = get_cur_bus_speed, +}; + +#define SLOT_NAME_SIZE 10 + +static int ctrl_slot_setup(struct controller *ctrl, + void __iomem *smbios_start, + void __iomem *smbios_table) +{ + struct slot *slot; + struct hotplug_slot *hotplug_slot; + struct hotplug_slot_info *hotplug_slot_info; + u8 number_of_slots; + u8 slot_device; + u8 slot_number; + u8 ctrl_slot; + u32 tempdword; + char name[SLOT_NAME_SIZE]; + void __iomem *slot_entry= NULL; + int result = -ENOMEM; + + dbg("%s\n", __func__); + + tempdword = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); + + number_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; + slot_device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; + slot_number = ctrl->first_slot; + + while (number_of_slots) { + slot = kzalloc(sizeof(*slot), GFP_KERNEL); + if (!slot) + goto error; + + slot->hotplug_slot = kzalloc(sizeof(*(slot->hotplug_slot)), + GFP_KERNEL); + if (!slot->hotplug_slot) + goto error_slot; + hotplug_slot = slot->hotplug_slot; + + hotplug_slot->info = kzalloc(sizeof(*(hotplug_slot->info)), + GFP_KERNEL); + if (!hotplug_slot->info) + goto error_hpslot; + hotplug_slot_info = hotplug_slot->info; + + slot->ctrl = ctrl; + slot->bus = ctrl->bus; + slot->device = slot_device; + slot->number = slot_number; + dbg("slot->number = %u\n", slot->number); + + slot_entry = get_SMBIOS_entry(smbios_start, smbios_table, 9, + slot_entry); + + while (slot_entry && (readw(slot_entry + SMBIOS_SLOT_NUMBER) != + slot->number)) { + slot_entry = get_SMBIOS_entry(smbios_start, + smbios_table, 9, slot_entry); + } + + slot->p_sm_slot = slot_entry; + + init_timer(&slot->task_event); + slot->task_event.expires = jiffies + 5 * HZ; + slot->task_event.function = cpqhp_pushbutton_thread; + + /*FIXME: these capabilities aren't used but if they are + * they need to be correctly implemented + */ + slot->capabilities |= PCISLOT_REPLACE_SUPPORTED; + slot->capabilities |= PCISLOT_INTERLOCK_SUPPORTED; + + if (is_slot64bit(slot)) + slot->capabilities |= PCISLOT_64_BIT_SUPPORTED; + if (is_slot66mhz(slot)) + slot->capabilities |= PCISLOT_66_MHZ_SUPPORTED; + if (ctrl->speed == PCI_SPEED_66MHz) + slot->capabilities |= PCISLOT_66_MHZ_OPERATION; + + ctrl_slot = + slot_device - (readb(ctrl->hpc_reg + SLOT_MASK) >> 4); + + /* Check presence */ + slot->capabilities |= + ((((~tempdword) >> 23) | + ((~tempdword) >> 15)) >> ctrl_slot) & 0x02; + /* Check the switch state */ + slot->capabilities |= + ((~tempdword & 0xFF) >> ctrl_slot) & 0x01; + /* Check the slot enable */ + slot->capabilities |= + ((read_slot_enable(ctrl) << 2) >> ctrl_slot) & 0x04; + + /* register this slot with the hotplug pci core */ + hotplug_slot->release = &release_slot; + hotplug_slot->private = slot; + snprintf(name, SLOT_NAME_SIZE, "%u", slot->number); + hotplug_slot->ops = &cpqphp_hotplug_slot_ops; + + hotplug_slot_info->power_status = get_slot_enabled(ctrl, slot); + hotplug_slot_info->attention_status = + cpq_get_attention_status(ctrl, slot); + hotplug_slot_info->latch_status = + cpq_get_latch_status(ctrl, slot); + hotplug_slot_info->adapter_status = + get_presence_status(ctrl, slot); + + dbg("registering bus %d, dev %d, number %d, " + "ctrl->slot_device_offset %d, slot %d\n", + slot->bus, slot->device, + slot->number, ctrl->slot_device_offset, + slot_number); + result = pci_hp_register(hotplug_slot, + ctrl->pci_dev->bus, + slot->device, + name); + if (result) { + err("pci_hp_register failed with error %d\n", result); + goto error_info; + } + + slot->next = ctrl->slot; + ctrl->slot = slot; + + number_of_slots--; + slot_device++; + slot_number++; + } + + return 0; +error_info: + kfree(hotplug_slot_info); +error_hpslot: + kfree(hotplug_slot); +error_slot: + kfree(slot); +error: + return result; +} + +static int one_time_init(void) +{ + int loop; + int retval = 0; + + if (initialized) + return 0; + + power_mode = 0; + + retval = init_cpqhp_routing_table(); + if (retval) + goto error; + + if (cpqhp_debug) + pci_print_IRQ_route(); + + dbg("Initialize + Start the notification mechanism \n"); + + retval = cpqhp_event_start_thread(); + if (retval) + goto error; + + dbg("Initialize slot lists\n"); + for (loop = 0; loop < 256; loop++) + cpqhp_slot_list[loop] = NULL; + + /* FIXME: We also need to hook the NMI handler eventually. + * this also needs to be worked with Christoph + * register_NMI_handler(); + */ + /* Map rom address */ + cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); + if (!cpqhp_rom_start) { + err ("Could not ioremap memory region for ROM\n"); + retval = -EIO; + goto error; + } + + /* Now, map the int15 entry point if we are on compaq specific + * hardware + */ + compaq_nvram_init(cpqhp_rom_start); + + /* Map smbios table entry point structure */ + smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start, + cpqhp_rom_start + ROM_PHY_LEN); + if (!smbios_table) { + err ("Could not find the SMBIOS pointer in memory\n"); + retval = -EIO; + goto error_rom_start; + } + + smbios_start = ioremap(readl(smbios_table + ST_ADDRESS), + readw(smbios_table + ST_LENGTH)); + if (!smbios_start) { + err ("Could not ioremap memory region taken from SMBIOS values\n"); + retval = -EIO; + goto error_smbios_start; + } + + initialized = 1; + + return retval; + +error_smbios_start: + iounmap(smbios_start); +error_rom_start: + iounmap(cpqhp_rom_start); +error: + return retval; +} + static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { u8 num_of_slots = 0; @@ -815,7 +853,9 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return err; } - // Need to read VID early b/c it's used to differentiate CPQ and INTC discovery + /* Need to read VID early b/c it's used to differentiate CPQ and INTC + * discovery + */ rc = pci_read_config_word(pdev, PCI_VENDOR_ID, &vendor_id); if (rc || ((vendor_id != PCI_VENDOR_ID_COMPAQ) && (vendor_id != PCI_VENDOR_ID_INTEL))) { err(msg_HPC_non_compaq_or_intel); @@ -832,217 +872,209 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } /* Check for the proper subsytem ID's - * Intel uses a different SSID programming model than Compaq. + * Intel uses a different SSID programming model than Compaq. * For Intel, each SSID bit identifies a PHP capability. * Also Intel HPC's may have RID=0. */ - if ((pdev->revision > 2) || (vendor_id == PCI_VENDOR_ID_INTEL)) { - // TODO: This code can be made to support non-Compaq or Intel subsystem IDs - rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid); - if (rc) { - err("%s : pci_read_config_word failed\n", __func__); - goto err_disable_device; - } - dbg("Subsystem Vendor ID: %x\n", subsystem_vid); - if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) { - err(msg_HPC_non_compaq_or_intel); - rc = -ENODEV; - goto err_disable_device; - } + if ((pdev->revision <= 2) && (vendor_id != PCI_VENDOR_ID_INTEL)) { + err(msg_HPC_not_supported); + return -ENODEV; + } - ctrl = kzalloc(sizeof(struct controller), GFP_KERNEL); - if (!ctrl) { - err("%s : out of memory\n", __func__); - rc = -ENOMEM; - goto err_disable_device; - } + /* TODO: This code can be made to support non-Compaq or Intel + * subsystem IDs + */ + rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsystem_vid); + if (rc) { + err("%s : pci_read_config_word failed\n", __func__); + goto err_disable_device; + } + dbg("Subsystem Vendor ID: %x\n", subsystem_vid); + if ((subsystem_vid != PCI_VENDOR_ID_COMPAQ) && (subsystem_vid != PCI_VENDOR_ID_INTEL)) { + err(msg_HPC_non_compaq_or_intel); + rc = -ENODEV; + goto err_disable_device; + } - rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid); - if (rc) { - err("%s : pci_read_config_word failed\n", __func__); - goto err_free_ctrl; - } + ctrl = kzalloc(sizeof(struct controller), GFP_KERNEL); + if (!ctrl) { + err("%s : out of memory\n", __func__); + rc = -ENOMEM; + goto err_disable_device; + } - info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid); - - /* Set Vendor ID, so it can be accessed later from other functions */ - ctrl->vendor_id = vendor_id; - - switch (subsystem_vid) { - case PCI_VENDOR_ID_COMPAQ: - if (pdev->revision >= 0x13) { /* CIOBX */ - ctrl->push_flag = 1; - ctrl->slot_switch_type = 1; - ctrl->push_button = 1; - ctrl->pci_config_space = 1; - ctrl->defeature_PHP = 1; - ctrl->pcix_support = 1; - ctrl->pcix_speed_capability = 1; - pci_read_config_byte(pdev, 0x41, &bus_cap); - if (bus_cap & 0x80) { - dbg("bus max supports 133MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_133MHz_PCIX; - break; - } - if (bus_cap & 0x40) { - dbg("bus max supports 100MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; - break; - } - if (bus_cap & 20) { - dbg("bus max supports 66MHz PCI-X\n"); - ctrl->speed_capability = PCI_SPEED_66MHz_PCIX; - break; - } - if (bus_cap & 10) { - dbg("bus max supports 66MHz PCI\n"); - ctrl->speed_capability = PCI_SPEED_66MHz; - break; - } - - break; - } - - switch (subsystem_deviceid) { - case PCI_SUB_HPC_ID: - /* Original 6500/7000 implementation */ - ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_33MHz; - ctrl->push_button = 0; - ctrl->pci_config_space = 1; - ctrl->defeature_PHP = 1; - ctrl->pcix_support = 0; - ctrl->pcix_speed_capability = 0; - break; - case PCI_SUB_HPC_ID2: - /* First Pushbutton implementation */ - ctrl->push_flag = 1; - ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_33MHz; - ctrl->push_button = 1; - ctrl->pci_config_space = 1; - ctrl->defeature_PHP = 1; - ctrl->pcix_support = 0; - ctrl->pcix_speed_capability = 0; - break; - case PCI_SUB_HPC_ID_INTC: - /* Third party (6500/7000) */ - ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_33MHz; - ctrl->push_button = 0; - ctrl->pci_config_space = 1; - ctrl->defeature_PHP = 1; - ctrl->pcix_support = 0; - ctrl->pcix_speed_capability = 0; - break; - case PCI_SUB_HPC_ID3: - /* First 66 Mhz implementation */ - ctrl->push_flag = 1; - ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_66MHz; - ctrl->push_button = 1; - ctrl->pci_config_space = 1; - ctrl->defeature_PHP = 1; - ctrl->pcix_support = 0; - ctrl->pcix_speed_capability = 0; - break; - case PCI_SUB_HPC_ID4: - /* First PCI-X implementation, 100MHz */ - ctrl->push_flag = 1; - ctrl->slot_switch_type = 1; - ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; - ctrl->push_button = 1; - ctrl->pci_config_space = 1; - ctrl->defeature_PHP = 1; - ctrl->pcix_support = 1; - ctrl->pcix_speed_capability = 0; - break; - default: - err(msg_HPC_not_supported); - rc = -ENODEV; - goto err_free_ctrl; - } - break; + rc = pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &subsystem_deviceid); + if (rc) { + err("%s : pci_read_config_word failed\n", __func__); + goto err_free_ctrl; + } - case PCI_VENDOR_ID_INTEL: - /* Check for speed capability (0=33, 1=66) */ - if (subsystem_deviceid & 0x0001) { - ctrl->speed_capability = PCI_SPEED_66MHz; - } else { - ctrl->speed_capability = PCI_SPEED_33MHz; - } - - /* Check for push button */ - if (subsystem_deviceid & 0x0002) { - /* no push button */ - ctrl->push_button = 0; - } else { - /* push button supported */ - ctrl->push_button = 1; - } - - /* Check for slot switch type (0=mechanical, 1=not mechanical) */ - if (subsystem_deviceid & 0x0004) { - /* no switch */ - ctrl->slot_switch_type = 0; - } else { - /* switch */ - ctrl->slot_switch_type = 1; - } - - /* PHP Status (0=De-feature PHP, 1=Normal operation) */ - if (subsystem_deviceid & 0x0008) { - ctrl->defeature_PHP = 1; // PHP supported - } else { - ctrl->defeature_PHP = 0; // PHP not supported - } - - /* Alternate Base Address Register Interface (0=not supported, 1=supported) */ - if (subsystem_deviceid & 0x0010) { - ctrl->alternate_base_address = 1; // supported - } else { - ctrl->alternate_base_address = 0; // not supported - } - - /* PCI Config Space Index (0=not supported, 1=supported) */ - if (subsystem_deviceid & 0x0020) { - ctrl->pci_config_space = 1; // supported - } else { - ctrl->pci_config_space = 0; // not supported - } - - /* PCI-X support */ - if (subsystem_deviceid & 0x0080) { - /* PCI-X capable */ - ctrl->pcix_support = 1; - /* Frequency of operation in PCI-X mode */ - if (subsystem_deviceid & 0x0040) { - /* 133MHz PCI-X if bit 7 is 1 */ - ctrl->pcix_speed_capability = 1; - } else { - /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */ - /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */ - ctrl->pcix_speed_capability = 0; - } - } else { - /* Conventional PCI */ - ctrl->pcix_support = 0; - ctrl->pcix_speed_capability = 0; - } + info("Hot Plug Subsystem Device ID: %x\n", subsystem_deviceid); + + /* Set Vendor ID, so it can be accessed later from other + * functions + */ + ctrl->vendor_id = vendor_id; + + switch (subsystem_vid) { + case PCI_VENDOR_ID_COMPAQ: + if (pdev->revision >= 0x13) { /* CIOBX */ + ctrl->push_flag = 1; + ctrl->slot_switch_type = 1; + ctrl->push_button = 1; + ctrl->pci_config_space = 1; + ctrl->defeature_PHP = 1; + ctrl->pcix_support = 1; + ctrl->pcix_speed_capability = 1; + pci_read_config_byte(pdev, 0x41, &bus_cap); + if (bus_cap & 0x80) { + dbg("bus max supports 133MHz PCI-X\n"); + ctrl->speed_capability = PCI_SPEED_133MHz_PCIX; break; + } + if (bus_cap & 0x40) { + dbg("bus max supports 100MHz PCI-X\n"); + ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; + break; + } + if (bus_cap & 20) { + dbg("bus max supports 66MHz PCI-X\n"); + ctrl->speed_capability = PCI_SPEED_66MHz_PCIX; + break; + } + if (bus_cap & 10) { + dbg("bus max supports 66MHz PCI\n"); + ctrl->speed_capability = PCI_SPEED_66MHz; + break; + } + + break; + } - default: - err(msg_HPC_not_supported); - rc = -ENODEV; - goto err_free_ctrl; + switch (subsystem_deviceid) { + case PCI_SUB_HPC_ID: + /* Original 6500/7000 implementation */ + ctrl->slot_switch_type = 1; + ctrl->speed_capability = PCI_SPEED_33MHz; + ctrl->push_button = 0; + ctrl->pci_config_space = 1; + ctrl->defeature_PHP = 1; + ctrl->pcix_support = 0; + ctrl->pcix_speed_capability = 0; + break; + case PCI_SUB_HPC_ID2: + /* First Pushbutton implementation */ + ctrl->push_flag = 1; + ctrl->slot_switch_type = 1; + ctrl->speed_capability = PCI_SPEED_33MHz; + ctrl->push_button = 1; + ctrl->pci_config_space = 1; + ctrl->defeature_PHP = 1; + ctrl->pcix_support = 0; + ctrl->pcix_speed_capability = 0; + break; + case PCI_SUB_HPC_ID_INTC: + /* Third party (6500/7000) */ + ctrl->slot_switch_type = 1; + ctrl->speed_capability = PCI_SPEED_33MHz; + ctrl->push_button = 0; + ctrl->pci_config_space = 1; + ctrl->defeature_PHP = 1; + ctrl->pcix_support = 0; + ctrl->pcix_speed_capability = 0; + break; + case PCI_SUB_HPC_ID3: + /* First 66 Mhz implementation */ + ctrl->push_flag = 1; + ctrl->slot_switch_type = 1; + ctrl->speed_capability = PCI_SPEED_66MHz; + ctrl->push_button = 1; + ctrl->pci_config_space = 1; + ctrl->defeature_PHP = 1; + ctrl->pcix_support = 0; + ctrl->pcix_speed_capability = 0; + break; + case PCI_SUB_HPC_ID4: + /* First PCI-X implementation, 100MHz */ + ctrl->push_flag = 1; + ctrl->slot_switch_type = 1; + ctrl->speed_capability = PCI_SPEED_100MHz_PCIX; + ctrl->push_button = 1; + ctrl->pci_config_space = 1; + ctrl->defeature_PHP = 1; + ctrl->pcix_support = 1; + ctrl->pcix_speed_capability = 0; + break; + default: + err(msg_HPC_not_supported); + rc = -ENODEV; + goto err_free_ctrl; } + break; + + case PCI_VENDOR_ID_INTEL: + /* Check for speed capability (0=33, 1=66) */ + if (subsystem_deviceid & 0x0001) + ctrl->speed_capability = PCI_SPEED_66MHz; + else + ctrl->speed_capability = PCI_SPEED_33MHz; + + /* Check for push button */ + if (subsystem_deviceid & 0x0002) + ctrl->push_button = 0; + else + ctrl->push_button = 1; + + /* Check for slot switch type (0=mechanical, 1=not mechanical) */ + if (subsystem_deviceid & 0x0004) + ctrl->slot_switch_type = 0; + else + ctrl->slot_switch_type = 1; + + /* PHP Status (0=De-feature PHP, 1=Normal operation) */ + if (subsystem_deviceid & 0x0008) + ctrl->defeature_PHP = 1; /* PHP supported */ + else + ctrl->defeature_PHP = 0; /* PHP not supported */ + + /* Alternate Base Address Register Interface + * (0=not supported, 1=supported) + */ + if (subsystem_deviceid & 0x0010) + ctrl->alternate_base_address = 1; + else + ctrl->alternate_base_address = 0; + + /* PCI Config Space Index (0=not supported, 1=supported) */ + if (subsystem_deviceid & 0x0020) + ctrl->pci_config_space = 1; + else + ctrl->pci_config_space = 0; + + /* PCI-X support */ + if (subsystem_deviceid & 0x0080) { + ctrl->pcix_support = 1; + if (subsystem_deviceid & 0x0040) + /* 133MHz PCI-X if bit 7 is 1 */ + ctrl->pcix_speed_capability = 1; + else + /* 100MHz PCI-X if bit 7 is 1 and bit 0 is 0, */ + /* 66MHz PCI-X if bit 7 is 1 and bit 0 is 1 */ + ctrl->pcix_speed_capability = 0; + } else { + /* Conventional PCI */ + ctrl->pcix_support = 0; + ctrl->pcix_speed_capability = 0; + } + break; - } else { + default: err(msg_HPC_not_supported); - return -ENODEV; + rc = -ENODEV; + goto err_free_ctrl; } - // Tell the user that we found one. + /* Tell the user that we found one. */ info("Initializing the PCI hot plug controller residing on PCI bus %d\n", pdev->bus->number); @@ -1087,7 +1119,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (rc) { goto err_free_bus; } - + dbg("pdev = %p\n", pdev); dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0)); dbg("pci resource len %llx\n", (unsigned long long)pci_resource_len(pdev, 0)); @@ -1109,7 +1141,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_free_mem_region; } - // Check for 66Mhz operation + /* Check for 66Mhz operation */ ctrl->speed = get_controller_speed(ctrl); @@ -1120,7 +1152,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * ********************************************************/ - // find the physical slot number of the first hot plug slot + /* find the physical slot number of the first hot plug slot */ /* Get slot won't work for devices behind bridges, but * in this case it will always be called for the "base" @@ -1137,7 +1169,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_iounmap; } - // Store PCI Config Space for all devices on this bus + /* Store PCI Config Space for all devices on this bus */ rc = cpqhp_save_config(ctrl, ctrl->bus, readb(ctrl->hpc_reg + SLOT_MASK)); if (rc) { err("%s: unable to save PCI configuration data, error %d\n", @@ -1148,7 +1180,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) /* * Get IO, memory, and IRQ resources for new devices */ - // The next line is required for cpqhp_find_available_resources + /* The next line is required for cpqhp_find_available_resources */ ctrl->interrupt = pdev->irq; if (ctrl->interrupt < 0x10) { cpqhp_legacy_mode = 1; @@ -1182,7 +1214,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) __func__, rc); goto err_iounmap; } - + /* Mask all general input interrupts */ writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_MASK); @@ -1196,12 +1228,14 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_iounmap; } - /* Enable Shift Out interrupt and clear it, also enable SERR on power fault */ + /* Enable Shift Out interrupt and clear it, also enable SERR on power + * fault + */ temp_word = readw(ctrl->hpc_reg + MISC); temp_word |= 0x4006; writew(temp_word, ctrl->hpc_reg + MISC); - // Changed 05/05/97 to clear all interrupts at start + /* Changed 05/05/97 to clear all interrupts at start */ writel(0xFFFFFFFFL, ctrl->hpc_reg + INT_INPUT_CLEAR); ctrl->ctrl_int_comp = readl(ctrl->hpc_reg + INT_INPUT_CLEAR); @@ -1216,13 +1250,14 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) cpqhp_ctrl_list = ctrl; } - // turn off empty slots here unless command line option "ON" set - // Wait for exclusive access to hardware + /* turn off empty slots here unless command line option "ON" set + * Wait for exclusive access to hardware + */ mutex_lock(&ctrl->crit_sect); num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0F; - // find first device number for the ctrl + /* find first device number for the ctrl */ device = readb(ctrl->hpc_reg + SLOT_MASK) >> 4; while (num_of_slots) { @@ -1234,23 +1269,21 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) hp_slot = func->device - ctrl->slot_device_offset; dbg("hp_slot: %d\n", hp_slot); - // We have to save the presence info for these slots + /* We have to save the presence info for these slots */ temp_word = ctrl->ctrl_int_comp >> 16; func->presence_save = (temp_word >> hp_slot) & 0x01; func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; - if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { + if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) func->switch_save = 0; - } else { + else func->switch_save = 0x10; - } - if (!power_mode) { + if (!power_mode) if (!func->is_a_board) { green_LED_off(ctrl, hp_slot); slot_disable(ctrl, hp_slot); } - } device++; num_of_slots--; @@ -1258,7 +1291,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (!power_mode) { set_SOGO(ctrl); - // Wait for SOBS to be unset + /* Wait for SOBS to be unset */ wait_for_ctrl_irq(ctrl); } @@ -1269,7 +1302,7 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_free_irq; } - // Done with exclusive hardware access + /* Done with exclusive hardware access */ mutex_unlock(&ctrl->crit_sect); cpqhp_create_debugfs_files(ctrl); @@ -1291,77 +1324,6 @@ err_disable_device: return rc; } - -static int one_time_init(void) -{ - int loop; - int retval = 0; - - if (initialized) - return 0; - - power_mode = 0; - - retval = pci_print_IRQ_route(); - if (retval) - goto error; - - dbg("Initialize + Start the notification mechanism \n"); - - retval = cpqhp_event_start_thread(); - if (retval) - goto error; - - dbg("Initialize slot lists\n"); - for (loop = 0; loop < 256; loop++) { - cpqhp_slot_list[loop] = NULL; - } - - // FIXME: We also need to hook the NMI handler eventually. - // this also needs to be worked with Christoph - // register_NMI_handler(); - - // Map rom address - cpqhp_rom_start = ioremap(ROM_PHY_ADDR, ROM_PHY_LEN); - if (!cpqhp_rom_start) { - err ("Could not ioremap memory region for ROM\n"); - retval = -EIO; - goto error; - } - - /* Now, map the int15 entry point if we are on compaq specific hardware */ - compaq_nvram_init(cpqhp_rom_start); - - /* Map smbios table entry point structure */ - smbios_table = detect_SMBIOS_pointer(cpqhp_rom_start, - cpqhp_rom_start + ROM_PHY_LEN); - if (!smbios_table) { - err ("Could not find the SMBIOS pointer in memory\n"); - retval = -EIO; - goto error_rom_start; - } - - smbios_start = ioremap(readl(smbios_table + ST_ADDRESS), - readw(smbios_table + ST_LENGTH)); - if (!smbios_start) { - err ("Could not ioremap memory region taken from SMBIOS values\n"); - retval = -EIO; - goto error_smbios_start; - } - - initialized = 1; - - return retval; - -error_smbios_start: - iounmap(smbios_start); -error_rom_start: - iounmap(cpqhp_rom_start); -error: - return retval; -} - - static void __exit unload_cpqphpd(void) { struct pci_func *next; @@ -1381,10 +1343,10 @@ static void __exit unload_cpqphpd(void) if (ctrl->hpc_reg) { u16 misc; rc = read_slot_enable (ctrl); - + writeb(0, ctrl->hpc_reg + SLOT_SERR); writel(0xFFFFFFC0L | ~rc, ctrl->hpc_reg + INT_MASK); - + misc = readw(ctrl->hpc_reg + MISC); misc &= 0xFFFD; writew(misc, ctrl->hpc_reg + MISC); @@ -1464,38 +1426,34 @@ static void __exit unload_cpqphpd(void) } } - // Stop the notification mechanism + /* Stop the notification mechanism */ if (initialized) cpqhp_event_stop_thread(); - //unmap the rom address + /* unmap the rom address */ if (cpqhp_rom_start) iounmap(cpqhp_rom_start); if (smbios_start) iounmap(smbios_start); } - - static struct pci_device_id hpcd_pci_tbl[] = { { /* handle any PCI Hotplug controller */ .class = ((PCI_CLASS_SYSTEM_PCI_HOTPLUG << 8) | 0x00), .class_mask = ~0, - + /* no matter who makes it */ .vendor = PCI_ANY_ID, .device = PCI_ANY_ID, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - + }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(pci, hpcd_pci_tbl); - - static struct pci_driver cpqhpc_driver = { .name = "compaq_pci_hotplug", .id_table = hpcd_pci_tbl, @@ -1503,8 +1461,6 @@ static struct pci_driver cpqhpc_driver = { /* remove: cpqhpc_remove_one, */ }; - - static int __init cpqhpc_init(void) { int result; @@ -1518,7 +1474,6 @@ static int __init cpqhpc_init(void) return result; } - static void __exit cpqhpc_cleanup(void) { dbg("unload_cpqphpd()\n"); @@ -1529,8 +1484,5 @@ static void __exit cpqhpc_cleanup(void) cpqhp_shutdown_debugfs(); } - module_init(cpqhpc_init); module_exit(cpqhpc_cleanup); - - diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index cc227a8c4b1..2fa47af992a 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -81,14 +81,15 @@ static u8 handle_switch_change(u8 change, struct controller * ctrl) for (hp_slot = 0; hp_slot < 6; hp_slot++) { if (change & (0x1L << hp_slot)) { - /********************************** + /* * this one changed. - **********************************/ + */ func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); /* this is the structure that tells the worker thread - *what to do */ + * what to do + */ taskInfo = &(ctrl->event_queue[ctrl->next_event]); ctrl->next_event = (ctrl->next_event + 1) % 10; taskInfo->hp_slot = hp_slot; @@ -100,17 +101,17 @@ static u8 handle_switch_change(u8 change, struct controller * ctrl) func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; if (ctrl->ctrl_int_comp & (0x1L << hp_slot)) { - /********************************** + /* * Switch opened - **********************************/ + */ func->switch_save = 0; taskInfo->event_type = INT_SWITCH_OPEN; } else { - /********************************** + /* * Switch closed - **********************************/ + */ func->switch_save = 0x10; @@ -131,9 +132,8 @@ static struct slot *cpqhp_find_slot(struct controller *ctrl, u8 device) { struct slot *slot = ctrl->slot; - while (slot && (slot->device != device)) { + while (slot && (slot->device != device)) slot = slot->next; - } return slot; } @@ -152,17 +152,17 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl) if (!change) return 0; - /********************************** + /* * Presence Change - **********************************/ + */ dbg("cpqsbd: Presence/Notify input change.\n"); dbg(" Changed bits are 0x%4.4x\n", change ); for (hp_slot = 0; hp_slot < 6; hp_slot++) { if (change & (0x0101 << hp_slot)) { - /********************************** + /* * this one changed. - **********************************/ + */ func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); @@ -177,22 +177,23 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl) return 0; /* If the switch closed, must be a button - * If not in button mode, nevermind */ + * If not in button mode, nevermind + */ if (func->switch_save && (ctrl->push_button == 1)) { temp_word = ctrl->ctrl_int_comp >> 16; temp_byte = (temp_word >> hp_slot) & 0x01; temp_byte |= (temp_word >> (hp_slot + 7)) & 0x02; if (temp_byte != func->presence_save) { - /************************************** + /* * button Pressed (doesn't do anything) - **************************************/ + */ dbg("hp_slot %d button pressed\n", hp_slot); taskInfo->event_type = INT_BUTTON_PRESS; } else { - /********************************** + /* * button Released - TAKE ACTION!!!! - **********************************/ + */ dbg("hp_slot %d button released\n", hp_slot); taskInfo->event_type = INT_BUTTON_RELEASE; @@ -210,7 +211,8 @@ static u8 handle_presence_change(u16 change, struct controller * ctrl) } } else { /* Switch is open, assume a presence change - * Save the presence state */ + * Save the presence state + */ temp_word = ctrl->ctrl_int_comp >> 16; func->presence_save = (temp_word >> hp_slot) & 0x01; func->presence_save |= (temp_word >> (hp_slot + 7)) & 0x02; @@ -241,17 +243,17 @@ static u8 handle_power_fault(u8 change, struct controller * ctrl) if (!change) return 0; - /********************************** + /* * power fault - **********************************/ + */ info("power fault interrupt\n"); for (hp_slot = 0; hp_slot < 6; hp_slot++) { if (change & (0x01 << hp_slot)) { - /********************************** + /* * this one changed. - **********************************/ + */ func = cpqhp_slot_find(ctrl->bus, (hp_slot + ctrl->slot_device_offset), 0); @@ -262,16 +264,16 @@ static u8 handle_power_fault(u8 change, struct controller * ctrl) rc++; if (ctrl->ctrl_int_comp & (0x00000100 << hp_slot)) { - /********************************** + /* * power fault Cleared - **********************************/ + */ func->status = 0x00; taskInfo->event_type = INT_POWER_FAULT_CLEAR; } else { - /********************************** + /* * power fault - **********************************/ + */ taskInfo->event_type = INT_POWER_FAULT; if (ctrl->rev < 4) { @@ -432,13 +434,15 @@ static struct pci_resource *do_pre_bridge_resource_split(struct pci_resource **h /* If we got here, there the bridge requires some of the resource, but - * we may be able to split some off of the front */ + * we may be able to split some off of the front + */ node = *head; if (node->length & (alignment -1)) { /* this one isn't an aligned length, so we'll make a new entry - * and split it up. */ + * and split it up. + */ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); if (!split_node) @@ -544,10 +548,10 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size if (!(*head)) return NULL; - if ( cpqhp_resource_sort_and_combine(head) ) + if (cpqhp_resource_sort_and_combine(head)) return NULL; - if ( sort_by_size(head) ) + if (sort_by_size(head)) return NULL; for (node = *head; node; node = node->next) { @@ -556,7 +560,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size if (node->base & (size - 1)) { /* this one isn't base aligned properly - * so we'll make a new entry and split it up */ + * so we'll make a new entry and split it up + */ temp_dword = (node->base | (size-1)) + 1; /* Short circuit if adjusted size is too small */ @@ -581,7 +586,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size /* Don't need to check if too small since we already did */ if (node->length > size) { /* this one is longer than we need - * so we'll make a new entry and split it up */ + * so we'll make a new entry and split it up + */ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); if (!split_node) @@ -601,7 +607,8 @@ static struct pci_resource *get_io_resource(struct pci_resource **head, u32 size continue; /* If we got here, then it is the right size - * Now take it out of the list and break */ + * Now take it out of the list and break + */ if (*head == node) { *head = node->next; } else { @@ -642,14 +649,16 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz return NULL; for (max = *head; max; max = max->next) { - /* If not big enough we could probably just bail, - * instead we'll continue to the next. */ + /* If not big enough we could probably just bail, + * instead we'll continue to the next. + */ if (max->length < size) continue; if (max->base & (size - 1)) { /* this one isn't base aligned properly - * so we'll make a new entry and split it up */ + * so we'll make a new entry and split it up + */ temp_dword = (max->base | (size-1)) + 1; /* Short circuit if adjusted size is too small */ @@ -672,7 +681,8 @@ static struct pci_resource *get_max_resource(struct pci_resource **head, u32 siz if ((max->base + max->length) & (size - 1)) { /* this one isn't end aligned properly at the top - * so we'll make a new entry and split it up */ + * so we'll make a new entry and split it up + */ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); if (!split_node) @@ -744,7 +754,8 @@ static struct pci_resource *get_resource(struct pci_resource **head, u32 size) if (node->base & (size - 1)) { dbg("%s: not aligned\n", __func__); /* this one isn't base aligned properly - * so we'll make a new entry and split it up */ + * so we'll make a new entry and split it up + */ temp_dword = (node->base | (size-1)) + 1; /* Short circuit if adjusted size is too small */ @@ -769,7 +780,8 @@ static struct pci_resource *get_resource(struct pci_resource **head, u32 size) if (node->length > size) { dbg("%s: too big\n", __func__); /* this one is longer than we need - * so we'll make a new entry and split it up */ + * so we'll make a new entry and split it up + */ split_node = kmalloc(sizeof(*split_node), GFP_KERNEL); if (!split_node) @@ -886,19 +898,19 @@ irqreturn_t cpqhp_ctrl_intr(int IRQ, void *data) u32 Diff; u32 temp_dword; - + misc = readw(ctrl->hpc_reg + MISC); - /*************************************** + /* * Check to see if it was our interrupt - ***************************************/ + */ if (!(misc & 0x000C)) { return IRQ_NONE; } if (misc & 0x0004) { - /********************************** + /* * Serial Output interrupt Pending - **********************************/ + */ /* Clear the interrupt */ misc |= 0x0004; @@ -961,11 +973,8 @@ struct pci_func *cpqhp_slot_create(u8 busnumber) struct pci_func *next; new_slot = kzalloc(sizeof(*new_slot), GFP_KERNEL); - if (new_slot == NULL) { - /* I'm not dead yet! - * You will be. */ + if (new_slot == NULL) return new_slot; - } new_slot->next = NULL; new_slot->configured = 1; @@ -996,10 +1005,8 @@ static int slot_remove(struct pci_func * old_slot) return 1; next = cpqhp_slot_list[old_slot->bus]; - - if (next == NULL) { + if (next == NULL) return 1; - } if (next == old_slot) { cpqhp_slot_list[old_slot->bus] = old_slot->next; @@ -1008,9 +1015,8 @@ static int slot_remove(struct pci_func * old_slot) return 0; } - while ((next->next != old_slot) && (next->next != NULL)) { + while ((next->next != old_slot) && (next->next != NULL)) next = next->next; - } if (next->next == old_slot) { next->next = old_slot->next; @@ -1040,9 +1046,8 @@ static int bridge_slot_remove(struct pci_func *bridge) for (tempBus = secondaryBus; tempBus <= subordinateBus; tempBus++) { next = cpqhp_slot_list[tempBus]; - while (!slot_remove(next)) { + while (!slot_remove(next)) next = cpqhp_slot_list[tempBus]; - } } next = cpqhp_slot_list[bridge->bus]; @@ -1130,39 +1135,43 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ u8 slot_power = readb(ctrl->hpc_reg + SLOT_POWER); u16 reg16; u32 leds = readl(ctrl->hpc_reg + LED_CONTROL); - + if (ctrl->speed == adapter_speed) return 0; - + /* We don't allow freq/mode changes if we find another adapter running - * in another slot on this controller */ + * in another slot on this controller + */ for(slot = ctrl->slot; slot; slot = slot->next) { - if (slot->device == (hp_slot + ctrl->slot_device_offset)) + if (slot->device == (hp_slot + ctrl->slot_device_offset)) continue; if (!slot->hotplug_slot || !slot->hotplug_slot->info) continue; - if (slot->hotplug_slot->info->adapter_status == 0) + if (slot->hotplug_slot->info->adapter_status == 0) continue; /* If another adapter is running on the same segment but at a * lower speed/mode, we allow the new adapter to function at - * this rate if supported */ - if (ctrl->speed < adapter_speed) + * this rate if supported + */ + if (ctrl->speed < adapter_speed) return 0; return 1; } - + /* If the controller doesn't support freq/mode changes and the - * controller is running at a higher mode, we bail */ + * controller is running at a higher mode, we bail + */ if ((ctrl->speed > adapter_speed) && (!ctrl->pcix_speed_capability)) return 1; - + /* But we allow the adapter to run at a lower rate if possible */ if ((ctrl->speed < adapter_speed) && (!ctrl->pcix_speed_capability)) return 0; /* We try to set the max speed supported by both the adapter and - * controller */ + * controller + */ if (ctrl->speed_capability < adapter_speed) { if (ctrl->speed == ctrl->speed_capability) return 0; @@ -1171,22 +1180,22 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ writel(0x0L, ctrl->hpc_reg + LED_CONTROL); writeb(0x00, ctrl->hpc_reg + SLOT_ENABLE); - - set_SOGO(ctrl); + + set_SOGO(ctrl); wait_for_ctrl_irq(ctrl); - + if (adapter_speed != PCI_SPEED_133MHz_PCIX) reg = 0xF5; else - reg = 0xF4; + reg = 0xF4; pci_write_config_byte(ctrl->pci_dev, 0x41, reg); - + reg16 = readw(ctrl->hpc_reg + NEXT_CURR_FREQ); reg16 &= ~0x000F; switch(adapter_speed) { - case(PCI_SPEED_133MHz_PCIX): + case(PCI_SPEED_133MHz_PCIX): reg = 0x75; - reg16 |= 0xB; + reg16 |= 0xB; break; case(PCI_SPEED_100MHz_PCIX): reg = 0x74; @@ -1203,48 +1212,48 @@ static u8 set_controller_speed(struct controller *ctrl, u8 adapter_speed, u8 hp_ default: /* 33MHz PCI 2.2 */ reg = 0x71; break; - + } reg16 |= 0xB << 12; writew(reg16, ctrl->hpc_reg + NEXT_CURR_FREQ); - - mdelay(5); - + + mdelay(5); + /* Reenable interrupts */ writel(0, ctrl->hpc_reg + INT_MASK); - pci_write_config_byte(ctrl->pci_dev, 0x41, reg); - + pci_write_config_byte(ctrl->pci_dev, 0x41, reg); + /* Restart state machine */ reg = ~0xF; pci_read_config_byte(ctrl->pci_dev, 0x43, ®); pci_write_config_byte(ctrl->pci_dev, 0x43, reg); - + /* Only if mode change...*/ if (((ctrl->speed == PCI_SPEED_66MHz) && (adapter_speed == PCI_SPEED_66MHz_PCIX)) || ((ctrl->speed == PCI_SPEED_66MHz_PCIX) && (adapter_speed == PCI_SPEED_66MHz))) set_SOGO(ctrl); - + wait_for_ctrl_irq(ctrl); mdelay(1100); - + /* Restore LED/Slot state */ writel(leds, ctrl->hpc_reg + LED_CONTROL); writeb(slot_power, ctrl->hpc_reg + SLOT_ENABLE); - + set_SOGO(ctrl); wait_for_ctrl_irq(ctrl); ctrl->speed = adapter_speed; slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); - info("Successfully changed frequency/mode for adapter in slot %d\n", + info("Successfully changed frequency/mode for adapter in slot %d\n", slot->number); return 0; } -/* the following routines constitute the bulk of the - hotplug controller logic +/* the following routines constitute the bulk of the + * hotplug controller logic */ @@ -1268,17 +1277,17 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl) hp_slot = func->device - ctrl->slot_device_offset; - if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) { - /********************************** - * The switch is open. - **********************************/ + /* + * The switch is open. + */ + if (readl(ctrl->hpc_reg + INT_INPUT_CLEAR) & (0x01L << hp_slot)) rc = INTERLOCK_OPEN; - } else if (is_slot_enabled (ctrl, hp_slot)) { - /********************************** - * The board is already on - **********************************/ + /* + * The board is already on + */ + else if (is_slot_enabled (ctrl, hp_slot)) rc = CARD_FUNCTIONING; - } else { + else { mutex_lock(&ctrl->crit_sect); /* turn on board without attaching to the bus */ @@ -1299,7 +1308,7 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl) /* Wait for SOBS to be unset */ wait_for_ctrl_irq (ctrl); - + adapter_speed = get_adapter_speed(ctrl, hp_slot); if (ctrl->speed != adapter_speed) if (set_controller_speed(ctrl, adapter_speed, hp_slot)) @@ -1352,7 +1361,8 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl) * Get slot won't work for devices behind * bridges, but in this case it will always be * called for the "base" bus/dev/func of an - * adapter. */ + * adapter. + */ mutex_lock(&ctrl->crit_sect); @@ -1377,7 +1387,8 @@ static u32 board_replaced(struct pci_func *func, struct controller *ctrl) * Get slot won't work for devices behind bridges, but * in this case it will always be called for the "base" - * bus/dev/func of an adapter. */ + * bus/dev/func of an adapter. + */ mutex_lock(&ctrl->crit_sect); @@ -1434,7 +1445,8 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl) wait_for_ctrl_irq (ctrl); /* Change bits in slot power register to force another shift out - * NOTE: this is to work around the timer bug */ + * NOTE: this is to work around the timer bug + */ temp_byte = readb(ctrl->hpc_reg + SLOT_POWER); writeb(0x00, ctrl->hpc_reg + SLOT_POWER); writeb(temp_byte, ctrl->hpc_reg + SLOT_POWER); @@ -1443,12 +1455,12 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl) /* Wait for SOBS to be unset */ wait_for_ctrl_irq (ctrl); - + adapter_speed = get_adapter_speed(ctrl, hp_slot); if (ctrl->speed != adapter_speed) if (set_controller_speed(ctrl, adapter_speed, hp_slot)) rc = WRONG_BUS_FREQUENCY; - + /* turn off board without attaching to the bus */ disable_slot_power (ctrl, hp_slot); @@ -1461,7 +1473,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl) if (rc) return rc; - + p_slot = cpqhp_find_slot(ctrl, hp_slot + ctrl->slot_device_offset); /* turn on board and blink green LED */ @@ -1521,7 +1533,7 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl) } /* All F's is an empty slot or an invalid board */ - if (temp_register != 0xFFFFFFFF) { /* Check for a board in the slot */ + if (temp_register != 0xFFFFFFFF) { res_lists.io_head = ctrl->io_head; res_lists.mem_head = ctrl->mem_head; res_lists.p_mem_head = ctrl->p_mem_head; @@ -1570,9 +1582,8 @@ static u32 board_added(struct pci_func *func, struct controller *ctrl) index = 0; do { new_slot = cpqhp_slot_find(ctrl->bus, func->device, index++); - if (new_slot && !new_slot->pci_dev) { + if (new_slot && !new_slot->pci_dev) cpqhp_configure_device(ctrl, new_slot); - } } while (new_slot); mutex_lock(&ctrl->crit_sect); @@ -1859,12 +1870,12 @@ static void interrupt_event_handler(struct controller *ctrl) info(msg_button_on, p_slot->number); } mutex_lock(&ctrl->crit_sect); - + dbg("blink green LED and turn off amber\n"); - + amber_LED_off (ctrl, hp_slot); green_LED_blink (ctrl, hp_slot); - + set_SOGO(ctrl); /* Wait for SOBS to be unset */ @@ -1958,7 +1969,7 @@ void cpqhp_pushbutton_thread(unsigned long slot) if (cpqhp_process_SI(ctrl, func) != 0) { amber_LED_on(ctrl, hp_slot); green_LED_off(ctrl, hp_slot); - + set_SOGO(ctrl); /* Wait for SOBS to be unset */ @@ -2079,7 +2090,7 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func) struct pci_bus *pci_bus = ctrl->pci_bus; int physical_slot=0; - device = func->device; + device = func->device; func = cpqhp_slot_find(ctrl->bus, device, index++); p_slot = cpqhp_find_slot(ctrl, device); if (p_slot) { @@ -2113,9 +2124,8 @@ int cpqhp_process_SS(struct controller *ctrl, struct pci_func *func) /* If the VGA Enable bit is set, remove isn't * supported */ - if (BCR & PCI_BRIDGE_CTL_VGA) { + if (BCR & PCI_BRIDGE_CTL_VGA) rc = REMOVE_NOT_SUPPORTED; - } } } @@ -2183,67 +2193,67 @@ int cpqhp_hardware_test(struct controller *ctrl, int test_num) num_of_slots = readb(ctrl->hpc_reg + SLOT_MASK) & 0x0f; switch (test_num) { - case 1: - /* Do stuff here! */ - - /* Do that funky LED thing */ - /* so we can restore them later */ - save_LED = readl(ctrl->hpc_reg + LED_CONTROL); - work_LED = 0x01010101; - switch_leds(ctrl, num_of_slots, &work_LED, 0); - switch_leds(ctrl, num_of_slots, &work_LED, 1); - switch_leds(ctrl, num_of_slots, &work_LED, 0); - switch_leds(ctrl, num_of_slots, &work_LED, 1); - - work_LED = 0x01010000; - writel(work_LED, ctrl->hpc_reg + LED_CONTROL); - switch_leds(ctrl, num_of_slots, &work_LED, 0); - switch_leds(ctrl, num_of_slots, &work_LED, 1); - work_LED = 0x00000101; - writel(work_LED, ctrl->hpc_reg + LED_CONTROL); - switch_leds(ctrl, num_of_slots, &work_LED, 0); - switch_leds(ctrl, num_of_slots, &work_LED, 1); + case 1: + /* Do stuff here! */ + + /* Do that funky LED thing */ + /* so we can restore them later */ + save_LED = readl(ctrl->hpc_reg + LED_CONTROL); + work_LED = 0x01010101; + switch_leds(ctrl, num_of_slots, &work_LED, 0); + switch_leds(ctrl, num_of_slots, &work_LED, 1); + switch_leds(ctrl, num_of_slots, &work_LED, 0); + switch_leds(ctrl, num_of_slots, &work_LED, 1); + + work_LED = 0x01010000; + writel(work_LED, ctrl->hpc_reg + LED_CONTROL); + switch_leds(ctrl, num_of_slots, &work_LED, 0); + switch_leds(ctrl, num_of_slots, &work_LED, 1); + work_LED = 0x00000101; + writel(work_LED, ctrl->hpc_reg + LED_CONTROL); + switch_leds(ctrl, num_of_slots, &work_LED, 0); + switch_leds(ctrl, num_of_slots, &work_LED, 1); + + work_LED = 0x01010000; + writel(work_LED, ctrl->hpc_reg + LED_CONTROL); + for (loop = 0; loop < num_of_slots; loop++) { + set_SOGO(ctrl); - work_LED = 0x01010000; - writel(work_LED, ctrl->hpc_reg + LED_CONTROL); - for (loop = 0; loop < num_of_slots; loop++) { - set_SOGO(ctrl); + /* Wait for SOGO interrupt */ + wait_for_ctrl_irq (ctrl); - /* Wait for SOGO interrupt */ - wait_for_ctrl_irq (ctrl); + /* Get ready for next iteration */ + long_delay((3*HZ)/10); + work_LED = work_LED >> 16; + writel(work_LED, ctrl->hpc_reg + LED_CONTROL); - /* Get ready for next iteration */ - long_delay((3*HZ)/10); - work_LED = work_LED >> 16; - writel(work_LED, ctrl->hpc_reg + LED_CONTROL); - - set_SOGO(ctrl); + set_SOGO(ctrl); - /* Wait for SOGO interrupt */ - wait_for_ctrl_irq (ctrl); + /* Wait for SOGO interrupt */ + wait_for_ctrl_irq (ctrl); - /* Get ready for next iteration */ - long_delay((3*HZ)/10); - work_LED = work_LED << 16; - writel(work_LED, ctrl->hpc_reg + LED_CONTROL); - work_LED = work_LED << 1; - writel(work_LED, ctrl->hpc_reg + LED_CONTROL); - } + /* Get ready for next iteration */ + long_delay((3*HZ)/10); + work_LED = work_LED << 16; + writel(work_LED, ctrl->hpc_reg + LED_CONTROL); + work_LED = work_LED << 1; + writel(work_LED, ctrl->hpc_reg + LED_CONTROL); + } - /* put it back the way it was */ - writel(save_LED, ctrl->hpc_reg + LED_CONTROL); + /* put it back the way it was */ + writel(save_LED, ctrl->hpc_reg + LED_CONTROL); - set_SOGO(ctrl); + set_SOGO(ctrl); - /* Wait for SOBS to be unset */ - wait_for_ctrl_irq (ctrl); - break; - case 2: - /* Do other stuff here! */ - break; - case 3: - /* and more... */ - break; + /* Wait for SOBS to be unset */ + wait_for_ctrl_irq (ctrl); + break; + case 2: + /* Do other stuff here! */ + break; + case 3: + /* and more... */ + break; } return 0; } @@ -2312,9 +2322,9 @@ static u32 configure_new_device(struct controller * ctrl, struct pci_func * func while ((function < max_functions) && (!stop_it)) { pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(func->device, function), 0x00, &ID); - if (ID == 0xFFFFFFFF) { /* There's nothing there. */ + if (ID == 0xFFFFFFFF) { function++; - } else { /* There's something there */ + } else { /* Setup slot structure. */ new_slot = cpqhp_slot_create(func->bus); @@ -2339,8 +2349,8 @@ static u32 configure_new_device(struct controller * ctrl, struct pci_func * func /* - Configuration logic that involves the hotplug data structures and - their bookkeeping + * Configuration logic that involves the hotplug data structures and + * their bookkeeping */ @@ -2393,7 +2403,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func if (rc) return rc; - if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* PCI-PCI Bridge */ + if ((temp_byte & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { /* set Primary bus */ dbg("set Primary bus = %d\n", func->bus); rc = pci_bus_write_config_byte(pci_bus, devfn, PCI_PRIMARY_BUS, func->bus); @@ -2484,7 +2494,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func temp_resources.irqs = &irqs; /* Make copies of the nodes we are going to pass down so that - * if there is a problem,we can just use these to free resources */ + * if there is a problem,we can just use these to free resources + */ hold_bus_node = kmalloc(sizeof(*hold_bus_node), GFP_KERNEL); hold_IO_node = kmalloc(sizeof(*hold_IO_node), GFP_KERNEL); hold_mem_node = kmalloc(sizeof(*hold_mem_node), GFP_KERNEL); @@ -2556,7 +2567,8 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func temp_word = (p_mem_node->base + p_mem_node->length - 1) >> 16; rc = pci_bus_write_config_word (pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, temp_word); - /* Adjust this to compensate for extra adjustment in first loop */ + /* Adjust this to compensate for extra adjustment in first loop + */ irqs.barber_pole--; rc = 0; @@ -2917,27 +2929,26 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func } /* End of base register loop */ if (cpqhp_legacy_mode) { /* Figure out which interrupt pin this function uses */ - rc = pci_bus_read_config_byte (pci_bus, devfn, + rc = pci_bus_read_config_byte (pci_bus, devfn, PCI_INTERRUPT_PIN, &temp_byte); /* If this function needs an interrupt and we are behind * a bridge and the pin is tied to something that's * alread mapped, set this one the same */ - if (temp_byte && resources->irqs && - (resources->irqs->valid_INT & + if (temp_byte && resources->irqs && + (resources->irqs->valid_INT & (0x01 << ((temp_byte + resources->irqs->barber_pole - 1) & 0x03)))) { /* We have to share with something already set up */ - IRQ = resources->irqs->interrupt[(temp_byte + + IRQ = resources->irqs->interrupt[(temp_byte + resources->irqs->barber_pole - 1) & 0x03]; } else { /* Program IRQ based on card type */ rc = pci_bus_read_config_byte (pci_bus, devfn, 0x0B, &class_code); - if (class_code == PCI_BASE_CLASS_STORAGE) { + if (class_code == PCI_BASE_CLASS_STORAGE) IRQ = cpqhp_disk_irq; - } else { + else IRQ = cpqhp_nic_irq; - } } /* IRQ Line */ diff --git a/drivers/pci/hotplug/cpqphp_nvram.c b/drivers/pci/hotplug/cpqphp_nvram.c index cb174888002..76ba8a1c774 100644 --- a/drivers/pci/hotplug/cpqphp_nvram.c +++ b/drivers/pci/hotplug/cpqphp_nvram.c @@ -94,12 +94,13 @@ static u8 evbuffer[1024]; static void __iomem *compaq_int15_entry_point; -static spinlock_t int15_lock; /* lock for ordering int15_bios_call() */ +/* lock for ordering int15_bios_call() */ +static spinlock_t int15_lock; /* This is a series of function that deals with - setting & getting the hotplug resource table in some environment variable. -*/ + * setting & getting the hotplug resource table in some environment variable. + */ /* * We really shouldn't be doing this unless there is a _very_ good reason to!!! @@ -113,7 +114,7 @@ static u32 add_byte( u32 **p_buffer, u8 value, u32 *used, u32 *avail) if ((*used + 1) > *avail) return(1); - + *((u8*)*p_buffer) = value; tByte = (u8**)p_buffer; (*tByte)++; @@ -170,10 +171,10 @@ static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size) unsigned long flags; int op = operation; int ret_val; - + if (!compaq_int15_entry_point) return -ENODEV; - + spin_lock_irqsave(&int15_lock, flags); __asm__ ( "xorl %%ebx,%%ebx\n" \ @@ -187,7 +188,7 @@ static u32 access_EV (u16 operation, u8 *ev_name, u8 *buffer, u32 *buf_size) "D" (buffer), "m" (compaq_int15_entry_point) : "%ebx", "%edx"); spin_unlock_irqrestore(&int15_lock, flags); - + return((ret_val & 0xFF00) >> 8); } @@ -210,14 +211,16 @@ static int load_HRT (void __iomem *rom_start) available = 1024; - // Now load the EV + /* Now load the EV */ temp_dword = available; rc = access_EV(READ_EV, "CQTHPS", evbuffer, &temp_dword); evbuffer_length = temp_dword; - // We're maintaining the resource lists so write FF to invalidate old info + /* We're maintaining the resource lists so write FF to invalidate old + * info + */ temp_dword = 1; rc = access_EV(WRITE_EV, "CQTHPS", &temp_byte, &temp_dword); @@ -263,13 +266,13 @@ static u32 store_HRT (void __iomem *rom_start) p_EV_header = (struct ev_hrt_header *) pFill; ctrl = cpqhp_ctrl_list; - - // The revision of this structure + + /* The revision of this structure */ rc = add_byte( &pFill, 1 + ctrl->push_flag, &usedbytes, &available); if (rc) return(rc); - // The number of controllers + /* The number of controllers */ rc = add_byte( &pFill, 1, &usedbytes, &available); if (rc) return(rc); @@ -279,27 +282,27 @@ static u32 store_HRT (void __iomem *rom_start) numCtrl++; - // The bus number + /* The bus number */ rc = add_byte( &pFill, ctrl->bus, &usedbytes, &available); if (rc) return(rc); - // The device Number + /* The device Number */ rc = add_byte( &pFill, PCI_SLOT(ctrl->pci_dev->devfn), &usedbytes, &available); if (rc) return(rc); - // The function Number + /* The function Number */ rc = add_byte( &pFill, PCI_FUNC(ctrl->pci_dev->devfn), &usedbytes, &available); if (rc) return(rc); - // Skip the number of available entries + /* Skip the number of available entries */ rc = add_dword( &pFill, 0, &usedbytes, &available); if (rc) return(rc); - // Figure out memory Available + /* Figure out memory Available */ resNode = ctrl->mem_head; @@ -308,12 +311,12 @@ static u32 store_HRT (void __iomem *rom_start) while (resNode) { loop ++; - // base + /* base */ rc = add_dword( &pFill, resNode->base, &usedbytes, &available); if (rc) return(rc); - // length + /* length */ rc = add_dword( &pFill, resNode->length, &usedbytes, &available); if (rc) return(rc); @@ -321,10 +324,10 @@ static u32 store_HRT (void __iomem *rom_start) resNode = resNode->next; } - // Fill in the number of entries + /* Fill in the number of entries */ p_ev_ctrl->mem_avail = loop; - // Figure out prefetchable memory Available + /* Figure out prefetchable memory Available */ resNode = ctrl->p_mem_head; @@ -333,12 +336,12 @@ static u32 store_HRT (void __iomem *rom_start) while (resNode) { loop ++; - // base + /* base */ rc = add_dword( &pFill, resNode->base, &usedbytes, &available); if (rc) return(rc); - // length + /* length */ rc = add_dword( &pFill, resNode->length, &usedbytes, &available); if (rc) return(rc); @@ -346,10 +349,10 @@ static u32 store_HRT (void __iomem *rom_start) resNode = resNode->next; } - // Fill in the number of entries + /* Fill in the number of entries */ p_ev_ctrl->p_mem_avail = loop; - // Figure out IO Available + /* Figure out IO Available */ resNode = ctrl->io_head; @@ -358,12 +361,12 @@ static u32 store_HRT (void __iomem *rom_start) while (resNode) { loop ++; - // base + /* base */ rc = add_dword( &pFill, resNode->base, &usedbytes, &available); if (rc) return(rc); - // length + /* length */ rc = add_dword( &pFill, resNode->length, &usedbytes, &available); if (rc) return(rc); @@ -371,10 +374,10 @@ static u32 store_HRT (void __iomem *rom_start) resNode = resNode->next; } - // Fill in the number of entries + /* Fill in the number of entries */ p_ev_ctrl->io_avail = loop; - // Figure out bus Available + /* Figure out bus Available */ resNode = ctrl->bus_head; @@ -383,12 +386,12 @@ static u32 store_HRT (void __iomem *rom_start) while (resNode) { loop ++; - // base + /* base */ rc = add_dword( &pFill, resNode->base, &usedbytes, &available); if (rc) return(rc); - // length + /* length */ rc = add_dword( &pFill, resNode->length, &usedbytes, &available); if (rc) return(rc); @@ -396,15 +399,15 @@ static u32 store_HRT (void __iomem *rom_start) resNode = resNode->next; } - // Fill in the number of entries + /* Fill in the number of entries */ p_ev_ctrl->bus_avail = loop; ctrl = ctrl->next; } - + p_EV_header->num_of_ctrl = numCtrl; - // Now store the EV + /* Now store the EV */ temp_dword = usedbytes; @@ -449,20 +452,21 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) struct ev_hrt_header *p_EV_header; if (!evbuffer_init) { - // Read the resource list information in from NVRAM + /* Read the resource list information in from NVRAM */ if (load_HRT(rom_start)) memset (evbuffer, 0, 1024); evbuffer_init = 1; } - // If we saved information in NVRAM, use it now + /* If we saved information in NVRAM, use it now */ p_EV_header = (struct ev_hrt_header *) evbuffer; - // The following code is for systems where version 1.0 of this - // driver has been loaded, but doesn't support the hardware. - // In that case, the driver would incorrectly store something - // in NVRAM. + /* The following code is for systems where version 1.0 of this + * driver has been loaded, but doesn't support the hardware. + * In that case, the driver would incorrectly store something + * in NVRAM. + */ if ((p_EV_header->Version == 2) || ((p_EV_header->Version == 1) && !ctrl->push_flag)) { p_byte = &(p_EV_header->next); @@ -479,7 +483,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) function = p_ev_ctrl->function; while ((bus != ctrl->bus) || - (device != PCI_SLOT(ctrl->pci_dev->devfn)) || + (device != PCI_SLOT(ctrl->pci_dev->devfn)) || (function != PCI_FUNC(ctrl->pci_dev->devfn))) { nummem = p_ev_ctrl->mem_avail; numpmem = p_ev_ctrl->p_mem_avail; @@ -491,7 +495,7 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) if (p_byte > ((u8*)p_EV_header + evbuffer_length)) return 2; - // Skip forward to the next entry + /* Skip forward to the next entry */ p_byte += (nummem + numpmem + numio + numbus) * 8; if (p_byte > ((u8*)p_EV_header + evbuffer_length)) @@ -629,8 +633,9 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) ctrl->bus_head = bus_node; } - // If all of the following fail, we don't have any resources for - // hot plug add + /* If all of the following fail, we don't have any resources for + * hot plug add + */ rc = 1; rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); @@ -640,14 +645,14 @@ int compaq_nvram_load (void __iomem *rom_start, struct controller *ctrl) if (rc) return(rc); } else { - if ((evbuffer[0] != 0) && (!ctrl->push_flag)) + if ((evbuffer[0] != 0) && (!ctrl->push_flag)) return 1; } return 0; } - + int compaq_nvram_store (void __iomem *rom_start) { int rc = 1; diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index 6c0ed0fcb8e..6173b9a4544 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -37,7 +37,6 @@ #include "../pci.h" #include "cpqphp.h" #include "cpqphp_nvram.h" -#include <asm/pci_x86.h> u8 cpqhp_nic_irq; @@ -82,14 +81,14 @@ static void __iomem *detect_HRT_floating_pointer(void __iomem *begin, void __iom } -int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) +int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) { unsigned char bus; struct pci_bus *child; int num; if (func->pci_dev == NULL) - func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); + func->pci_dev = pci_get_bus_and_slot(func->bus,PCI_DEVFN(func->device, func->function)); /* No pci device, we need to create it then */ if (func->pci_dev == NULL) { @@ -99,7 +98,7 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) if (num) pci_bus_add_devices(ctrl->pci_dev->bus); - func->pci_dev = pci_find_slot(func->bus, PCI_DEVFN(func->device, func->function)); + func->pci_dev = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, func->function)); if (func->pci_dev == NULL) { dbg("ERROR: pci_dev still null\n"); return 0; @@ -112,20 +111,24 @@ int cpqhp_configure_device (struct controller* ctrl, struct pci_func* func) pci_do_scan_bus(child); } + pci_dev_put(func->pci_dev); + return 0; } -int cpqhp_unconfigure_device(struct pci_func* func) +int cpqhp_unconfigure_device(struct pci_func* func) { int j; - + dbg("%s: bus/dev/func = %x/%x/%x\n", __func__, func->bus, func->device, func->function); for (j=0; j<8 ; j++) { - struct pci_dev* temp = pci_find_slot(func->bus, PCI_DEVFN(func->device, j)); - if (temp) + struct pci_dev* temp = pci_get_bus_and_slot(func->bus, PCI_DEVFN(func->device, j)); + if (temp) { + pci_dev_put(temp); pci_remove_bus_device(temp); + } } return 0; } @@ -178,32 +181,22 @@ int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) if (!rc) return !rc; - // set the Edge Level Control Register (ELCR) + /* set the Edge Level Control Register (ELCR) */ temp_word = inb(0x4d0); temp_word |= inb(0x4d1) << 8; temp_word |= 0x01 << irq_num; - // This should only be for x86 as it sets the Edge Level Control Register - outb((u8) (temp_word & 0xFF), 0x4d0); - outb((u8) ((temp_word & 0xFF00) >> 8), 0x4d1); - rc = 0; - } + /* This should only be for x86 as it sets the Edge Level + * Control Register + */ + outb((u8) (temp_word & 0xFF), 0x4d0); outb((u8) ((temp_word & + 0xFF00) >> 8), 0x4d1); rc = 0; } return rc; } -/* - * WTF??? This function isn't in the code, yet a function calls it, but the - * compiler optimizes it away? strange. Here as a placeholder to keep the - * compiler happy. - */ -static int PCI_ScanBusNonBridge (u8 bus, u8 device) -{ - return 0; -} - static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev_num) { u16 tdevice; @@ -213,11 +206,11 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev ctrl->pci_bus->number = bus_num; for (tdevice = 0; tdevice < 0xFF; tdevice++) { - //Scan for access first + /* Scan for access first */ if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) continue; dbg("Looking for nonbridge bus_num %d dev_num %d\n", bus_num, tdevice); - //Yep we got one. Not a bridge ? + /* Yep we got one. Not a bridge ? */ if ((work >> 8) != PCI_TO_PCI_BRIDGE_CLASS) { *dev_num = tdevice; dbg("found it !\n"); @@ -225,16 +218,16 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev } } for (tdevice = 0; tdevice < 0xFF; tdevice++) { - //Scan for access first + /* Scan for access first */ if (PCI_RefinedAccessConfig(ctrl->pci_bus, tdevice, 0x08, &work) == -1) continue; dbg("Looking for bridge bus_num %d dev_num %d\n", bus_num, tdevice); - //Yep we got one. bridge ? + /* Yep we got one. bridge ? */ if ((work >> 8) == PCI_TO_PCI_BRIDGE_CLASS) { pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(tdevice, 0), PCI_SECONDARY_BUS, &tbus); + /* XXX: no recursion, wtf? */ dbg("Recurse on bus_num %d tdevice %d\n", tbus, tdevice); - if (PCI_ScanBusNonBridge(tbus, tdevice) == 0) - return 0; + return 0; } } @@ -244,39 +237,23 @@ static int PCI_ScanBusForNonBridge(struct controller *ctrl, u8 bus_num, u8 * dev static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num, u8 slot, u8 nobridge) { - struct irq_routing_table *PCIIRQRoutingInfoLength; - long len; - long loop; + int loop, len; u32 work; - u8 tbus, tdevice, tslot; - PCIIRQRoutingInfoLength = pcibios_get_irq_routing_table(); - if (!PCIIRQRoutingInfoLength) - return -1; - - len = (PCIIRQRoutingInfoLength->size - - sizeof(struct irq_routing_table)) / sizeof(struct irq_info); - // Make sure I got at least one entry - if (len == 0) { - kfree(PCIIRQRoutingInfoLength ); - return -1; - } - + len = cpqhp_routing_table_length(); for (loop = 0; loop < len; ++loop) { - tbus = PCIIRQRoutingInfoLength->slots[loop].bus; - tdevice = PCIIRQRoutingInfoLength->slots[loop].devfn; - tslot = PCIIRQRoutingInfoLength->slots[loop].slot; + tbus = cpqhp_routing_table->slots[loop].bus; + tdevice = cpqhp_routing_table->slots[loop].devfn; + tslot = cpqhp_routing_table->slots[loop].slot; if (tslot == slot) { *bus_num = tbus; *dev_num = tdevice; ctrl->pci_bus->number = tbus; pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_VENDOR_ID, &work); - if (!nobridge || (work == 0xffffffff)) { - kfree(PCIIRQRoutingInfoLength ); + if (!nobridge || (work == 0xffffffff)) return 0; - } dbg("bus_num %d devfn %d\n", *bus_num, *dev_num); pci_bus_read_config_dword (ctrl->pci_bus, *dev_num, PCI_CLASS_REVISION, &work); @@ -287,28 +264,26 @@ static int PCI_GetBusDevHelper(struct controller *ctrl, u8 *bus_num, u8 *dev_num dbg("Scan bus for Non Bridge: bus %d\n", tbus); if (PCI_ScanBusForNonBridge(ctrl, tbus, dev_num) == 0) { *bus_num = tbus; - kfree(PCIIRQRoutingInfoLength ); return 0; } - } else { - kfree(PCIIRQRoutingInfoLength ); + } else return 0; - } - } } - kfree(PCIIRQRoutingInfoLength ); return -1; } int cpqhp_get_bus_dev (struct controller *ctrl, u8 * bus_num, u8 * dev_num, u8 slot) { - return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); //plain (bridges allowed) + /* plain (bridges allowed) */ + return PCI_GetBusDevHelper(ctrl, bus_num, dev_num, slot, 0); } -/* More PCI configuration routines; this time centered around hotplug controller */ +/* More PCI configuration routines; this time centered around hotplug + * controller + */ /* @@ -339,12 +314,12 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug) int stop_it; int index; - // Decide which slots are supported + /* Decide which slots are supported */ if (is_hot_plug) { - //********************************* - // is_hot_plug is the slot mask - //********************************* + /* + * is_hot_plug is the slot mask + */ FirstSupported = is_hot_plug >> 4; LastSupported = FirstSupported + (is_hot_plug & 0x0F) - 1; } else { @@ -352,123 +327,127 @@ int cpqhp_save_config(struct controller *ctrl, int busnumber, int is_hot_plug) LastSupported = 0x1F; } - // Save PCI configuration space for all devices in supported slots + /* Save PCI configuration space for all devices in supported slots */ ctrl->pci_bus->number = busnumber; for (device = FirstSupported; device <= LastSupported; device++) { ID = 0xFFFFFFFF; - rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); + rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_VENDOR_ID, &ID); + + if (ID == 0xFFFFFFFF) { + if (is_hot_plug) { + /* Setup slot structure with entry for empty + * slot + */ + new_slot = cpqhp_slot_create(busnumber); + if (new_slot == NULL) + return 1; - if (ID != 0xFFFFFFFF) { // device in slot - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); - if (rc) - return rc; + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = 0; + new_slot->is_a_board = 0; + new_slot->presence_save = 0; + new_slot->switch_save = 0; + } + continue; + } - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type); - if (rc) - return rc; + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), 0x0B, &class_code); + if (rc) + return rc; - // If multi-function device, set max_functions to 8 - if (header_type & 0x80) - max_functions = 8; - else - max_functions = 1; + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, 0), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; - function = 0; + /* If multi-function device, set max_functions to 8 */ + if (header_type & 0x80) + max_functions = 8; + else + max_functions = 1; - do { - DevError = 0; + function = 0; - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // P-P Bridge - // Recurse the subordinate bus - // get the subordinate bus number - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); - if (rc) { + do { + DevError = 0; + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + /* Recurse the subordinate bus + * get the subordinate bus number + */ + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_SECONDARY_BUS, &secondary_bus); + if (rc) { + return rc; + } else { + sub_bus = (int) secondary_bus; + + /* Save secondary bus cfg spc + * with this recursive call. + */ + rc = cpqhp_save_config(ctrl, sub_bus, 0); + if (rc) return rc; - } else { - sub_bus = (int) secondary_bus; - - // Save secondary bus cfg spc - // with this recursive call. - rc = cpqhp_save_config(ctrl, sub_bus, 0); - if (rc) - return rc; - ctrl->pci_bus->number = busnumber; - } + ctrl->pci_bus->number = busnumber; } + } - index = 0; + index = 0; + new_slot = cpqhp_slot_find(busnumber, device, index++); + while (new_slot && + (new_slot->function != (u8) function)) new_slot = cpqhp_slot_find(busnumber, device, index++); - while (new_slot && - (new_slot->function != (u8) function)) - new_slot = cpqhp_slot_find(busnumber, device, index++); - if (!new_slot) { - // Setup slot structure. - new_slot = cpqhp_slot_create(busnumber); - - if (new_slot == NULL) - return(1); - } - - new_slot->bus = (u8) busnumber; - new_slot->device = (u8) device; - new_slot->function = (u8) function; - new_slot->is_a_board = 1; - new_slot->switch_save = 0x10; - // In case of unsupported board - new_slot->status = DevError; - new_slot->pci_dev = pci_find_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); - - for (cloop = 0; cloop < 0x20; cloop++) { - rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); - if (rc) - return rc; - } + if (!new_slot) { + /* Setup slot structure. */ + new_slot = cpqhp_slot_create(busnumber); + if (new_slot == NULL) + return 1; + } - function++; + new_slot->bus = (u8) busnumber; + new_slot->device = (u8) device; + new_slot->function = (u8) function; + new_slot->is_a_board = 1; + new_slot->switch_save = 0x10; + /* In case of unsupported board */ + new_slot->status = DevError; + new_slot->pci_dev = pci_get_bus_and_slot(new_slot->bus, (new_slot->device << 3) | new_slot->function); - stop_it = 0; + for (cloop = 0; cloop < 0x20; cloop++) { + rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); + if (rc) + return rc; + } - // this loop skips to the next present function - // reading in Class Code and Header type. + pci_dev_put(new_slot->pci_dev); - while ((function < max_functions)&&(!stop_it)) { - rc = pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); - if (ID == 0xFFFFFFFF) { // nothing there. - function++; - } else { // Something there - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); - if (rc) - return rc; + function++; - rc = pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); - if (rc) - return rc; + stop_it = 0; - stop_it++; - } + /* this loop skips to the next present function + * reading in Class Code and Header type. + */ + while ((function < max_functions) && (!stop_it)) { + rc = pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_VENDOR_ID, &ID); + if (ID == 0xFFFFFFFF) { + function++; + continue; } + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), 0x0B, &class_code); + if (rc) + return rc; - } while (function < max_functions); - } // End of IF (device in slot?) - else if (is_hot_plug) { - // Setup slot structure with entry for empty slot - new_slot = cpqhp_slot_create(busnumber); + rc = pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(device, function), PCI_HEADER_TYPE, &header_type); + if (rc) + return rc; - if (new_slot == NULL) { - return(1); + stop_it++; } - new_slot->bus = (u8) busnumber; - new_slot->device = (u8) device; - new_slot->function = 0; - new_slot->is_a_board = 0; - new_slot->presence_save = 0; - new_slot->switch_save = 0; - } - } // End of FOR loop + } while (function < max_functions); + } /* End of FOR loop */ - return(0); + return 0; } @@ -489,7 +468,7 @@ int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) u8 secondary_bus; int sub_bus; int max_functions; - int function; + int function = 0; int cloop = 0; int stop_it; @@ -498,63 +477,58 @@ int cpqhp_save_slot_config (struct controller *ctrl, struct pci_func * new_slot) ctrl->pci_bus->number = new_slot->bus; pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_VENDOR_ID, &ID); - if (ID != 0xFFFFFFFF) { // device in slot - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); - - if (header_type & 0x80) // Multi-function device - max_functions = 8; - else - max_functions = 1; - - function = 0; - - do { - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - // Recurse the subordinate bus - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); + if (ID == 0xFFFFFFFF) + return 2; - sub_bus = (int) secondary_bus; + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), 0x0B, &class_code); + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, 0), PCI_HEADER_TYPE, &header_type); - // Save the config headers for the secondary bus. - rc = cpqhp_save_config(ctrl, sub_bus, 0); - if (rc) - return(rc); - ctrl->pci_bus->number = new_slot->bus; + if (header_type & 0x80) /* Multi-function device */ + max_functions = 8; + else + max_functions = 1; - } // End of IF + while (function < max_functions) { + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + /* Recurse the subordinate bus */ + pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_SECONDARY_BUS, &secondary_bus); - new_slot->status = 0; + sub_bus = (int) secondary_bus; - for (cloop = 0; cloop < 0x20; cloop++) { - pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); - } + /* Save the config headers for the secondary + * bus. + */ + rc = cpqhp_save_config(ctrl, sub_bus, 0); + if (rc) + return(rc); + ctrl->pci_bus->number = new_slot->bus; - function++; + } - stop_it = 0; + new_slot->status = 0; - // this loop skips to the next present function - // reading in the Class Code and the Header type. + for (cloop = 0; cloop < 0x20; cloop++) + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), cloop << 2, (u32 *) & (new_slot-> config_space [cloop])); - while ((function < max_functions) && (!stop_it)) { - pci_bus_read_config_dword (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); + function++; - if (ID == 0xFFFFFFFF) { // nothing there. - function++; - } else { // Something there - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); + stop_it = 0; - pci_bus_read_config_byte (ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); + /* this loop skips to the next present function + * reading in the Class Code and the Header type. + */ + while ((function < max_functions) && (!stop_it)) { + pci_bus_read_config_dword(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_VENDOR_ID, &ID); - stop_it++; - } + if (ID == 0xFFFFFFFF) + function++; + else { + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), 0x0B, &class_code); + pci_bus_read_config_byte(ctrl->pci_bus, PCI_DEVFN(new_slot->device, function), PCI_HEADER_TYPE, &header_type); + stop_it++; } + } - } while (function < max_functions); - } // End of IF (device in slot?) - else { - return 2; } return 0; @@ -590,11 +564,10 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func) pci_bus->number = func->bus; devfn = PCI_DEVFN(func->device, func->function); - // Check for Bridge + /* Check for Bridge */ pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { - // PCI-PCI Bridge pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); sub_bus = (int) secondary_bus; @@ -610,23 +583,27 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func) } pci_bus->number = func->bus; - //FIXME: this loop is duplicated in the non-bridge case. The two could be rolled together - // Figure out IO and memory base lengths + /* FIXME: this loop is duplicated in the non-bridge + * case. The two could be rolled together Figure out + * IO and memory base lengths + */ for (cloop = 0x10; cloop <= 0x14; cloop += 4) { temp_register = 0xFFFFFFFF; pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - - if (base) { // If this register is implemented + /* If this register is implemented */ + if (base) { if (base & 0x01L) { - // IO base - // set base = amount of IO space requested + /* IO base + * set base = amount of IO space + * requested + */ base = base & 0xFFFFFFFE; base = (~base) + 1; type = 1; } else { - // memory base + /* memory base */ base = base & 0xFFFFFFF0; base = (~base) + 1; @@ -637,32 +614,36 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func) type = 0; } - // Save information in slot structure + /* Save information in slot structure */ func->base_length[(cloop - 0x10) >> 2] = base; func->base_type[(cloop - 0x10) >> 2] = type; - } // End of base register loop + } /* End of base register loop */ - - } else if ((header_type & 0x7F) == 0x00) { // PCI-PCI Bridge - // Figure out IO and memory base lengths + } else if ((header_type & 0x7F) == 0x00) { + /* Figure out IO and memory base lengths */ for (cloop = 0x10; cloop <= 0x24; cloop += 4) { temp_register = 0xFFFFFFFF; pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - if (base) { // If this register is implemented + /* If this register is implemented */ + if (base) { if (base & 0x01L) { - // IO base - // base = amount of IO space requested + /* IO base + * base = amount of IO space + * requested + */ base = base & 0xFFFFFFFE; base = (~base) + 1; type = 1; } else { - // memory base - // base = amount of memory space requested + /* memory base + * base = amount of memory + * space requested + */ base = base & 0xFFFFFFF0; base = (~base) + 1; @@ -673,16 +654,16 @@ int cpqhp_save_base_addr_length(struct controller *ctrl, struct pci_func * func) type = 0; } - // Save information in slot structure + /* Save information in slot structure */ func->base_length[(cloop - 0x10) >> 2] = base; func->base_type[(cloop - 0x10) >> 2] = type; - } // End of base register loop + } /* End of base register loop */ - } else { // Some other unknown header type + } else { /* Some other unknown header type */ } - // find the next device in this slot + /* find the next device in this slot */ func = cpqhp_slot_find(func->bus, func->device, index++); } @@ -728,18 +709,18 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) pci_bus->number = func->bus; devfn = PCI_DEVFN(func->device, func->function); - // Save the command register + /* Save the command register */ pci_bus_read_config_word(pci_bus, devfn, PCI_COMMAND, &save_command); - // disable card + /* disable card */ command = 0x00; pci_bus_write_config_word(pci_bus, devfn, PCI_COMMAND, command); - // Check for Bridge + /* Check for Bridge */ pci_bus_read_config_byte(pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - // Clear Bridge Control Register + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + /* Clear Bridge Control Register */ command = 0x00; pci_bus_write_config_word(pci_bus, devfn, PCI_BRIDGE_CONTROL, command); pci_bus_read_config_byte(pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); @@ -755,7 +736,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) bus_node->next = func->bus_head; func->bus_head = bus_node; - // Save IO base and Limit registers + /* Save IO base and Limit registers */ pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_BASE, &b_base); pci_bus_read_config_byte(pci_bus, devfn, PCI_IO_LIMIT, &b_length); @@ -771,7 +752,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) func->io_head = io_node; } - // Save memory base and Limit registers + /* Save memory base and Limit registers */ pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_BASE, &w_base); pci_bus_read_config_word(pci_bus, devfn, PCI_MEMORY_LIMIT, &w_length); @@ -787,7 +768,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) func->mem_head = mem_node; } - // Save prefetchable memory base and Limit registers + /* Save prefetchable memory base and Limit registers */ pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_BASE, &w_base); pci_bus_read_config_word(pci_bus, devfn, PCI_PREF_MEMORY_LIMIT, &w_length); @@ -802,7 +783,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) p_mem_node->next = func->p_mem_head; func->p_mem_head = p_mem_node; } - // Figure out IO and memory base lengths + /* Figure out IO and memory base lengths */ for (cloop = 0x10; cloop <= 0x14; cloop += 4) { pci_bus_read_config_dword (pci_bus, devfn, cloop, &save_base); @@ -812,11 +793,14 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) temp_register = base; - if (base) { // If this register is implemented + /* If this register is implemented */ + if (base) { if (((base & 0x03L) == 0x01) && (save_command & 0x01)) { - // IO base - // set temp_register = amount of IO space requested + /* IO base + * set temp_register = amount + * of IO space requested + */ temp_register = base & 0xFFFFFFFE; temp_register = (~temp_register) + 1; @@ -834,7 +818,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) } else if (((base & 0x0BL) == 0x08) && (save_command & 0x02)) { - // prefetchable memory base + /* prefetchable memory base */ temp_register = base & 0xFFFFFFF0; temp_register = (~temp_register) + 1; @@ -851,7 +835,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) } else if (((base & 0x0BL) == 0x00) && (save_command & 0x02)) { - // prefetchable memory base + /* prefetchable memory base */ temp_register = base & 0xFFFFFFF0; temp_register = (~temp_register) + 1; @@ -868,9 +852,10 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) } else return(1); } - } // End of base register loop - } else if ((header_type & 0x7F) == 0x00) { // Standard header - // Figure out IO and memory base lengths + } /* End of base register loop */ + /* Standard header */ + } else if ((header_type & 0x7F) == 0x00) { + /* Figure out IO and memory base lengths */ for (cloop = 0x10; cloop <= 0x24; cloop += 4) { pci_bus_read_config_dword(pci_bus, devfn, cloop, &save_base); @@ -880,11 +865,14 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) temp_register = base; - if (base) { // If this register is implemented + /* If this register is implemented */ + if (base) { if (((base & 0x03L) == 0x01) && (save_command & 0x01)) { - // IO base - // set temp_register = amount of IO space requested + /* IO base + * set temp_register = amount + * of IO space requested + */ temp_register = base & 0xFFFFFFFE; temp_register = (~temp_register) + 1; @@ -901,7 +889,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) } else if (((base & 0x0BL) == 0x08) && (save_command & 0x02)) { - // prefetchable memory base + /* prefetchable memory base */ temp_register = base & 0xFFFFFFF0; temp_register = (~temp_register) + 1; @@ -918,7 +906,7 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) } else if (((base & 0x0BL) == 0x00) && (save_command & 0x02)) { - // prefetchable memory base + /* prefetchable memory base */ temp_register = base & 0xFFFFFFF0; temp_register = (~temp_register) + 1; @@ -935,15 +923,14 @@ int cpqhp_save_used_resources (struct controller *ctrl, struct pci_func * func) } else return(1); } - } // End of base register loop - } else { // Some other unknown header type + } /* End of base register loop */ } - // find the next device in this slot + /* find the next device in this slot */ func = cpqhp_slot_find(func->bus, func->device, index++); } - return(0); + return 0; } @@ -975,16 +962,16 @@ int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func) pci_bus->number = func->bus; devfn = PCI_DEVFN(func->device, func->function); - // Start at the top of config space so that the control - // registers are programmed last - for (cloop = 0x3C; cloop > 0; cloop -= 4) { + /* Start at the top of config space so that the control + * registers are programmed last + */ + for (cloop = 0x3C; cloop > 0; cloop -= 4) pci_bus_write_config_dword (pci_bus, devfn, cloop, func->config_space[cloop >> 2]); - } pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - // If this is a bridge device, restore subordinate devices - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge + /* If this is a bridge device, restore subordinate devices */ + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { pci_bus_read_config_byte (pci_bus, devfn, PCI_SECONDARY_BUS, &secondary_bus); sub_bus = (int) secondary_bus; @@ -1000,8 +987,9 @@ int cpqhp_configure_board(struct controller *ctrl, struct pci_func * func) } } else { - // Check all the base Address Registers to make sure - // they are the same. If not, the board is different. + /* Check all the base Address Registers to make sure + * they are the same. If not, the board is different. + */ for (cloop = 16; cloop < 40; cloop += 4) { pci_bus_read_config_dword (pci_bus, devfn, cloop, &temp); @@ -1058,27 +1046,28 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func) pci_bus_read_config_dword (pci_bus, devfn, PCI_VENDOR_ID, &temp_register); - // No adapter present + /* No adapter present */ if (temp_register == 0xFFFFFFFF) return(NO_ADAPTER_PRESENT); if (temp_register != func->config_space[0]) return(ADAPTER_NOT_SAME); - // Check for same revision number and class code + /* Check for same revision number and class code */ pci_bus_read_config_dword (pci_bus, devfn, PCI_CLASS_REVISION, &temp_register); - // Adapter not the same + /* Adapter not the same */ if (temp_register != func->config_space[0x08 >> 2]) return(ADAPTER_NOT_SAME); - // Check for Bridge + /* Check for Bridge */ pci_bus_read_config_byte (pci_bus, devfn, PCI_HEADER_TYPE, &header_type); - if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { // PCI-PCI Bridge - // In order to continue checking, we must program the - // bus registers in the bridge to respond to accesses - // for it's subordinate bus(es) + if ((header_type & 0x7F) == PCI_HEADER_TYPE_BRIDGE) { + /* In order to continue checking, we must program the + * bus registers in the bridge to respond to accesses + * for its subordinate bus(es) + */ temp_register = func->config_space[0x18 >> 2]; pci_bus_write_config_dword (pci_bus, devfn, PCI_PRIMARY_BUS, temp_register); @@ -1096,35 +1085,39 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func) } } - // Check to see if it is a standard config header + /* Check to see if it is a standard config header */ else if ((header_type & 0x7F) == PCI_HEADER_TYPE_NORMAL) { - // Check subsystem vendor and ID + /* Check subsystem vendor and ID */ pci_bus_read_config_dword (pci_bus, devfn, PCI_SUBSYSTEM_VENDOR_ID, &temp_register); if (temp_register != func->config_space[0x2C >> 2]) { - // If it's a SMART-2 and the register isn't filled - // in, ignore the difference because - // they just have an old rev of the firmware - + /* If it's a SMART-2 and the register isn't + * filled in, ignore the difference because + * they just have an old rev of the firmware + */ if (!((func->config_space[0] == 0xAE100E11) && (temp_register == 0x00L))) return(ADAPTER_NOT_SAME); } - // Figure out IO and memory base lengths + /* Figure out IO and memory base lengths */ for (cloop = 0x10; cloop <= 0x24; cloop += 4) { temp_register = 0xFFFFFFFF; pci_bus_write_config_dword (pci_bus, devfn, cloop, temp_register); pci_bus_read_config_dword (pci_bus, devfn, cloop, &base); - if (base) { // If this register is implemented + + /* If this register is implemented */ + if (base) { if (base & 0x01L) { - // IO base - // set base = amount of IO space requested + /* IO base + * set base = amount of IO + * space requested + */ base = base & 0xFFFFFFFE; base = (~base) + 1; type = 1; } else { - // memory base + /* memory base */ base = base & 0xFFFFFFF0; base = (~base) + 1; @@ -1135,23 +1128,24 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func) type = 0; } - // Check information in slot structure + /* Check information in slot structure */ if (func->base_length[(cloop - 0x10) >> 2] != base) return(ADAPTER_NOT_SAME); if (func->base_type[(cloop - 0x10) >> 2] != type) return(ADAPTER_NOT_SAME); - } // End of base register loop + } /* End of base register loop */ - } // End of (type 0 config space) else + } /* End of (type 0 config space) else */ else { - // this is not a type 0 or 1 config space header so - // we don't know how to do it + /* this is not a type 0 or 1 config space header so + * we don't know how to do it + */ return(DEVICE_TYPE_NOT_SUPPORTED); } - // Get the next function + /* Get the next function */ func = cpqhp_slot_find(func->bus, func->device, index++); } @@ -1168,7 +1162,7 @@ int cpqhp_valid_replace(struct controller *ctrl, struct pci_func * func) * this function is for hot plug ADD! * * returns 0 if success - */ + */ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_start) { u8 temp; @@ -1187,10 +1181,10 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st rom_resource_table = detect_HRT_floating_pointer(rom_start, rom_start+0xffff); dbg("rom_resource_table = %p\n", rom_resource_table); - if (rom_resource_table == NULL) { + if (rom_resource_table == NULL) return -ENODEV; - } - // Sum all resources and setup resource maps + + /* Sum all resources and setup resource maps */ unused_IRQ = readl(rom_resource_table + UNUSED_IRQ); dbg("unused_IRQ = %x\n", unused_IRQ); @@ -1222,13 +1216,11 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st temp = 0; - if (!cpqhp_nic_irq) { + if (!cpqhp_nic_irq) cpqhp_nic_irq = ctrl->cfgspc_irq; - } - if (!cpqhp_disk_irq) { + if (!cpqhp_disk_irq) cpqhp_disk_irq = ctrl->cfgspc_irq; - } dbg("cpqhp_disk_irq, cpqhp_nic_irq= %d, %d\n", cpqhp_disk_irq, cpqhp_nic_irq); @@ -1262,13 +1254,13 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st dev_func, io_base, io_length, mem_base, mem_length, pre_mem_base, pre_mem_length, primary_bus, secondary_bus, max_bus); - // If this entry isn't for our controller's bus, ignore it + /* If this entry isn't for our controller's bus, ignore it */ if (primary_bus != ctrl->bus) { i--; one_slot += sizeof (struct slot_rt); continue; } - // find out if this entry is for an occupied slot + /* find out if this entry is for an occupied slot */ ctrl->pci_bus->number = primary_bus; pci_bus_read_config_dword (ctrl->pci_bus, dev_func, PCI_VENDOR_ID, &temp_dword); dbg("temp_D_word = %x\n", temp_dword); @@ -1282,13 +1274,13 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st func = cpqhp_slot_find(primary_bus, dev_func >> 3, index++); } - // If we can't find a match, skip this table entry + /* If we can't find a match, skip this table entry */ if (!func) { i--; one_slot += sizeof (struct slot_rt); continue; } - // this may not work and shouldn't be used + /* this may not work and shouldn't be used */ if (secondary_bus != primary_bus) bridged_slot = 1; else @@ -1301,7 +1293,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st } - // If we've got a valid IO base, use it + /* If we've got a valid IO base, use it */ temp_dword = io_base + io_length; @@ -1325,7 +1317,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st } } - // If we've got a valid memory base, use it + /* If we've got a valid memory base, use it */ temp_dword = mem_base + mem_length; if ((mem_base) && (temp_dword < 0x10000)) { mem_node = kmalloc(sizeof(*mem_node), GFP_KERNEL); @@ -1348,8 +1340,9 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st } } - // If we've got a valid prefetchable memory base, and - // the base + length isn't greater than 0xFFFF + /* If we've got a valid prefetchable memory base, and + * the base + length isn't greater than 0xFFFF + */ temp_dword = pre_mem_base + pre_mem_length; if ((pre_mem_base) && (temp_dword < 0x10000)) { p_mem_node = kmalloc(sizeof(*p_mem_node), GFP_KERNEL); @@ -1372,9 +1365,10 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st } } - // If we've got a valid bus number, use it - // The second condition is to ignore bus numbers on - // populated slots that don't have PCI-PCI bridges + /* If we've got a valid bus number, use it + * The second condition is to ignore bus numbers on + * populated slots that don't have PCI-PCI bridges + */ if (secondary_bus && (secondary_bus != primary_bus)) { bus_node = kmalloc(sizeof(*bus_node), GFP_KERNEL); if (!bus_node) @@ -1398,8 +1392,9 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st one_slot += sizeof (struct slot_rt); } - // If all of the following fail, we don't have any resources for - // hot plug add + /* If all of the following fail, we don't have any resources for + * hot plug add + */ rc = 1; rc &= cpqhp_resource_sort_and_combine(&(ctrl->mem_head)); rc &= cpqhp_resource_sort_and_combine(&(ctrl->p_mem_head)); diff --git a/drivers/pci/hotplug/ibmphp_core.c b/drivers/pci/hotplug/ibmphp_core.c index 42e4260c3b1..7485ffda950 100644 --- a/drivers/pci/hotplug/ibmphp_core.c +++ b/drivers/pci/hotplug/ibmphp_core.c @@ -1318,7 +1318,6 @@ error: } struct hotplug_slot_ops ibmphp_hotplug_slot_ops = { - .owner = THIS_MODULE, .set_attention_status = set_attention_status, .enable_slot = enable_slot, .disable_slot = ibmphp_disable_slot, @@ -1421,3 +1420,4 @@ static void __exit ibmphp_exit(void) } module_init(ibmphp_init); +module_exit(ibmphp_exit); diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 535fce0f07f..844580489d4 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -347,125 +347,129 @@ static struct pci_slot_attribute hotplug_slot_attr_test = { .store = test_write_file }; -static int has_power_file(struct pci_slot *pci_slot) +static bool has_power_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) - return -ENODEV; + return false; if ((slot->ops->enable_slot) || (slot->ops->disable_slot) || (slot->ops->get_power_status)) - return 0; - return -ENOENT; + return true; + return false; } -static int has_attention_file(struct pci_slot *pci_slot) +static bool has_attention_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) - return -ENODEV; + return false; if ((slot->ops->set_attention_status) || (slot->ops->get_attention_status)) - return 0; - return -ENOENT; + return true; + return false; } -static int has_latch_file(struct pci_slot *pci_slot) +static bool has_latch_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) - return -ENODEV; + return false; if (slot->ops->get_latch_status) - return 0; - return -ENOENT; + return true; + return false; } -static int has_adapter_file(struct pci_slot *pci_slot) +static bool has_adapter_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) - return -ENODEV; + return false; if (slot->ops->get_adapter_status) - return 0; - return -ENOENT; + return true; + return false; } -static int has_max_bus_speed_file(struct pci_slot *pci_slot) +static bool has_max_bus_speed_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) - return -ENODEV; + return false; if (slot->ops->get_max_bus_speed) - return 0; - return -ENOENT; + return true; + return false; } -static int has_cur_bus_speed_file(struct pci_slot *pci_slot) +static bool has_cur_bus_speed_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) - return -ENODEV; + return false; if (slot->ops->get_cur_bus_speed) - return 0; - return -ENOENT; + return true; + return false; } -static int has_test_file(struct pci_slot *pci_slot) +static bool has_test_file(struct pci_slot *pci_slot) { struct hotplug_slot *slot = pci_slot->hotplug; if ((!slot) || (!slot->ops)) - return -ENODEV; + return false; if (slot->ops->hardware_test) - return 0; - return -ENOENT; + return true; + return false; } static int fs_add_slot(struct pci_slot *slot) { int retval = 0; - if (has_power_file(slot) == 0) { - retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr); + /* Create symbolic link to the hotplug driver module */ + pci_hp_create_module_link(slot); + + if (has_power_file(slot)) { + retval = sysfs_create_file(&slot->kobj, + &hotplug_slot_attr_power.attr); if (retval) goto exit_power; } - if (has_attention_file(slot) == 0) { + if (has_attention_file(slot)) { retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr); if (retval) goto exit_attention; } - if (has_latch_file(slot) == 0) { + if (has_latch_file(slot)) { retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr); if (retval) goto exit_latch; } - if (has_adapter_file(slot) == 0) { + if (has_adapter_file(slot)) { retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr); if (retval) goto exit_adapter; } - if (has_max_bus_speed_file(slot) == 0) { + if (has_max_bus_speed_file(slot)) { retval = sysfs_create_file(&slot->kobj, - &hotplug_slot_attr_max_bus_speed.attr); + &hotplug_slot_attr_max_bus_speed.attr); if (retval) goto exit_max_speed; } - if (has_cur_bus_speed_file(slot) == 0) { + if (has_cur_bus_speed_file(slot)) { retval = sysfs_create_file(&slot->kobj, - &hotplug_slot_attr_cur_bus_speed.attr); + &hotplug_slot_attr_cur_bus_speed.attr); if (retval) goto exit_cur_speed; } - if (has_test_file(slot) == 0) { + if (has_test_file(slot)) { retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr); if (retval) @@ -475,55 +479,61 @@ static int fs_add_slot(struct pci_slot *slot) goto exit; exit_test: - if (has_cur_bus_speed_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); - + if (has_cur_bus_speed_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_cur_bus_speed.attr); exit_cur_speed: - if (has_max_bus_speed_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); - + if (has_max_bus_speed_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_max_bus_speed.attr); exit_max_speed: - if (has_adapter_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); - + if (has_adapter_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_presence.attr); exit_adapter: - if (has_latch_file(slot) == 0) + if (has_latch_file(slot)) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); - exit_latch: - if (has_attention_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); - + if (has_attention_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_attention.attr); exit_attention: - if (has_power_file(slot) == 0) + if (has_power_file(slot)) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); exit_power: + pci_hp_remove_module_link(slot); exit: return retval; } static void fs_remove_slot(struct pci_slot *slot) { - if (has_power_file(slot) == 0) + if (has_power_file(slot)) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); - if (has_attention_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); + if (has_attention_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_attention.attr); - if (has_latch_file(slot) == 0) + if (has_latch_file(slot)) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); - if (has_adapter_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); + if (has_adapter_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_presence.attr); - if (has_max_bus_speed_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); + if (has_max_bus_speed_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_max_bus_speed.attr); - if (has_cur_bus_speed_file(slot) == 0) - sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); + if (has_cur_bus_speed_file(slot)) + sysfs_remove_file(&slot->kobj, + &hotplug_slot_attr_cur_bus_speed.attr); - if (has_test_file(slot) == 0) + if (has_test_file(slot)) sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); + + pci_hp_remove_module_link(slot); } static struct hotplug_slot *get_slot_from_name (const char *name) @@ -540,10 +550,10 @@ static struct hotplug_slot *get_slot_from_name (const char *name) } /** - * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem + * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem * @bus: bus this slot is on * @slot: pointer to the &struct hotplug_slot to register - * @slot_nr: slot number + * @devnr: device number * @name: name registered with kobject core * * Registers a hotplug slot with the pci hotplug subsystem, which will allow @@ -551,8 +561,9 @@ static struct hotplug_slot *get_slot_from_name (const char *name) * * Returns 0 if successful, anything else for an error. */ -int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr, - const char *name) +int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, + int devnr, const char *name, + struct module *owner, const char *mod_name) { int result; struct pci_slot *pci_slot; @@ -567,14 +578,16 @@ int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr, return -EINVAL; } - mutex_lock(&pci_hp_mutex); + slot->ops->owner = owner; + slot->ops->mod_name = mod_name; + mutex_lock(&pci_hp_mutex); /* * No problems if we call this interface from both ACPI_PCI_SLOT * driver and call it here again. If we've already created the * pci_slot, the interface will simply bump the refcount. */ - pci_slot = pci_create_slot(bus, slot_nr, name, slot); + pci_slot = pci_create_slot(bus, devnr, name, slot); if (IS_ERR(pci_slot)) { result = PTR_ERR(pci_slot); goto out; @@ -684,6 +697,6 @@ MODULE_LICENSE("GPL"); module_param(debug, bool, 0644); MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); -EXPORT_SYMBOL_GPL(pci_hp_register); +EXPORT_SYMBOL_GPL(__pci_hp_register); EXPORT_SYMBOL_GPL(pci_hp_deregister); EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 0a368547e63..e6cf096498b 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -81,7 +81,6 @@ struct slot { struct hpc_ops *hpc_ops; struct hotplug_slot *hotplug_slot; struct list_head slot_list; - unsigned long last_emi_toggle; struct delayed_work work; /* work for button event */ struct mutex lock; }; @@ -203,8 +202,6 @@ struct hpc_ops { 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_emi_status)(struct slot *slot, u8 *status); - int (*toggle_emi)(struct slot *slot); 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); diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index fb254b2454d..2317557fdee 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -73,7 +73,6 @@ static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *val static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); static struct hotplug_slot_ops pciehp_hotplug_slot_ops = { - .owner = THIS_MODULE, .set_attention_status = set_attention_status, .enable_slot = enable_slot, .disable_slot = disable_slot, @@ -85,99 +84,6 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = { .get_cur_bus_speed = get_cur_bus_speed, }; -/* - * Check the status of the Electro Mechanical Interlock (EMI) - */ -static int get_lock_status(struct hotplug_slot *hotplug_slot, u8 *value) -{ - struct slot *slot = hotplug_slot->private; - return (slot->hpc_ops->get_emi_status(slot, value)); -} - -/* - * sysfs interface for the Electro Mechanical Interlock (EMI) - * 1 == locked, 0 == unlocked - */ -static ssize_t lock_read_file(struct hotplug_slot *slot, char *buf) -{ - int retval; - u8 value; - - retval = get_lock_status(slot, &value); - if (retval) - goto lock_read_exit; - retval = sprintf (buf, "%d\n", value); - -lock_read_exit: - return retval; -} - -/* - * Change the status of the Electro Mechanical Interlock (EMI) - * This is a toggle - in addition there must be at least 1 second - * in between toggles. - */ -static int set_lock_status(struct hotplug_slot *hotplug_slot, u8 status) -{ - struct slot *slot = hotplug_slot->private; - int retval; - u8 value; - - mutex_lock(&slot->ctrl->crit_sect); - - /* has it been >1 sec since our last toggle? */ - if ((get_seconds() - slot->last_emi_toggle) < 1) { - mutex_unlock(&slot->ctrl->crit_sect); - return -EINVAL; - } - - /* see what our current state is */ - retval = get_lock_status(hotplug_slot, &value); - if (retval || (value == status)) - goto set_lock_exit; - - slot->hpc_ops->toggle_emi(slot); -set_lock_exit: - mutex_unlock(&slot->ctrl->crit_sect); - return 0; -} - -/* - * sysfs interface which allows the user to toggle the Electro Mechanical - * Interlock. Valid values are either 0 or 1. 0 == unlock, 1 == lock - */ -static ssize_t lock_write_file(struct hotplug_slot *hotplug_slot, - const char *buf, size_t count) -{ - struct slot *slot = hotplug_slot->private; - unsigned long llock; - u8 lock; - int retval = 0; - - llock = simple_strtoul(buf, NULL, 10); - lock = (u8)(llock & 0xff); - - switch (lock) { - case 0: - case 1: - retval = set_lock_status(hotplug_slot, lock); - break; - default: - ctrl_err(slot->ctrl, "%d is an invalid lock value\n", - lock); - retval = -EINVAL; - } - if (retval) - return retval; - return count; -} - -static struct hotplug_slot_attribute hotplug_slot_attr_lock = { - .attr = {.name = "lock", .mode = S_IFREG | S_IRUGO | S_IWUSR}, - .show = lock_read_file, - .store = lock_write_file -}; - /** * release_slot - free up the memory used by a slot * @hotplug_slot: slot to free @@ -236,17 +142,6 @@ static int init_slots(struct controller *ctrl) get_attention_status(hotplug_slot, &info->attention_status); get_latch_status(hotplug_slot, &info->latch_status); get_adapter_status(hotplug_slot, &info->adapter_status); - /* create additional sysfs entries */ - if (EMI(ctrl)) { - retval = sysfs_create_file(&hotplug_slot->pci_slot->kobj, - &hotplug_slot_attr_lock.attr); - if (retval) { - pci_hp_deregister(hotplug_slot); - ctrl_err(ctrl, "Cannot create additional sysfs " - "entries\n"); - goto error_info; - } - } } return 0; @@ -261,13 +156,8 @@ error: static void cleanup_slots(struct controller *ctrl) { struct slot *slot; - - list_for_each_entry(slot, &ctrl->slot_list, slot_list) { - if (EMI(ctrl)) - sysfs_remove_file(&slot->hotplug_slot->pci_slot->kobj, - &hotplug_slot_attr_lock.attr); + list_for_each_entry(slot, &ctrl->slot_list, slot_list) pci_hp_deregister(slot->hotplug_slot); - } } /* diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 07bd3215114..52813257e5b 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -422,35 +422,6 @@ static int hpc_query_power_fault(struct slot *slot) return !!(slot_status & PCI_EXP_SLTSTA_PFD); } -static int hpc_get_emi_status(struct slot *slot, u8 *status) -{ - struct controller *ctrl = slot->ctrl; - u16 slot_status; - int retval; - - retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); - if (retval) { - ctrl_err(ctrl, "Cannot check EMI status\n"); - return retval; - } - *status = !!(slot_status & PCI_EXP_SLTSTA_EIS); - return retval; -} - -static int hpc_toggle_emi(struct slot *slot) -{ - u16 slot_cmd; - u16 cmd_mask; - int rc; - - slot_cmd = PCI_EXP_SLTCTL_EIC; - cmd_mask = PCI_EXP_SLTCTL_EIC; - rc = pcie_write_cmd(slot->ctrl, slot_cmd, cmd_mask); - slot->last_emi_toggle = get_seconds(); - - return rc; -} - static int hpc_set_attention_status(struct slot *slot, u8 value) { struct controller *ctrl = slot->ctrl; @@ -874,8 +845,6 @@ static struct hpc_ops pciehp_hpc_ops = { .get_attention_status = hpc_get_attention_status, .get_latch_status = hpc_get_latch_status, .get_adapter_status = hpc_get_adapter_status, - .get_emi_status = hpc_get_emi_status, - .toggle_emi = hpc_toggle_emi, .get_max_bus_speed = hpc_get_max_lnk_speed, .get_cur_bus_speed = hpc_get_cur_lnk_speed, diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c index e3dd6cf9e89..5175d9b26f0 100644 --- a/drivers/pci/hotplug/pcihp_skeleton.c +++ b/drivers/pci/hotplug/pcihp_skeleton.c @@ -82,7 +82,6 @@ static int get_latch_status (struct hotplug_slot *slot, u8 *value); static int get_adapter_status (struct hotplug_slot *slot, u8 *value); static struct hotplug_slot_ops skel_hotplug_slot_ops = { - .owner = THIS_MODULE, .enable_slot = enable_slot, .disable_slot = disable_slot, .set_attention_status = set_attention_status, diff --git a/drivers/pci/hotplug/rpaphp_core.c b/drivers/pci/hotplug/rpaphp_core.c index 95d02a08fdc..c159223389e 100644 --- a/drivers/pci/hotplug/rpaphp_core.c +++ b/drivers/pci/hotplug/rpaphp_core.c @@ -423,7 +423,6 @@ static int disable_slot(struct hotplug_slot *hotplug_slot) } struct hotplug_slot_ops rpaphp_hotplug_slot_ops = { - .owner = THIS_MODULE, .enable_slot = enable_slot, .disable_slot = disable_slot, .set_attention_status = set_attention_status, diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index 2d6da78fddb..a4494d78e7c 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -83,7 +83,6 @@ static int disable_slot(struct hotplug_slot *slot); static inline int get_power_status(struct hotplug_slot *slot, u8 *value); static struct hotplug_slot_ops sn_hotplug_slot_ops = { - .owner = THIS_MODULE, .enable_slot = enable_slot, .disable_slot = disable_slot, .get_power_status = get_power_status, diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c index fe8d149c229..8a520a3d0f5 100644 --- a/drivers/pci/hotplug/shpchp_core.c +++ b/drivers/pci/hotplug/shpchp_core.c @@ -69,7 +69,6 @@ static int get_max_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *val static int get_cur_bus_speed (struct hotplug_slot *slot, enum pci_bus_speed *value); static struct hotplug_slot_ops shpchp_hotplug_slot_ops = { - .owner = THIS_MODULE, .set_attention_status = set_attention_status, .enable_slot = enable_slot, .disable_slot = disable_slot, diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index cd389162735..e53eacd75c8 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -39,6 +39,7 @@ #include <linux/sysdev.h> #include <asm/cacheflush.h> #include <asm/iommu.h> +#include <asm/e820.h> #include "pci.h" #define ROOT_SIZE VTD_PAGE_SIZE @@ -53,6 +54,8 @@ #define DEFAULT_DOMAIN_ADDRESS_WIDTH 48 +#define MAX_AGAW_WIDTH 64 + #define DOMAIN_MAX_ADDR(gaw) ((((u64)1) << gaw) - 1) #define IOVA_PFN(addr) ((addr) >> PAGE_SHIFT) @@ -131,8 +134,6 @@ static inline void context_set_fault_enable(struct context_entry *context) context->lo &= (((u64)-1) << 2) | 1; } -#define CONTEXT_TT_MULTI_LEVEL 0 - static inline void context_set_translation_type(struct context_entry *context, unsigned long value) { @@ -217,6 +218,14 @@ static inline bool dma_pte_present(struct dma_pte *pte) return (pte->val & 3) != 0; } +/* + * This domain is a statically identity mapping domain. + * 1. This domain creats a static 1:1 mapping to all usable memory. + * 2. It maps to each iommu if successful. + * 3. Each iommu mapps to this domain if successful. + */ +struct dmar_domain *si_domain; + /* devices under the same p2p bridge are owned in one domain */ #define DOMAIN_FLAG_P2P_MULTIPLE_DEVICES (1 << 0) @@ -225,6 +234,9 @@ static inline bool dma_pte_present(struct dma_pte *pte) */ #define DOMAIN_FLAG_VIRTUAL_MACHINE (1 << 1) +/* si_domain contains mulitple devices */ +#define DOMAIN_FLAG_STATIC_IDENTITY (1 << 2) + struct dmar_domain { int id; /* domain id */ unsigned long iommu_bmp; /* bitmap of iommus this domain uses*/ @@ -256,6 +268,7 @@ struct device_domain_info { u8 bus; /* PCI bus number */ u8 devfn; /* PCI devfn number */ struct pci_dev *dev; /* it's NULL for PCIE-to-PCI bridge */ + struct intel_iommu *iommu; /* IOMMU used by this device */ struct dmar_domain *domain; /* pointer to domain */ }; @@ -401,17 +414,13 @@ void free_iova_mem(struct iova *iova) static inline int width_to_agaw(int width); -/* calculate agaw for each iommu. - * "SAGAW" may be different across iommus, use a default agaw, and - * get a supported less agaw for iommus that don't support the default agaw. - */ -int iommu_calculate_agaw(struct intel_iommu *iommu) +static int __iommu_calculate_agaw(struct intel_iommu *iommu, int max_gaw) { unsigned long sagaw; int agaw = -1; sagaw = cap_sagaw(iommu->cap); - for (agaw = width_to_agaw(DEFAULT_DOMAIN_ADDRESS_WIDTH); + for (agaw = width_to_agaw(max_gaw); agaw >= 0; agaw--) { if (test_bit(agaw, &sagaw)) break; @@ -420,12 +429,32 @@ int iommu_calculate_agaw(struct intel_iommu *iommu) return agaw; } -/* in native case, each domain is related to only one iommu */ +/* + * Calculate max SAGAW for each iommu. + */ +int iommu_calculate_max_sagaw(struct intel_iommu *iommu) +{ + return __iommu_calculate_agaw(iommu, MAX_AGAW_WIDTH); +} + +/* + * calculate agaw for each iommu. + * "SAGAW" may be different across iommus, use a default agaw, and + * get a supported less agaw for iommus that don't support the default agaw. + */ +int iommu_calculate_agaw(struct intel_iommu *iommu) +{ + return __iommu_calculate_agaw(iommu, DEFAULT_DOMAIN_ADDRESS_WIDTH); +} + +/* This functionin only returns single iommu in a domain */ static struct intel_iommu *domain_get_iommu(struct dmar_domain *domain) { int iommu_id; + /* si_domain and vm domain should not get here. */ BUG_ON(domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE); + BUG_ON(domain->flags & DOMAIN_FLAG_STATIC_IDENTITY); iommu_id = find_first_bit(&domain->iommu_bmp, g_num_of_iommus); if (iommu_id < 0 || iommu_id >= g_num_of_iommus) @@ -809,7 +838,7 @@ static int iommu_alloc_root_entry(struct intel_iommu *iommu) static void iommu_set_root_entry(struct intel_iommu *iommu) { void *addr; - u32 cmd, sts; + u32 sts; unsigned long flag; addr = iommu->root_entry; @@ -817,12 +846,11 @@ static void iommu_set_root_entry(struct intel_iommu *iommu) spin_lock_irqsave(&iommu->register_lock, flag); dmar_writeq(iommu->reg + DMAR_RTADDR_REG, virt_to_phys(addr)); - cmd = iommu->gcmd | DMA_GCMD_SRTP; - writel(cmd, iommu->reg + DMAR_GCMD_REG); + writel(iommu->gcmd | DMA_GCMD_SRTP, iommu->reg + DMAR_GCMD_REG); /* Make sure hardware complete it */ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, - readl, (sts & DMA_GSTS_RTPS), sts); + readl, (sts & DMA_GSTS_RTPS), sts); spin_unlock_irqrestore(&iommu->register_lock, flag); } @@ -834,39 +862,25 @@ static void iommu_flush_write_buffer(struct intel_iommu *iommu) if (!rwbf_quirk && !cap_rwbf(iommu->cap)) return; - val = iommu->gcmd | DMA_GCMD_WBF; spin_lock_irqsave(&iommu->register_lock, flag); - writel(val, iommu->reg + DMAR_GCMD_REG); + writel(iommu->gcmd | DMA_GCMD_WBF, iommu->reg + DMAR_GCMD_REG); /* Make sure hardware complete it */ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, - readl, (!(val & DMA_GSTS_WBFS)), val); + readl, (!(val & DMA_GSTS_WBFS)), val); spin_unlock_irqrestore(&iommu->register_lock, flag); } /* return value determine if we need a write buffer flush */ -static int __iommu_flush_context(struct intel_iommu *iommu, - u16 did, u16 source_id, u8 function_mask, u64 type, - int non_present_entry_flush) +static void __iommu_flush_context(struct intel_iommu *iommu, + u16 did, u16 source_id, u8 function_mask, + u64 type) { u64 val = 0; unsigned long flag; - /* - * In the non-present entry flush case, if hardware doesn't cache - * non-present entry we do nothing and if hardware cache non-present - * entry, we flush entries of domain 0 (the domain id is used to cache - * any non-present entries) - */ - if (non_present_entry_flush) { - if (!cap_caching_mode(iommu->cap)) - return 1; - else - did = 0; - } - switch (type) { case DMA_CCMD_GLOBAL_INVL: val = DMA_CCMD_GLOBAL_INVL; @@ -891,33 +905,16 @@ static int __iommu_flush_context(struct intel_iommu *iommu, dmar_readq, (!(val & DMA_CCMD_ICC)), val); spin_unlock_irqrestore(&iommu->register_lock, flag); - - /* flush context entry will implicitly flush write buffer */ - return 0; } /* return value determine if we need a write buffer flush */ -static int __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did, - u64 addr, unsigned int size_order, u64 type, - int non_present_entry_flush) +static void __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did, + u64 addr, unsigned int size_order, u64 type) { int tlb_offset = ecap_iotlb_offset(iommu->ecap); u64 val = 0, val_iva = 0; unsigned long flag; - /* - * In the non-present entry flush case, if hardware doesn't cache - * non-present entry we do nothing and if hardware cache non-present - * entry, we flush entries of domain 0 (the domain id is used to cache - * any non-present entries) - */ - if (non_present_entry_flush) { - if (!cap_caching_mode(iommu->cap)) - return 1; - else - did = 0; - } - switch (type) { case DMA_TLB_GLOBAL_FLUSH: /* global flush doesn't need set IVA_REG */ @@ -965,37 +962,101 @@ static int __iommu_flush_iotlb(struct intel_iommu *iommu, u16 did, pr_debug("IOMMU: tlb flush request %Lx, actual %Lx\n", (unsigned long long)DMA_TLB_IIRG(type), (unsigned long long)DMA_TLB_IAIG(val)); - /* flush iotlb entry will implicitly flush write buffer */ - return 0; } -static int iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did, - u64 addr, unsigned int pages, int non_present_entry_flush) +static struct device_domain_info *iommu_support_dev_iotlb( + struct dmar_domain *domain, int segment, u8 bus, u8 devfn) +{ + int found = 0; + unsigned long flags; + struct device_domain_info *info; + struct intel_iommu *iommu = device_to_iommu(segment, bus, devfn); + + if (!ecap_dev_iotlb_support(iommu->ecap)) + return NULL; + + if (!iommu->qi) + return NULL; + + spin_lock_irqsave(&device_domain_lock, flags); + list_for_each_entry(info, &domain->devices, link) + if (info->bus == bus && info->devfn == devfn) { + found = 1; + break; + } + spin_unlock_irqrestore(&device_domain_lock, flags); + + if (!found || !info->dev) + return NULL; + + if (!pci_find_ext_capability(info->dev, PCI_EXT_CAP_ID_ATS)) + return NULL; + + if (!dmar_find_matched_atsr_unit(info->dev)) + return NULL; + + info->iommu = iommu; + + return info; +} + +static void iommu_enable_dev_iotlb(struct device_domain_info *info) +{ + if (!info) + return; + + pci_enable_ats(info->dev, VTD_PAGE_SHIFT); +} + +static void iommu_disable_dev_iotlb(struct device_domain_info *info) +{ + if (!info->dev || !pci_ats_enabled(info->dev)) + return; + + pci_disable_ats(info->dev); +} + +static void iommu_flush_dev_iotlb(struct dmar_domain *domain, + u64 addr, unsigned mask) { - unsigned int mask; + u16 sid, qdep; + unsigned long flags; + struct device_domain_info *info; + + spin_lock_irqsave(&device_domain_lock, flags); + list_for_each_entry(info, &domain->devices, link) { + if (!info->dev || !pci_ats_enabled(info->dev)) + continue; + + sid = info->bus << 8 | info->devfn; + qdep = pci_ats_queue_depth(info->dev); + qi_flush_dev_iotlb(info->iommu, sid, qdep, addr, mask); + } + spin_unlock_irqrestore(&device_domain_lock, flags); +} + +static void iommu_flush_iotlb_psi(struct intel_iommu *iommu, u16 did, + u64 addr, unsigned int pages) +{ + unsigned int mask = ilog2(__roundup_pow_of_two(pages)); BUG_ON(addr & (~VTD_PAGE_MASK)); BUG_ON(pages == 0); - /* Fallback to domain selective flush if no PSI support */ - if (!cap_pgsel_inv(iommu->cap)) - return iommu->flush.flush_iotlb(iommu, did, 0, 0, - DMA_TLB_DSI_FLUSH, - non_present_entry_flush); - /* + * Fallback to domain selective flush if no PSI support or the size is + * too big. * PSI requires page size to be 2 ^ x, and the base address is naturally * aligned to the size */ - mask = ilog2(__roundup_pow_of_two(pages)); - /* Fallback to domain selective flush if size is too big */ - if (mask > cap_max_amask_val(iommu->cap)) - return iommu->flush.flush_iotlb(iommu, did, 0, 0, - DMA_TLB_DSI_FLUSH, non_present_entry_flush); - - return iommu->flush.flush_iotlb(iommu, did, addr, mask, - DMA_TLB_PSI_FLUSH, - non_present_entry_flush); + if (!cap_pgsel_inv(iommu->cap) || mask > cap_max_amask_val(iommu->cap)) + iommu->flush.flush_iotlb(iommu, did, 0, 0, + DMA_TLB_DSI_FLUSH); + else + iommu->flush.flush_iotlb(iommu, did, addr, mask, + DMA_TLB_PSI_FLUSH); + if (did) + iommu_flush_dev_iotlb(iommu->domains[did], addr, mask); } static void iommu_disable_protect_mem_regions(struct intel_iommu *iommu) @@ -1021,13 +1082,13 @@ static int iommu_enable_translation(struct intel_iommu *iommu) unsigned long flags; spin_lock_irqsave(&iommu->register_lock, flags); - writel(iommu->gcmd|DMA_GCMD_TE, iommu->reg + DMAR_GCMD_REG); + iommu->gcmd |= DMA_GCMD_TE; + writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG); /* Make sure hardware complete it */ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, - readl, (sts & DMA_GSTS_TES), sts); + readl, (sts & DMA_GSTS_TES), sts); - iommu->gcmd |= DMA_GCMD_TE; spin_unlock_irqrestore(&iommu->register_lock, flags); return 0; } @@ -1043,7 +1104,7 @@ static int iommu_disable_translation(struct intel_iommu *iommu) /* Make sure hardware complete it */ IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, - readl, (!(sts & DMA_GSTS_TES)), sts); + readl, (!(sts & DMA_GSTS_TES)), sts); spin_unlock_irqrestore(&iommu->register_lock, flag); return 0; @@ -1142,48 +1203,71 @@ void free_dmar_iommu(struct intel_iommu *iommu) free_context_table(iommu); } -static struct dmar_domain * iommu_alloc_domain(struct intel_iommu *iommu) +static struct dmar_domain *alloc_domain(void) { - unsigned long num; - unsigned long ndomains; struct dmar_domain *domain; - unsigned long flags; domain = alloc_domain_mem(); if (!domain) return NULL; + memset(&domain->iommu_bmp, 0, sizeof(unsigned long)); + domain->flags = 0; + + return domain; +} + +static int iommu_attach_domain(struct dmar_domain *domain, + struct intel_iommu *iommu) +{ + int num; + unsigned long ndomains; + unsigned long flags; + ndomains = cap_ndoms(iommu->cap); spin_lock_irqsave(&iommu->lock, flags); + num = find_first_zero_bit(iommu->domain_ids, ndomains); if (num >= ndomains) { spin_unlock_irqrestore(&iommu->lock, flags); - free_domain_mem(domain); printk(KERN_ERR "IOMMU: no free domain ids\n"); - return NULL; + return -ENOMEM; } - set_bit(num, iommu->domain_ids); domain->id = num; - memset(&domain->iommu_bmp, 0, sizeof(unsigned long)); + set_bit(num, iommu->domain_ids); set_bit(iommu->seq_id, &domain->iommu_bmp); - domain->flags = 0; iommu->domains[num] = domain; spin_unlock_irqrestore(&iommu->lock, flags); - return domain; + return 0; } -static void iommu_free_domain(struct dmar_domain *domain) +static void iommu_detach_domain(struct dmar_domain *domain, + struct intel_iommu *iommu) { unsigned long flags; - struct intel_iommu *iommu; - - iommu = domain_get_iommu(domain); + int num, ndomains; + int found = 0; spin_lock_irqsave(&iommu->lock, flags); - clear_bit(domain->id, iommu->domain_ids); + ndomains = cap_ndoms(iommu->cap); + num = find_first_bit(iommu->domain_ids, ndomains); + for (; num < ndomains; ) { + if (iommu->domains[num] == domain) { + found = 1; + break; + } + num = find_next_bit(iommu->domain_ids, + cap_ndoms(iommu->cap), num+1); + } + + if (found) { + clear_bit(num, iommu->domain_ids); + clear_bit(iommu->seq_id, &domain->iommu_bmp); + iommu->domains[num] = NULL; + } spin_unlock_irqrestore(&iommu->lock, flags); } @@ -1303,6 +1387,8 @@ static int domain_init(struct dmar_domain *domain, int guest_width) static void domain_exit(struct dmar_domain *domain) { + struct dmar_drhd_unit *drhd; + struct intel_iommu *iommu; u64 end; /* Domain 0 is reserved, so dont process it */ @@ -1321,12 +1407,15 @@ static void domain_exit(struct dmar_domain *domain) /* free page tables */ dma_pte_free_pagetable(domain, 0, end); - iommu_free_domain(domain); + for_each_active_iommu(iommu, drhd) + if (test_bit(iommu->seq_id, &domain->iommu_bmp)) + iommu_detach_domain(domain, iommu); + free_domain_mem(domain); } -static int domain_context_mapping_one(struct dmar_domain *domain, - int segment, u8 bus, u8 devfn) +static int domain_context_mapping_one(struct dmar_domain *domain, int segment, + u8 bus, u8 devfn, int translation) { struct context_entry *context; unsigned long flags; @@ -1336,10 +1425,14 @@ static int domain_context_mapping_one(struct dmar_domain *domain, unsigned long ndomains; int id; int agaw; + struct device_domain_info *info = NULL; pr_debug("Set context mapping for %02x:%02x.%d\n", bus, PCI_SLOT(devfn), PCI_FUNC(devfn)); + BUG_ON(!domain->pgd); + BUG_ON(translation != CONTEXT_TT_PASS_THROUGH && + translation != CONTEXT_TT_MULTI_LEVEL); iommu = device_to_iommu(segment, bus, devfn); if (!iommu) @@ -1357,7 +1450,8 @@ static int domain_context_mapping_one(struct dmar_domain *domain, id = domain->id; pgd = domain->pgd; - if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) { + if (domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE || + domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) { int found = 0; /* find an available domain id for this device in iommu */ @@ -1382,6 +1476,7 @@ static int domain_context_mapping_one(struct dmar_domain *domain, } set_bit(num, iommu->domain_ids); + set_bit(iommu->seq_id, &domain->iommu_bmp); iommu->domains[num] = domain; id = num; } @@ -1399,21 +1494,44 @@ static int domain_context_mapping_one(struct dmar_domain *domain, } context_set_domain_id(context, id); - context_set_address_width(context, iommu->agaw); - context_set_address_root(context, virt_to_phys(pgd)); - context_set_translation_type(context, CONTEXT_TT_MULTI_LEVEL); + + if (translation != CONTEXT_TT_PASS_THROUGH) { + info = iommu_support_dev_iotlb(domain, segment, bus, devfn); + translation = info ? CONTEXT_TT_DEV_IOTLB : + CONTEXT_TT_MULTI_LEVEL; + } + /* + * In pass through mode, AW must be programmed to indicate the largest + * AGAW value supported by hardware. And ASR is ignored by hardware. + */ + if (unlikely(translation == CONTEXT_TT_PASS_THROUGH)) + context_set_address_width(context, iommu->msagaw); + else { + context_set_address_root(context, virt_to_phys(pgd)); + context_set_address_width(context, iommu->agaw); + } + + context_set_translation_type(context, translation); context_set_fault_enable(context); context_set_present(context); domain_flush_cache(domain, context, sizeof(*context)); - /* it's a non-present to present mapping */ - if (iommu->flush.flush_context(iommu, domain->id, - (((u16)bus) << 8) | devfn, DMA_CCMD_MASK_NOBIT, - DMA_CCMD_DEVICE_INVL, 1)) + /* + * It's a non-present to present mapping. If hardware doesn't cache + * non-present entry we only need to flush the write-buffer. If the + * _does_ cache non-present entries, then it does so in the special + * domain #0, which we have to flush: + */ + if (cap_caching_mode(iommu->cap)) { + iommu->flush.flush_context(iommu, 0, + (((u16)bus) << 8) | devfn, + DMA_CCMD_MASK_NOBIT, + DMA_CCMD_DEVICE_INVL); + iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_DSI_FLUSH); + } else { iommu_flush_write_buffer(iommu); - else - iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_DSI_FLUSH, 0); - + } + iommu_enable_dev_iotlb(info); spin_unlock_irqrestore(&iommu->lock, flags); spin_lock_irqsave(&domain->iommu_lock, flags); @@ -1426,13 +1544,15 @@ static int domain_context_mapping_one(struct dmar_domain *domain, } static int -domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev) +domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev, + int translation) { int ret; struct pci_dev *tmp, *parent; ret = domain_context_mapping_one(domain, pci_domain_nr(pdev->bus), - pdev->bus->number, pdev->devfn); + pdev->bus->number, pdev->devfn, + translation); if (ret) return ret; @@ -1446,7 +1566,7 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev) ret = domain_context_mapping_one(domain, pci_domain_nr(parent->bus), parent->bus->number, - parent->devfn); + parent->devfn, translation); if (ret) return ret; parent = parent->bus->self; @@ -1454,12 +1574,14 @@ domain_context_mapping(struct dmar_domain *domain, struct pci_dev *pdev) if (tmp->is_pcie) /* this is a PCIE-to-PCI bridge */ return domain_context_mapping_one(domain, pci_domain_nr(tmp->subordinate), - tmp->subordinate->number, 0); + tmp->subordinate->number, 0, + translation); else /* this is a legacy PCI bridge */ return domain_context_mapping_one(domain, pci_domain_nr(tmp->bus), tmp->bus->number, - tmp->devfn); + tmp->devfn, + translation); } static int domain_context_mapped(struct pci_dev *pdev) @@ -1540,9 +1662,8 @@ static void iommu_detach_dev(struct intel_iommu *iommu, u8 bus, u8 devfn) clear_context_table(iommu, bus, devfn); iommu->flush.flush_context(iommu, 0, 0, 0, - DMA_CCMD_GLOBAL_INVL, 0); - iommu->flush.flush_iotlb(iommu, 0, 0, 0, - DMA_TLB_GLOBAL_FLUSH, 0); + DMA_CCMD_GLOBAL_INVL); + iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH); } static void domain_remove_dev_info(struct dmar_domain *domain) @@ -1561,6 +1682,7 @@ static void domain_remove_dev_info(struct dmar_domain *domain) info->dev->dev.archdata.iommu = NULL; spin_unlock_irqrestore(&device_domain_lock, flags); + iommu_disable_dev_iotlb(info); iommu = device_to_iommu(info->segment, info->bus, info->devfn); iommu_detach_dev(iommu, info->bus, info->devfn); free_devinfo_mem(info); @@ -1597,6 +1719,7 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) unsigned long flags; int bus = 0, devfn = 0; int segment; + int ret; domain = find_domain(pdev); if (domain) @@ -1629,6 +1752,10 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) } } + domain = alloc_domain(); + if (!domain) + goto error; + /* Allocate new domain for the device */ drhd = dmar_find_matched_drhd_unit(pdev); if (!drhd) { @@ -1638,9 +1765,11 @@ static struct dmar_domain *get_domain_for_dev(struct pci_dev *pdev, int gaw) } iommu = drhd->iommu; - domain = iommu_alloc_domain(iommu); - if (!domain) + ret = iommu_attach_domain(domain, iommu); + if (ret) { + domain_exit(domain); goto error; + } if (domain_init(domain, gaw)) { domain_exit(domain); @@ -1714,6 +1843,8 @@ error: return find_domain(pdev); } +static int iommu_identity_mapping; + static int iommu_prepare_identity_map(struct pci_dev *pdev, unsigned long long start, unsigned long long end) @@ -1726,8 +1857,11 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev, printk(KERN_INFO "IOMMU: Setting identity map for device %s [0x%Lx - 0x%Lx]\n", pci_name(pdev), start, end); - /* page table init */ - domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH); + if (iommu_identity_mapping) + domain = si_domain; + else + /* page table init */ + domain = get_domain_for_dev(pdev, DEFAULT_DOMAIN_ADDRESS_WIDTH); if (!domain) return -ENOMEM; @@ -1756,7 +1890,7 @@ static int iommu_prepare_identity_map(struct pci_dev *pdev, goto error; /* context entry init */ - ret = domain_context_mapping(domain, pdev); + ret = domain_context_mapping(domain, pdev, CONTEXT_TT_MULTI_LEVEL); if (!ret) return 0; error: @@ -1857,13 +1991,141 @@ static inline void iommu_prepare_isa(void) } #endif /* !CONFIG_DMAR_FLPY_WA */ -static int __init init_dmars(void) +/* 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 si_domain_init(void) +{ + struct dmar_drhd_unit *drhd; + struct intel_iommu *iommu; + int ret = 0; + + si_domain = alloc_domain(); + if (!si_domain) + return -EFAULT; + + + for_each_active_iommu(iommu, drhd) { + ret = iommu_attach_domain(si_domain, iommu); + if (ret) { + domain_exit(si_domain); + return -EFAULT; + } + } + + if (md_domain_init(si_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { + domain_exit(si_domain); + return -EFAULT; + } + + si_domain->flags = DOMAIN_FLAG_STATIC_IDENTITY; + + return 0; +} + +static void domain_remove_one_dev_info(struct dmar_domain *domain, + struct pci_dev *pdev); +static int identity_mapping(struct pci_dev *pdev) +{ + struct device_domain_info *info; + + if (likely(!iommu_identity_mapping)) + return 0; + + + list_for_each_entry(info, &si_domain->devices, link) + if (info->dev == pdev) + return 1; + return 0; +} + +static int domain_add_dev_info(struct dmar_domain *domain, + struct pci_dev *pdev) +{ + struct device_domain_info *info; + unsigned long flags; + + info = alloc_devinfo_mem(); + if (!info) + return -ENOMEM; + + info->segment = pci_domain_nr(pdev->bus); + info->bus = pdev->bus->number; + info->devfn = pdev->devfn; + info->dev = pdev; + info->domain = domain; + + spin_lock_irqsave(&device_domain_lock, flags); + list_add(&info->link, &domain->devices); + list_add(&info->global, &device_domain_list); + pdev->dev.archdata.iommu = info; + spin_unlock_irqrestore(&device_domain_lock, flags); + + return 0; +} + +static int iommu_prepare_static_identity_mapping(void) +{ + int i; + struct pci_dev *pdev = NULL; + int ret; + + ret = si_domain_init(); + if (ret) + return -EFAULT; + + printk(KERN_INFO "IOMMU: Setting identity map:\n"); + for_each_pci_dev(pdev) { + for (i = 0; i < e820.nr_map; i++) { + struct e820entry *ei = &e820.map[i]; + + if (ei->type == E820_RAM) { + ret = iommu_prepare_identity_map(pdev, + ei->addr, ei->addr + ei->size); + if (ret) { + printk(KERN_INFO "1:1 mapping to one domain failed.\n"); + return -EFAULT; + } + } + } + ret = domain_add_dev_info(si_domain, pdev); + if (ret) + return ret; + } + + return 0; +} + +int __init init_dmars(void) { struct dmar_drhd_unit *drhd; struct dmar_rmrr_unit *rmrr; 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 @@ -1917,7 +2179,15 @@ static int __init init_dmars(void) printk(KERN_ERR "IOMMU: allocate root entry failed\n"); goto error; } + if (!ecap_pass_through(iommu->ecap)) + 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. @@ -1973,35 +2243,60 @@ static int __init init_dmars(void) } /* - * 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 pass through is set and enabled, context entries of all pci + * devices are intialized by pass through translation type. */ - 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"); + if (iommu_pass_through) { + ret = init_context_pass_through(); + if (ret) { + printk(KERN_ERR "IOMMU: Pass through init failed.\n"); + iommu_pass_through = 0; } } - iommu_prepare_gfx_mapping(); + /* + * 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) { + 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"); + } + } + + iommu_prepare_gfx_mapping(); - iommu_prepare_isa(); + iommu_prepare_isa(); + } /* * for each drhd @@ -2023,10 +2318,8 @@ static int __init init_dmars(void) iommu_set_root_entry(iommu); - iommu->flush.flush_context(iommu, 0, 0, 0, DMA_CCMD_GLOBAL_INVL, - 0); - iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH, - 0); + 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); @@ -2112,7 +2405,8 @@ get_valid_domain_for_dev(struct pci_dev *pdev) /* make sure context mapping is ok */ if (unlikely(!domain_context_mapped(pdev))) { - ret = domain_context_mapping(domain, pdev); + ret = domain_context_mapping(domain, pdev, + CONTEXT_TT_MULTI_LEVEL); if (ret) { printk(KERN_ERR "Domain context map for %s failed", @@ -2124,6 +2418,52 @@ get_valid_domain_for_dev(struct pci_dev *pdev) return domain; } +static int iommu_dummy(struct pci_dev *pdev) +{ + return pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO; +} + +/* Check if the pdev needs to go through non-identity map and unmap process.*/ +static int iommu_no_mapping(struct pci_dev *pdev) +{ + int found; + + if (!iommu_identity_mapping) + return iommu_dummy(pdev); + + found = identity_mapping(pdev); + if (found) { + if (pdev->dma_mask > DMA_BIT_MASK(32)) + return 1; + else { + /* + * 32 bit DMA is removed from si_domain and fall back + * to non-identity mapping. + */ + domain_remove_one_dev_info(si_domain, pdev); + printk(KERN_INFO "32bit %s uses non-identity mapping\n", + pci_name(pdev)); + return 0; + } + } else { + /* + * In case of a detached 64 bit DMA device from vm, the device + * is put into si_domain for identity mapping. + */ + if (pdev->dma_mask > DMA_BIT_MASK(32)) { + int ret; + ret = domain_add_dev_info(si_domain, pdev); + if (!ret) { + printk(KERN_INFO "64bit %s uses identity mapping\n", + pci_name(pdev)); + return 1; + } + } + } + + return iommu_dummy(pdev); +} + static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, size_t size, int dir, u64 dma_mask) { @@ -2136,7 +2476,8 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, struct intel_iommu *iommu; BUG_ON(dir == DMA_NONE); - if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) + + if (iommu_no_mapping(pdev)) return paddr; domain = get_valid_domain_for_dev(pdev); @@ -2173,10 +2514,11 @@ static dma_addr_t __intel_map_single(struct device *hwdev, phys_addr_t paddr, if (ret) goto error; - /* it's a non-present to present mapping */ - ret = iommu_flush_iotlb_psi(iommu, domain->id, - start_paddr, size >> VTD_PAGE_SHIFT, 1); - if (ret) + /* it's a non-present to present mapping. Only flush if caching mode */ + if (cap_caching_mode(iommu->cap)) + iommu_flush_iotlb_psi(iommu, 0, start_paddr, + size >> VTD_PAGE_SHIFT); + else iommu_flush_write_buffer(iommu); return start_paddr + ((u64)paddr & (~PAGE_MASK)); @@ -2210,15 +2552,22 @@ static void flush_unmaps(void) if (!iommu) continue; - if (deferred_flush[i].next) { - iommu->flush.flush_iotlb(iommu, 0, 0, 0, - DMA_TLB_GLOBAL_FLUSH, 0); - for (j = 0; j < deferred_flush[i].next; j++) { - __free_iova(&deferred_flush[i].domain[j]->iovad, - deferred_flush[i].iova[j]); - } - deferred_flush[i].next = 0; + if (!deferred_flush[i].next) + continue; + + iommu->flush.flush_iotlb(iommu, 0, 0, 0, + DMA_TLB_GLOBAL_FLUSH); + for (j = 0; j < deferred_flush[i].next; j++) { + 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); + iommu_flush_dev_iotlb(deferred_flush[i].domain[j], + iova->pfn_lo << PAGE_SHIFT, mask); + __free_iova(&deferred_flush[i].domain[j]->iovad, iova); } + deferred_flush[i].next = 0; } list_size = 0; @@ -2269,8 +2618,9 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr, struct iova *iova; struct intel_iommu *iommu; - if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) + if (iommu_no_mapping(pdev)) return; + domain = find_domain(pdev); BUG_ON(!domain); @@ -2291,9 +2641,8 @@ static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr, /* free page tables */ dma_pte_free_pagetable(domain, start_addr, start_addr + size); if (intel_iommu_strict) { - if (iommu_flush_iotlb_psi(iommu, - domain->id, start_addr, size >> VTD_PAGE_SHIFT, 0)) - iommu_flush_write_buffer(iommu); + iommu_flush_iotlb_psi(iommu, domain->id, start_addr, + size >> VTD_PAGE_SHIFT); /* free iova */ __free_iova(&domain->iovad, iova); } else { @@ -2361,7 +2710,7 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, struct scatterlist *sg; struct intel_iommu *iommu; - if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) + if (iommu_no_mapping(pdev)) return; domain = find_domain(pdev); @@ -2384,9 +2733,8 @@ static void intel_unmap_sg(struct device *hwdev, struct scatterlist *sglist, /* free page tables */ dma_pte_free_pagetable(domain, start_addr, start_addr + size); - if (iommu_flush_iotlb_psi(iommu, domain->id, start_addr, - size >> VTD_PAGE_SHIFT, 0)) - iommu_flush_write_buffer(iommu); + iommu_flush_iotlb_psi(iommu, domain->id, start_addr, + size >> VTD_PAGE_SHIFT); /* free iova */ __free_iova(&domain->iovad, iova); @@ -2423,7 +2771,7 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne struct intel_iommu *iommu; BUG_ON(dir == DMA_NONE); - if (pdev->dev.archdata.iommu == DUMMY_DEVICE_DOMAIN_INFO) + if (iommu_no_mapping(pdev)) return intel_nontranslate_map_sg(hwdev, sglist, nelems, dir); domain = get_valid_domain_for_dev(pdev); @@ -2478,10 +2826,13 @@ static int intel_map_sg(struct device *hwdev, struct scatterlist *sglist, int ne offset += size; } - /* it's a non-present to present mapping */ - if (iommu_flush_iotlb_psi(iommu, domain->id, - start_addr, offset >> VTD_PAGE_SHIFT, 1)) + /* it's a non-present to present mapping. Only flush if caching mode */ + if (cap_caching_mode(iommu->cap)) + iommu_flush_iotlb_psi(iommu, 0, start_addr, + offset >> VTD_PAGE_SHIFT); + else iommu_flush_write_buffer(iommu); + return nelems; } @@ -2640,9 +2991,9 @@ static int init_iommu_hw(void) iommu_set_root_entry(iommu); iommu->flush.flush_context(iommu, 0, 0, 0, - DMA_CCMD_GLOBAL_INVL, 0); + DMA_CCMD_GLOBAL_INVL); iommu->flush.flush_iotlb(iommu, 0, 0, 0, - DMA_TLB_GLOBAL_FLUSH, 0); + DMA_TLB_GLOBAL_FLUSH); iommu_disable_protect_mem_regions(iommu); iommu_enable_translation(iommu); } @@ -2657,9 +3008,9 @@ static void iommu_flush_all(void) for_each_active_iommu(iommu, drhd) { iommu->flush.flush_context(iommu, 0, 0, 0, - DMA_CCMD_GLOBAL_INVL, 0); + DMA_CCMD_GLOBAL_INVL); iommu->flush.flush_iotlb(iommu, 0, 0, 0, - DMA_TLB_GLOBAL_FLUSH, 0); + DMA_TLB_GLOBAL_FLUSH); } } @@ -2782,7 +3133,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 || dmar_disabled) + if (no_iommu || (swiotlb && !iommu_pass_through) || dmar_disabled) return -ENODEV; iommu_init_mempool(); @@ -2802,35 +3153,18 @@ int __init intel_iommu_init(void) init_timer(&unmap_timer); force_iommu = 1; - dma_ops = &intel_dma_ops; - init_iommu_sysfs(); - - register_iommu(&intel_iommu_ops); - return 0; -} + 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"); -static int vm_domain_add_dev_info(struct dmar_domain *domain, - struct pci_dev *pdev) -{ - struct device_domain_info *info; - unsigned long flags; - - info = alloc_devinfo_mem(); - if (!info) - return -ENOMEM; - - info->segment = pci_domain_nr(pdev->bus); - info->bus = pdev->bus->number; - info->devfn = pdev->devfn; - info->dev = pdev; - info->domain = domain; + init_iommu_sysfs(); - spin_lock_irqsave(&device_domain_lock, flags); - list_add(&info->link, &domain->devices); - list_add(&info->global, &device_domain_list); - pdev->dev.archdata.iommu = info; - spin_unlock_irqrestore(&device_domain_lock, flags); + register_iommu(&intel_iommu_ops); return 0; } @@ -2862,7 +3196,7 @@ static void iommu_detach_dependent_devices(struct intel_iommu *iommu, } } -static void vm_domain_remove_one_dev_info(struct dmar_domain *domain, +static void domain_remove_one_dev_info(struct dmar_domain *domain, struct pci_dev *pdev) { struct device_domain_info *info; @@ -2888,6 +3222,7 @@ static void vm_domain_remove_one_dev_info(struct dmar_domain *domain, info->dev->dev.archdata.iommu = NULL; spin_unlock_irqrestore(&device_domain_lock, flags); + iommu_disable_dev_iotlb(info); iommu_detach_dev(iommu, info->bus, info->devfn); iommu_detach_dependent_devices(iommu, pdev); free_devinfo_mem(info); @@ -2938,6 +3273,7 @@ static void vm_domain_remove_all_dev_info(struct dmar_domain *domain) spin_unlock_irqrestore(&device_domain_lock, flags1); + iommu_disable_dev_iotlb(info); iommu = device_to_iommu(info->segment, info->bus, info->devfn); iommu_detach_dev(iommu, info->bus, info->devfn); iommu_detach_dependent_devices(iommu, info->dev); @@ -2993,7 +3329,7 @@ static struct dmar_domain *iommu_alloc_vm_domain(void) return domain; } -static int vm_domain_init(struct dmar_domain *domain, int guest_width) +static int md_domain_init(struct dmar_domain *domain, int guest_width) { int adjust_width; @@ -3084,7 +3420,7 @@ static int intel_iommu_domain_init(struct iommu_domain *domain) "intel_iommu_domain_init: dmar_domain == NULL\n"); return -ENOMEM; } - if (vm_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { + if (md_domain_init(dmar_domain, DEFAULT_DOMAIN_ADDRESS_WIDTH)) { printk(KERN_ERR "intel_iommu_domain_init() failed\n"); vm_domain_exit(dmar_domain); @@ -3119,8 +3455,9 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, old_domain = find_domain(pdev); if (old_domain) { - if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE) - vm_domain_remove_one_dev_info(old_domain, pdev); + if (dmar_domain->flags & DOMAIN_FLAG_VIRTUAL_MACHINE || + dmar_domain->flags & DOMAIN_FLAG_STATIC_IDENTITY) + domain_remove_one_dev_info(old_domain, pdev); else domain_remove_dev_info(old_domain); } @@ -3142,11 +3479,11 @@ static int intel_iommu_attach_device(struct iommu_domain *domain, return -EFAULT; } - ret = domain_context_mapping(dmar_domain, pdev); + ret = domain_add_dev_info(dmar_domain, pdev); if (ret) return ret; - ret = vm_domain_add_dev_info(dmar_domain, pdev); + ret = domain_context_mapping(dmar_domain, pdev, CONTEXT_TT_MULTI_LEVEL); return ret; } @@ -3156,7 +3493,7 @@ static void intel_iommu_detach_device(struct iommu_domain *domain, struct dmar_domain *dmar_domain = domain->priv; struct pci_dev *pdev = to_pci_dev(dev); - vm_domain_remove_one_dev_info(dmar_domain, pdev); + domain_remove_one_dev_info(dmar_domain, pdev); } static int intel_iommu_map_range(struct iommu_domain *domain, diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 3a0cb0bb059..4f5b8712931 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -10,6 +10,8 @@ #include <linux/intel-iommu.h> #include "intr_remapping.h" #include <acpi/acpi.h> +#include <asm/pci-direct.h> +#include "pci.h" static struct ioapic_scope ir_ioapic[MAX_IO_APICS]; static int ir_ioapic_num; @@ -314,7 +316,8 @@ int modify_irte(int irq, struct irte *irte_modified) index = irq_iommu->irte_index + irq_iommu->sub_handle; irte = &iommu->ir_table->base[index]; - set_64bit((unsigned long *)irte, irte_modified->low); + set_64bit((unsigned long *)&irte->low, irte_modified->low); + set_64bit((unsigned long *)&irte->high, irte_modified->high); __iommu_flush_cache(iommu, irte, sizeof(*irte)); rc = qi_flush_iec(iommu, index, 0); @@ -369,12 +372,32 @@ struct intel_iommu *map_dev_to_ir(struct pci_dev *dev) return drhd->iommu; } +static int clear_entries(struct irq_2_iommu *irq_iommu) +{ + struct irte *start, *entry, *end; + struct intel_iommu *iommu; + int index; + + if (irq_iommu->sub_handle) + return 0; + + iommu = irq_iommu->iommu; + index = irq_iommu->irte_index + irq_iommu->sub_handle; + + start = iommu->ir_table->base + index; + end = start + (1 << irq_iommu->irte_mask); + + for (entry = start; entry < end; entry++) { + set_64bit((unsigned long *)&entry->low, 0); + set_64bit((unsigned long *)&entry->high, 0); + } + + return qi_flush_iec(iommu, index, irq_iommu->irte_mask); +} + int free_irte(int irq) { int rc = 0; - int index, i; - struct irte *irte; - struct intel_iommu *iommu; struct irq_2_iommu *irq_iommu; unsigned long flags; @@ -385,16 +408,7 @@ int free_irte(int irq) return -1; } - iommu = irq_iommu->iommu; - - index = irq_iommu->irte_index + irq_iommu->sub_handle; - irte = &iommu->ir_table->base[index]; - - if (!irq_iommu->sub_handle) { - for (i = 0; i < (1 << irq_iommu->irte_mask); i++) - set_64bit((unsigned long *)(irte + i), 0); - rc = qi_flush_iec(iommu, index, irq_iommu->irte_mask); - } + rc = clear_entries(irq_iommu); irq_iommu->iommu = NULL; irq_iommu->irte_index = 0; @@ -406,10 +420,95 @@ int free_irte(int irq) return rc; } +/* + * source validation type + */ +#define SVT_NO_VERIFY 0x0 /* no verification is required */ +#define SVT_VERIFY_SID_SQ 0x1 /* verify using SID and SQ fiels */ +#define SVT_VERIFY_BUS 0x2 /* verify bus of request-id */ + +/* + * source-id qualifier + */ +#define SQ_ALL_16 0x0 /* verify all 16 bits of request-id */ +#define SQ_13_IGNORE_1 0x1 /* verify most significant 13 bits, ignore + * the third least significant bit + */ +#define SQ_13_IGNORE_2 0x2 /* verify most significant 13 bits, ignore + * the second and third least significant bits + */ +#define SQ_13_IGNORE_3 0x3 /* verify most significant 13 bits, ignore + * the least three significant bits + */ + +/* + * set SVT, SQ and SID fields of irte to verify + * source ids of interrupt requests + */ +static void set_irte_sid(struct irte *irte, unsigned int svt, + unsigned int sq, unsigned int sid) +{ + irte->svt = svt; + irte->sq = sq; + irte->sid = sid; +} + +int set_ioapic_sid(struct irte *irte, int apic) +{ + int i; + u16 sid = 0; + + if (!irte) + return -1; + + for (i = 0; i < MAX_IO_APICS; i++) { + if (ir_ioapic[i].id == apic) { + sid = (ir_ioapic[i].bus << 8) | ir_ioapic[i].devfn; + break; + } + } + + if (sid == 0) { + pr_warning("Failed to set source-id of IOAPIC (%d)\n", apic); + return -1; + } + + set_irte_sid(irte, 1, 0, sid); + + return 0; +} + +int set_msi_sid(struct irte *irte, struct pci_dev *dev) +{ + struct pci_dev *bridge; + + if (!irte || !dev) + return -1; + + /* PCIe device or Root Complex integrated PCI device */ + if (dev->is_pcie || !dev->bus->parent) { + set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, + (dev->bus->number << 8) | dev->devfn); + return 0; + } + + bridge = pci_find_upstream_pcie_bridge(dev); + if (bridge) { + if (bridge->is_pcie) /* this is a PCIE-to-PCI/PCIX bridge */ + set_irte_sid(irte, SVT_VERIFY_BUS, SQ_ALL_16, + (bridge->bus->number << 8) | dev->bus->number); + else /* this is a legacy PCI bridge */ + set_irte_sid(irte, SVT_VERIFY_SID_SQ, SQ_ALL_16, + (bridge->bus->number << 8) | bridge->devfn); + } + + return 0; +} + static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) { u64 addr; - u32 cmd, sts; + u32 sts; unsigned long flags; addr = virt_to_phys((void *)iommu->ir_table->base); @@ -420,9 +519,8 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) (addr) | IR_X2APIC_MODE(mode) | INTR_REMAP_TABLE_REG_SIZE); /* Set interrupt-remapping table pointer */ - cmd = iommu->gcmd | DMA_GCMD_SIRTP; iommu->gcmd |= DMA_GCMD_SIRTP; - writel(cmd, iommu->reg + DMAR_GCMD_REG); + writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG); IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_IRTPS), sts); @@ -437,9 +535,8 @@ static void iommu_set_intr_remapping(struct intel_iommu *iommu, int mode) spin_lock_irqsave(&iommu->register_lock, flags); /* Enable interrupt-remapping */ - cmd = iommu->gcmd | DMA_GCMD_IRE; iommu->gcmd |= DMA_GCMD_IRE; - writel(cmd, iommu->reg + DMAR_GCMD_REG); + writel(iommu->gcmd, iommu->reg + DMAR_GCMD_REG); IOMMU_WAIT_OP(iommu, DMAR_GSTS_REG, readl, (sts & DMA_GSTS_IRES), sts); @@ -614,6 +711,35 @@ error: return -1; } +static void ir_parse_one_ioapic_scope(struct acpi_dmar_device_scope *scope, + struct intel_iommu *iommu) +{ + struct acpi_dmar_pci_path *path; + u8 bus; + int count; + + bus = scope->bus; + path = (struct acpi_dmar_pci_path *)(scope + 1); + count = (scope->length - sizeof(struct acpi_dmar_device_scope)) + / sizeof(struct acpi_dmar_pci_path); + + while (--count > 0) { + /* + * Access PCI directly due to the PCI + * subsystem isn't initialized yet. + */ + bus = read_pci_config_byte(bus, path->dev, path->fn, + PCI_SECONDARY_BUS); + path++; + } + + ir_ioapic[ir_ioapic_num].bus = bus; + ir_ioapic[ir_ioapic_num].devfn = PCI_DEVFN(path->dev, path->fn); + ir_ioapic[ir_ioapic_num].iommu = iommu; + ir_ioapic[ir_ioapic_num].id = scope->enumeration_id; + ir_ioapic_num++; +} + static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, struct intel_iommu *iommu) { @@ -638,9 +764,7 @@ static int ir_parse_ioapic_scope(struct acpi_dmar_header *header, " 0x%Lx\n", scope->enumeration_id, drhd->address); - ir_ioapic[ir_ioapic_num].iommu = iommu; - ir_ioapic[ir_ioapic_num].id = scope->enumeration_id; - ir_ioapic_num++; + ir_parse_one_ioapic_scope(scope, iommu); } start += scope->length; } diff --git a/drivers/pci/intr_remapping.h b/drivers/pci/intr_remapping.h index ca48f0df8ac..63a263c1841 100644 --- a/drivers/pci/intr_remapping.h +++ b/drivers/pci/intr_remapping.h @@ -3,6 +3,8 @@ struct ioapic_scope { struct intel_iommu *iommu; unsigned int id; + unsigned int bus; /* PCI bus number */ + unsigned int devfn; /* PCI devfn number */ }; #define IR_X2APIC_MODE(mode) (mode ? (1 << 11) : 0) diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index b497daab3d4..e3a87210e94 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -5,6 +5,7 @@ * * PCI Express I/O Virtualization (IOV) support. * Single Root IOV 1.0 + * Address Translation Service 1.0 */ #include <linux/pci.h> @@ -110,7 +111,7 @@ static int virtfn_add(struct pci_dev *dev, int id, int reset) } if (reset) - pci_execute_reset_function(virtfn); + __pci_reset_function(virtfn); pci_device_add(virtfn, virtfn->bus); mutex_unlock(&iov->dev->sriov->lock); @@ -164,7 +165,7 @@ static void virtfn_remove(struct pci_dev *dev, int id, int reset) if (reset) { device_release_driver(&virtfn->dev); - pci_execute_reset_function(virtfn); + __pci_reset_function(virtfn); } sprintf(buf, "virtfn%u", id); @@ -487,13 +488,15 @@ found: iov->self = dev; pci_read_config_dword(dev, pos + PCI_SRIOV_CAP, &iov->cap); pci_read_config_byte(dev, pos + PCI_SRIOV_FUNC_LINK, &iov->link); + if (dev->pcie_type == PCI_EXP_TYPE_RC_END) + iov->link = PCI_DEVFN(PCI_SLOT(dev->devfn), iov->link); if (pdev) iov->dev = pci_dev_get(pdev); - else { + else iov->dev = dev; - mutex_init(&iov->lock); - } + + mutex_init(&iov->lock); dev->sriov = iov; dev->is_physfn = 1; @@ -513,11 +516,11 @@ static void sriov_release(struct pci_dev *dev) { BUG_ON(dev->sriov->nr_virtfn); - if (dev == dev->sriov->dev) - mutex_destroy(&dev->sriov->lock); - else + if (dev != dev->sriov->dev) pci_dev_put(dev->sriov->dev); + mutex_destroy(&dev->sriov->lock); + kfree(dev->sriov); dev->sriov = NULL; } @@ -679,3 +682,145 @@ irqreturn_t pci_sriov_migration(struct pci_dev *dev) return sriov_migration(dev) ? IRQ_HANDLED : IRQ_NONE; } EXPORT_SYMBOL_GPL(pci_sriov_migration); + +static int ats_alloc_one(struct pci_dev *dev, int ps) +{ + int pos; + u16 cap; + struct pci_ats *ats; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); + if (!pos) + return -ENODEV; + + ats = kzalloc(sizeof(*ats), GFP_KERNEL); + if (!ats) + return -ENOMEM; + + ats->pos = pos; + ats->stu = ps; + pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); + ats->qdep = PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : + PCI_ATS_MAX_QDEP; + dev->ats = ats; + + return 0; +} + +static void ats_free_one(struct pci_dev *dev) +{ + kfree(dev->ats); + dev->ats = NULL; +} + +/** + * pci_enable_ats - enable the ATS capability + * @dev: the PCI device + * @ps: the IOMMU page shift + * + * Returns 0 on success, or negative on failure. + */ +int pci_enable_ats(struct pci_dev *dev, int ps) +{ + int rc; + u16 ctrl; + + BUG_ON(dev->ats && dev->ats->is_enabled); + + if (ps < PCI_ATS_MIN_STU) + return -EINVAL; + + if (dev->is_physfn || dev->is_virtfn) { + struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; + + mutex_lock(&pdev->sriov->lock); + if (pdev->ats) + rc = pdev->ats->stu == ps ? 0 : -EINVAL; + else + rc = ats_alloc_one(pdev, ps); + + if (!rc) + pdev->ats->ref_cnt++; + mutex_unlock(&pdev->sriov->lock); + if (rc) + return rc; + } + + if (!dev->is_physfn) { + rc = ats_alloc_one(dev, ps); + if (rc) + return rc; + } + + ctrl = PCI_ATS_CTRL_ENABLE; + if (!dev->is_virtfn) + ctrl |= PCI_ATS_CTRL_STU(ps - PCI_ATS_MIN_STU); + pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); + + dev->ats->is_enabled = 1; + + return 0; +} + +/** + * pci_disable_ats - disable the ATS capability + * @dev: the PCI device + */ +void pci_disable_ats(struct pci_dev *dev) +{ + u16 ctrl; + + BUG_ON(!dev->ats || !dev->ats->is_enabled); + + pci_read_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, &ctrl); + ctrl &= ~PCI_ATS_CTRL_ENABLE; + pci_write_config_word(dev, dev->ats->pos + PCI_ATS_CTRL, ctrl); + + dev->ats->is_enabled = 0; + + if (dev->is_physfn || dev->is_virtfn) { + struct pci_dev *pdev = dev->is_physfn ? dev : dev->physfn; + + mutex_lock(&pdev->sriov->lock); + pdev->ats->ref_cnt--; + if (!pdev->ats->ref_cnt) + ats_free_one(pdev); + mutex_unlock(&pdev->sriov->lock); + } + + if (!dev->is_physfn) + ats_free_one(dev); +} + +/** + * pci_ats_queue_depth - query the ATS Invalidate Queue Depth + * @dev: the PCI device + * + * Returns the queue depth on success, or negative on failure. + * + * The ATS spec uses 0 in the Invalidate Queue Depth field to + * indicate that the function can accept 32 Invalidate Request. + * But here we use the `real' values (i.e. 1~32) for the Queue + * Depth; and 0 indicates the function shares the Queue with + * other functions (doesn't exclusively own a Queue). + */ +int pci_ats_queue_depth(struct pci_dev *dev) +{ + int pos; + u16 cap; + + if (dev->is_virtfn) + return 0; + + if (dev->ats) + return dev->ats->qdep; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ATS); + if (!pos) + return -ENODEV; + + pci_read_config_word(dev, pos + PCI_ATS_CAP, &cap); + + return PCI_ATS_CAP_QDEP(cap) ? PCI_ATS_CAP_QDEP(cap) : + PCI_ATS_MAX_QDEP; +} diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 362773247fb..d9f06fbfa0b 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -75,22 +75,17 @@ void arch_teardown_msi_irqs(struct pci_dev *dev) } #endif -static void __msi_set_enable(struct pci_dev *dev, int pos, int enable) +static void msi_set_enable(struct pci_dev *dev, int pos, int enable) { u16 control; - if (pos) { - pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); - control &= ~PCI_MSI_FLAGS_ENABLE; - if (enable) - control |= PCI_MSI_FLAGS_ENABLE; - pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); - } -} + BUG_ON(!pos); -static void msi_set_enable(struct pci_dev *dev, int enable) -{ - __msi_set_enable(dev, pci_find_capability(dev, PCI_CAP_ID_MSI), enable); + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); + control &= ~PCI_MSI_FLAGS_ENABLE; + if (enable) + control |= PCI_MSI_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); } static void msix_set_enable(struct pci_dev *dev, int enable) @@ -131,9 +126,6 @@ static inline __attribute_const__ u32 msi_enabled_mask(u16 control) * mask all MSI interrupts by clearing the MSI enable bit does not work * reliably as devices without an INTx disable bit will then generate a * level IRQ which will never be cleared. - * - * Returns 1 if it succeeded in masking the interrupt and 0 if the device - * doesn't support MSI masking. */ static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { @@ -303,7 +295,7 @@ static void __pci_restore_msi_state(struct pci_dev *dev) pos = entry->msi_attrib.pos; pci_intx_for_msi(dev, 0); - msi_set_enable(dev, 0); + msi_set_enable(dev, pos, 0); write_msi_msg(dev->irq, &entry->msg); pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); @@ -321,22 +313,22 @@ static void __pci_restore_msix_state(struct pci_dev *dev) if (!dev->msix_enabled) return; + BUG_ON(list_empty(&dev->msi_list)); + entry = list_entry(dev->msi_list.next, struct msi_desc, list); + pos = entry->msi_attrib.pos; + pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); /* route the table */ pci_intx_for_msi(dev, 0); - msix_set_enable(dev, 0); + control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); list_for_each_entry(entry, &dev->msi_list, list) { write_msi_msg(entry->irq, &entry->msg); msix_mask_irq(entry, entry->masked); } - BUG_ON(list_empty(&dev->msi_list)); - entry = list_entry(dev->msi_list.next, struct msi_desc, list); - pos = entry->msi_attrib.pos; - pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); control &= ~PCI_MSIX_FLAGS_MASKALL; - control |= PCI_MSIX_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); } @@ -365,9 +357,9 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) u16 control; unsigned mask; - msi_set_enable(dev, 0); /* Ensure msi is disabled as I set it up */ - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + msi_set_enable(dev, pos, 0); /* Disable MSI during set up */ + pci_read_config_word(dev, msi_control_reg(pos), &control); /* MSI Entry Initialization */ entry = alloc_msi_entry(dev); @@ -381,7 +373,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = pos; - entry->mask_pos = msi_mask_bits_reg(pos, entry->msi_attrib.is_64); + entry->mask_pos = msi_mask_reg(pos, entry->msi_attrib.is_64); /* All MSIs are unmasked by default, Mask them all */ if (entry->msi_attrib.maskbit) pci_read_config_dword(dev, entry->mask_pos, &entry->masked); @@ -399,7 +391,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) /* Set MSI enabled bits */ pci_intx_for_msi(dev, 0); - msi_set_enable(dev, 1); + msi_set_enable(dev, pos, 1); dev->msi_enabled = 1; dev->irq = entry->irq; @@ -427,11 +419,14 @@ static int msix_capability_init(struct pci_dev *dev, u8 bir; void __iomem *base; - msix_set_enable(dev, 0);/* Ensure msix is disabled as I set it up */ - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); + + /* Ensure MSI-X is disabled while it is set up */ + control &= ~PCI_MSIX_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + /* Request & Map MSI-X table region */ - pci_read_config_word(dev, msi_control_reg(pos), &control); nr_entries = multi_msix_capable(control); pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset); @@ -442,7 +437,6 @@ static int msix_capability_init(struct pci_dev *dev, if (base == NULL) return -ENOMEM; - /* MSI-X Table Initialization */ for (i = 0; i < nvec; i++) { entry = alloc_msi_entry(dev); if (!entry) @@ -455,7 +449,6 @@ static int msix_capability_init(struct pci_dev *dev, entry->msi_attrib.default_irq = dev->irq; entry->msi_attrib.pos = pos; entry->mask_base = base; - msix_mask_irq(entry, 1); list_add_tail(&entry->list, &dev->msi_list); } @@ -480,22 +473,31 @@ static int msix_capability_init(struct pci_dev *dev, return ret; } + /* + * Some devices require MSI-X to be enabled before we can touch the + * MSI-X registers. We need to mask all the vectors to prevent + * interrupts coming in before they're fully set up. + */ + control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + i = 0; list_for_each_entry(entry, &dev->msi_list, list) { entries[i].vector = entry->irq; set_irq_msi(entry->irq, entry); + j = entries[i].entry; + entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); + msix_mask_irq(entry, 1); i++; } - /* Set MSI-X enabled bits */ + + /* Set MSI-X enabled bits and unmask the function */ pci_intx_for_msi(dev, 0); - msix_set_enable(dev, 1); dev->msix_enabled = 1; - list_for_each_entry(entry, &dev->msi_list, list) { - int vector = entry->msi_attrib.entry_nr; - entry->masked = readl(base + vector * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); - } + control &= ~PCI_MSIX_FLAGS_MASKALL; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); return 0; } @@ -596,17 +598,20 @@ void pci_msi_shutdown(struct pci_dev *dev) struct msi_desc *desc; u32 mask; u16 ctrl; + unsigned pos; if (!pci_msi_enable || !dev || !dev->msi_enabled) return; - msi_set_enable(dev, 0); + BUG_ON(list_empty(&dev->msi_list)); + desc = list_first_entry(&dev->msi_list, struct msi_desc, list); + pos = desc->msi_attrib.pos; + + msi_set_enable(dev, pos, 0); pci_intx_for_msi(dev, 1); dev->msi_enabled = 0; - BUG_ON(list_empty(&dev->msi_list)); - desc = list_first_entry(&dev->msi_list, struct msi_desc, list); - pci_read_config_word(dev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &ctrl); + pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &ctrl); mask = msi_capable_mask(ctrl); msi_mask_irq(desc, mask, ~mask); @@ -648,10 +653,7 @@ static int msi_free_irqs(struct pci_dev* dev) list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { if (entry->msi_attrib.is_msix) { - writel(1, entry->mask_base + entry->msi_attrib.entry_nr - * PCI_MSIX_ENTRY_SIZE - + PCI_MSIX_ENTRY_VECTOR_CTRL_OFFSET); - + msix_mask_irq(entry, 1); if (list_is_last(&entry->list, &dev->msi_list)) iounmap(entry->mask_base); } @@ -691,8 +693,8 @@ int pci_msix_table_size(struct pci_dev *dev) * indicates the successful configuration of MSI-X capability structure * with new allocated MSI-X irqs. A return of < 0 indicates a failure. * Or a return of > 0 indicates that driver request is exceeding the number - * of irqs available. Driver should use the returned value to re-send - * its request. + * of irqs or MSI-X vectors available. Driver should use the returned value to + * re-send its request. **/ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) { @@ -708,7 +710,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) nr_entries = pci_msix_table_size(dev); if (nvec > nr_entries) - return -EINVAL; + return nr_entries; /* Check for any invalid entries */ for (i = 0; i < nvec; i++) { diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index 71f4df2ef65..a0662842550 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h @@ -16,21 +16,15 @@ #define msi_lower_address_reg(base) (base + PCI_MSI_ADDRESS_LO) #define msi_upper_address_reg(base) (base + PCI_MSI_ADDRESS_HI) #define msi_data_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_DATA_64 : base+PCI_MSI_DATA_32 ) -#define msi_mask_bits_reg(base, is64bit) \ - ( (is64bit == 1) ? base+PCI_MSI_MASK_BIT : base+PCI_MSI_MASK_BIT-4) -#define msi_disable(control) control &= ~PCI_MSI_FLAGS_ENABLE + (base + ((is64bit == 1) ? PCI_MSI_DATA_64 : PCI_MSI_DATA_32)) +#define msi_mask_reg(base, is64bit) \ + (base + ((is64bit == 1) ? PCI_MSI_MASK_64 : PCI_MSI_MASK_32)) #define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) #define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) #define msix_table_offset_reg(base) (base + 0x04) #define msix_pba_offset_reg(base) (base + 0x08) -#define msix_enable(control) control |= PCI_MSIX_FLAGS_ENABLE -#define msix_disable(control) control &= ~PCI_MSIX_FLAGS_ENABLE #define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) -#define multi_msix_capable msix_table_size -#define msix_unmask(address) (address & ~PCI_MSIX_FLAGS_BITMASK) -#define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) -#define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) +#define multi_msix_capable(control) msix_table_size((control)) #endif /* MSI_H */ diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 07bbb9b3b93..6c93af5ced1 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -485,6 +485,8 @@ static int pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) pmcsr &= ~PCI_PM_CTRL_STATE_MASK; pmcsr |= state; break; + case PCI_D3hot: + case PCI_D3cold: case PCI_UNKNOWN: /* Boot-up */ if ((pmcsr & PCI_PM_CTRL_STATE_MASK) == PCI_D3hot && !(pmcsr & PCI_PM_CTRL_NO_SOFT_RESET)) @@ -1208,7 +1210,7 @@ void pci_pme_active(struct pci_dev *dev, bool enable) * Error code depending on the platform is returned if both the platform and * the native mechanism fail to enable the generation of wake-up events */ -int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) +int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) { int error = 0; bool pme_done = false; @@ -1287,15 +1289,14 @@ pci_power_t pci_target_state(struct pci_dev *dev) default: target_state = state; } + } else if (!dev->pm_cap) { + target_state = PCI_D0; } else if (device_may_wakeup(&dev->dev)) { /* * Find the deepest state from which the device can generate * wake-up events, make it the target state and enable device * to generate PME#. */ - if (!dev->pm_cap) - return PCI_POWER_ERROR; - if (dev->pme_support) { while (target_state && !(dev->pme_support & (1 << target_state))) @@ -1532,7 +1533,7 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) if (!pin) return -1; - while (dev->bus->parent) { + while (!pci_is_root_bus(dev->bus)) { pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; } @@ -1552,7 +1553,7 @@ u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) { u8 pin = *pinp; - while (dev->bus->parent) { + while (!pci_is_root_bus(dev->bus)) { pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; } @@ -2058,111 +2059,177 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) EXPORT_SYMBOL(pci_set_dma_seg_boundary); #endif -static int __pcie_flr(struct pci_dev *dev, int probe) +static int pcie_flr(struct pci_dev *dev, int probe) { - u16 status; + int i; + int pos; u32 cap; - int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); + u16 status; - if (!exppos) + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pos) return -ENOTTY; - pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); + + pci_read_config_dword(dev, pos + PCI_EXP_DEVCAP, &cap); if (!(cap & PCI_EXP_DEVCAP_FLR)) return -ENOTTY; if (probe) return 0; - pci_block_user_cfg_access(dev); - /* Wait for Transaction Pending bit clean */ - pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); - if (!(status & PCI_EXP_DEVSTA_TRPND)) - goto transaction_done; + for (i = 0; i < 4; i++) { + if (i) + msleep((1 << (i - 1)) * 100); - msleep(100); - pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); - if (!(status & PCI_EXP_DEVSTA_TRPND)) - goto transaction_done; - - dev_info(&dev->dev, "Busy after 100ms while trying to reset; " - "sleeping for 1 second\n"); - ssleep(1); - pci_read_config_word(dev, exppos + PCI_EXP_DEVSTA, &status); - if (status & PCI_EXP_DEVSTA_TRPND) - dev_info(&dev->dev, "Still busy after 1s; " - "proceeding with reset anyway\n"); - -transaction_done: - pci_write_config_word(dev, exppos + PCI_EXP_DEVCTL, + pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &status); + if (!(status & PCI_EXP_DEVSTA_TRPND)) + goto clear; + } + + dev_err(&dev->dev, "transaction is not cleared; " + "proceeding with reset anyway\n"); + +clear: + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, PCI_EXP_DEVCTL_BCR_FLR); - mdelay(100); + msleep(100); - pci_unblock_user_cfg_access(dev); return 0; } -static int __pci_af_flr(struct pci_dev *dev, int probe) +static int pci_af_flr(struct pci_dev *dev, int probe) { - int cappos = pci_find_capability(dev, PCI_CAP_ID_AF); - u8 status; + int i; + int pos; u8 cap; + u8 status; - if (!cappos) + pos = pci_find_capability(dev, PCI_CAP_ID_AF); + if (!pos) return -ENOTTY; - pci_read_config_byte(dev, cappos + PCI_AF_CAP, &cap); + + pci_read_config_byte(dev, pos + PCI_AF_CAP, &cap); if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR)) return -ENOTTY; if (probe) return 0; - pci_block_user_cfg_access(dev); - /* Wait for Transaction Pending bit clean */ - pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); - if (!(status & PCI_AF_STATUS_TP)) - goto transaction_done; + for (i = 0; i < 4; i++) { + if (i) + msleep((1 << (i - 1)) * 100); + + pci_read_config_byte(dev, pos + PCI_AF_STATUS, &status); + if (!(status & PCI_AF_STATUS_TP)) + goto clear; + } + dev_err(&dev->dev, "transaction is not cleared; " + "proceeding with reset anyway\n"); + +clear: + pci_write_config_byte(dev, pos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); msleep(100); - pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); - if (!(status & PCI_AF_STATUS_TP)) - goto transaction_done; - - dev_info(&dev->dev, "Busy after 100ms while trying to" - " reset; sleeping for 1 second\n"); - ssleep(1); - pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); - if (status & PCI_AF_STATUS_TP) - dev_info(&dev->dev, "Still busy after 1s; " - "proceeding with reset anyway\n"); - -transaction_done: - pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); - mdelay(100); - - pci_unblock_user_cfg_access(dev); + return 0; } -static int __pci_reset_function(struct pci_dev *pdev, int probe) +static int pci_pm_reset(struct pci_dev *dev, int probe) { - int res; + u16 csr; + + if (!dev->pm_cap) + return -ENOTTY; - res = __pcie_flr(pdev, probe); - if (res != -ENOTTY) - return res; + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &csr); + if (csr & PCI_PM_CTRL_NO_SOFT_RESET) + return -ENOTTY; - res = __pci_af_flr(pdev, probe); - if (res != -ENOTTY) - return res; + if (probe) + return 0; - return res; + if (dev->current_state != PCI_D0) + return -EINVAL; + + csr &= ~PCI_PM_CTRL_STATE_MASK; + csr |= PCI_D3hot; + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr); + msleep(pci_pm_d3_delay); + + csr &= ~PCI_PM_CTRL_STATE_MASK; + csr |= PCI_D0; + pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, csr); + msleep(pci_pm_d3_delay); + + return 0; +} + +static int pci_parent_bus_reset(struct pci_dev *dev, int probe) +{ + u16 ctrl; + struct pci_dev *pdev; + + if (dev->subordinate) + return -ENOTTY; + + list_for_each_entry(pdev, &dev->bus->devices, bus_list) + if (pdev != dev) + return -ENOTTY; + + if (probe) + return 0; + + pci_read_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, &ctrl); + ctrl |= PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl); + msleep(100); + + ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET; + pci_write_config_word(dev->bus->self, PCI_BRIDGE_CONTROL, ctrl); + msleep(100); + + return 0; +} + +static int pci_dev_reset(struct pci_dev *dev, int probe) +{ + int rc; + + might_sleep(); + + if (!probe) { + pci_block_user_cfg_access(dev); + /* block PM suspend, driver probe, etc. */ + down(&dev->dev.sem); + } + + rc = pcie_flr(dev, probe); + if (rc != -ENOTTY) + goto done; + + rc = pci_af_flr(dev, probe); + if (rc != -ENOTTY) + goto done; + + rc = pci_pm_reset(dev, probe); + if (rc != -ENOTTY) + goto done; + + rc = pci_parent_bus_reset(dev, probe); +done: + if (!probe) { + up(&dev->dev.sem); + pci_unblock_user_cfg_access(dev); + } + + return rc; } /** - * pci_execute_reset_function() - Reset a PCI device function - * @dev: Device function to reset + * __pci_reset_function - reset a PCI device function + * @dev: PCI device to reset * * Some devices allow an individual function to be reset without affecting * other functions in the same device. The PCI device must be responsive @@ -2174,18 +2241,18 @@ static int __pci_reset_function(struct pci_dev *pdev, int probe) * device including MSI, bus mastering, BARs, decoding IO and memory spaces, * etc. * - * Returns 0 if the device function was successfully reset or -ENOTTY if the + * Returns 0 if the device function was successfully reset or negative if the * device doesn't support resetting a single function. */ -int pci_execute_reset_function(struct pci_dev *dev) +int __pci_reset_function(struct pci_dev *dev) { - return __pci_reset_function(dev, 0); + return pci_dev_reset(dev, 0); } -EXPORT_SYMBOL_GPL(pci_execute_reset_function); +EXPORT_SYMBOL_GPL(__pci_reset_function); /** - * pci_reset_function() - quiesce and reset a PCI device function - * @dev: Device function to reset + * pci_reset_function - quiesce and reset a PCI device function + * @dev: PCI device to reset * * Some devices allow an individual function to be reset without affecting * other functions in the same device. The PCI device must be responsive @@ -2193,32 +2260,33 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function); * * This function does not just reset the PCI portion of a device, but * clears all the state associated with the device. This function differs - * from pci_execute_reset_function in that it saves and restores device state + * from __pci_reset_function in that it saves and restores device state * over the reset. * - * Returns 0 if the device function was successfully reset or -ENOTTY if the + * Returns 0 if the device function was successfully reset or negative if the * device doesn't support resetting a single function. */ int pci_reset_function(struct pci_dev *dev) { - int r = __pci_reset_function(dev, 1); + int rc; - if (r < 0) - return r; + rc = pci_dev_reset(dev, 1); + if (rc) + return rc; - if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0) - disable_irq(dev->irq); pci_save_state(dev); + /* + * both INTx and MSI are disabled after the Interrupt Disable bit + * is set and the Bus Master bit is cleared. + */ pci_write_config_word(dev, PCI_COMMAND, PCI_COMMAND_INTX_DISABLE); - r = pci_execute_reset_function(dev); + rc = pci_dev_reset(dev, 0); pci_restore_state(dev); - if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0) - enable_irq(dev->irq); - return r; + return rc; } EXPORT_SYMBOL_GPL(pci_reset_function); @@ -2591,6 +2659,8 @@ static int __init pci_setup(char *str) } else if (!strncmp(str, "resource_alignment=", 19)) { pci_set_resource_alignment_param(str + 19, strlen(str + 19)); + } else if (!strncmp(str, "ecrc=", 5)) { + pcie_ecrc_get_policy(str + 5); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d03f6b99f29..f73bcbedf37 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -229,6 +229,15 @@ struct pci_sriov { u8 __iomem *mstate; /* VF Migration State Array */ }; +/* Address Translation Service */ +struct pci_ats { + int pos; /* capability position */ + int stu; /* Smallest Translation Unit */ + int qdep; /* Invalidate Queue Depth */ + int ref_cnt; /* Physical Function reference count */ + int is_enabled:1; /* Enable bit is set */ +}; + #ifdef CONFIG_PCI_IOV extern int pci_iov_init(struct pci_dev *dev); extern void pci_iov_release(struct pci_dev *dev); @@ -236,6 +245,20 @@ extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); extern void pci_restore_iov_state(struct pci_dev *dev); extern int pci_iov_bus_range(struct pci_bus *bus); + +extern int pci_enable_ats(struct pci_dev *dev, int ps); +extern void pci_disable_ats(struct pci_dev *dev); +extern int pci_ats_queue_depth(struct pci_dev *dev); +/** + * pci_ats_enabled - query the ATS status + * @dev: the PCI device + * + * Returns 1 if ATS capability is enabled, or 0 if not. + */ +static inline int pci_ats_enabled(struct pci_dev *dev) +{ + return dev->ats && dev->ats->is_enabled; +} #else static inline int pci_iov_init(struct pci_dev *dev) { @@ -257,6 +280,22 @@ static inline int pci_iov_bus_range(struct pci_bus *bus) { return 0; } + +static inline int pci_enable_ats(struct pci_dev *dev, int ps) +{ + return -ENODEV; +} +static inline void pci_disable_ats(struct pci_dev *dev) +{ +} +static inline int pci_ats_queue_depth(struct pci_dev *dev) +{ + return -ENODEV; +} +static inline int pci_ats_enabled(struct pci_dev *dev) +{ + return 0; +} #endif /* CONFIG_PCI_IOV */ #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig index c3bde588aa1..50e94e02378 100644 --- a/drivers/pci/pcie/aer/Kconfig +++ b/drivers/pci/pcie/aer/Kconfig @@ -10,3 +10,18 @@ config PCIEAER This enables PCI Express Root Port Advanced Error Reporting (AER) driver support. Error reporting messages sent to Root Port will be handled by PCI Express AER driver. + + +# +# PCI Express ECRC +# +config PCIE_ECRC + bool "PCI Express ECRC settings control" + depends on PCIEAER + help + Used to override firmware/bios settings for PCI Express ECRC + (transaction layer end-to-end CRC checking). + + When in doubt, say N. + +source "drivers/pci/pcie/aer/Kconfig.debug" diff --git a/drivers/pci/pcie/aer/Kconfig.debug b/drivers/pci/pcie/aer/Kconfig.debug new file mode 100644 index 00000000000..b8c925c1f6a --- /dev/null +++ b/drivers/pci/pcie/aer/Kconfig.debug @@ -0,0 +1,18 @@ +# +# PCI Express Root Port Device AER Debug Configuration +# + +config PCIEAER_INJECT + tristate "PCIE AER error injector support" + depends on PCIEAER + default n + help + This enables PCI Express Root Port Advanced Error Reporting + (AER) software error injector. + + Debuging PCIE AER code is quite difficult because it is hard + to trigger various real hardware errors. Software based + error injection can fake almost all kinds of errors with the + help of a user space helper tool aer-inject, which can be + gotten from: + http://www.kernel.org/pub/linux/utils/pci/aer-inject/ diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile index 8da3bd8455a..2cba67510dc 100644 --- a/drivers/pci/pcie/aer/Makefile +++ b/drivers/pci/pcie/aer/Makefile @@ -4,6 +4,9 @@ obj-$(CONFIG_PCIEAER) += aerdriver.o +obj-$(CONFIG_PCIE_ECRC) += ecrc.o + aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o aerdriver-$(CONFIG_ACPI) += aerdrv_acpi.o +obj-$(CONFIG_PCIEAER_INJECT) += aer_inject.o diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c new file mode 100644 index 00000000000..d92ae21a59d --- /dev/null +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -0,0 +1,473 @@ +/* + * PCIE AER software error injection support. + * + * Debuging PCIE AER code is quite difficult because it is hard to + * trigger various real hardware errors. Software based error + * injection can fake almost all kinds of errors with the help of a + * user space helper tool aer-inject, which can be gotten from: + * http://www.kernel.org/pub/linux/utils/pci/aer-inject/ + * + * Copyright 2009 Intel Corporation. + * Huang Ying <ying.huang@intel.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/miscdevice.h> +#include <linux/pci.h> +#include <linux/fs.h> +#include <asm/uaccess.h> +#include "aerdrv.h" + +struct aer_error_inj +{ + u8 bus; + u8 dev; + u8 fn; + u32 uncor_status; + u32 cor_status; + u32 header_log0; + u32 header_log1; + u32 header_log2; + u32 header_log3; +}; + +struct aer_error +{ + struct list_head list; + unsigned int bus; + unsigned int devfn; + int pos_cap_err; + + u32 uncor_status; + u32 cor_status; + u32 header_log0; + u32 header_log1; + u32 header_log2; + u32 header_log3; + u32 root_status; + u32 source_id; +}; + +struct pci_bus_ops +{ + struct list_head list; + struct pci_bus *bus; + struct pci_ops *ops; +}; + +static LIST_HEAD(einjected); + +static LIST_HEAD(pci_bus_ops_list); + +/* Protect einjected and pci_bus_ops_list */ +static DEFINE_SPINLOCK(inject_lock); + +static void aer_error_init(struct aer_error *err, unsigned int bus, + unsigned int devfn, int pos_cap_err) +{ + INIT_LIST_HEAD(&err->list); + err->bus = bus; + err->devfn = devfn; + err->pos_cap_err = pos_cap_err; +} + +/* inject_lock must be held before calling */ +static struct aer_error *__find_aer_error(unsigned int bus, unsigned int devfn) +{ + struct aer_error *err; + + list_for_each_entry(err, &einjected, list) { + if (bus == err->bus && devfn == err->devfn) + return err; + } + return NULL; +} + +/* inject_lock must be held before calling */ +static struct aer_error *__find_aer_error_by_dev(struct pci_dev *dev) +{ + return __find_aer_error(dev->bus->number, dev->devfn); +} + +/* inject_lock must be held before calling */ +static struct pci_ops *__find_pci_bus_ops(struct pci_bus *bus) +{ + struct pci_bus_ops *bus_ops; + + list_for_each_entry(bus_ops, &pci_bus_ops_list, list) { + if (bus_ops->bus == bus) + return bus_ops->ops; + } + return NULL; +} + +static struct pci_bus_ops *pci_bus_ops_pop(void) +{ + unsigned long flags; + struct pci_bus_ops *bus_ops = NULL; + + spin_lock_irqsave(&inject_lock, flags); + if (list_empty(&pci_bus_ops_list)) + bus_ops = NULL; + else { + struct list_head *lh = pci_bus_ops_list.next; + list_del(lh); + bus_ops = list_entry(lh, struct pci_bus_ops, list); + } + spin_unlock_irqrestore(&inject_lock, flags); + return bus_ops; +} + +static u32 *find_pci_config_dword(struct aer_error *err, int where, + int *prw1cs) +{ + int rw1cs = 0; + u32 *target = NULL; + + if (err->pos_cap_err == -1) + return NULL; + + switch (where - err->pos_cap_err) { + case PCI_ERR_UNCOR_STATUS: + target = &err->uncor_status; + rw1cs = 1; + break; + case PCI_ERR_COR_STATUS: + target = &err->cor_status; + rw1cs = 1; + break; + case PCI_ERR_HEADER_LOG: + target = &err->header_log0; + break; + case PCI_ERR_HEADER_LOG+4: + target = &err->header_log1; + break; + case PCI_ERR_HEADER_LOG+8: + target = &err->header_log2; + break; + case PCI_ERR_HEADER_LOG+12: + target = &err->header_log3; + break; + case PCI_ERR_ROOT_STATUS: + target = &err->root_status; + rw1cs = 1; + break; + case PCI_ERR_ROOT_COR_SRC: + target = &err->source_id; + break; + } + if (prw1cs) + *prw1cs = rw1cs; + return target; +} + +static int pci_read_aer(struct pci_bus *bus, unsigned int devfn, int where, + int size, u32 *val) +{ + u32 *sim; + struct aer_error *err; + unsigned long flags; + struct pci_ops *ops; + + spin_lock_irqsave(&inject_lock, flags); + if (size != sizeof(u32)) + goto out; + err = __find_aer_error(bus->number, devfn); + if (!err) + goto out; + + sim = find_pci_config_dword(err, where, NULL); + if (sim) { + *val = *sim; + spin_unlock_irqrestore(&inject_lock, flags); + return 0; + } +out: + ops = __find_pci_bus_ops(bus); + spin_unlock_irqrestore(&inject_lock, flags); + return ops->read(bus, devfn, where, size, val); +} + +int pci_write_aer(struct pci_bus *bus, unsigned int devfn, int where, int size, + u32 val) +{ + u32 *sim; + struct aer_error *err; + unsigned long flags; + int rw1cs; + struct pci_ops *ops; + + spin_lock_irqsave(&inject_lock, flags); + if (size != sizeof(u32)) + goto out; + err = __find_aer_error(bus->number, devfn); + if (!err) + goto out; + + sim = find_pci_config_dword(err, where, &rw1cs); + if (sim) { + if (rw1cs) + *sim ^= val; + else + *sim = val; + spin_unlock_irqrestore(&inject_lock, flags); + return 0; + } +out: + ops = __find_pci_bus_ops(bus); + spin_unlock_irqrestore(&inject_lock, flags); + return ops->write(bus, devfn, where, size, val); +} + +static struct pci_ops pci_ops_aer = { + .read = pci_read_aer, + .write = pci_write_aer, +}; + +static void pci_bus_ops_init(struct pci_bus_ops *bus_ops, + struct pci_bus *bus, + struct pci_ops *ops) +{ + INIT_LIST_HEAD(&bus_ops->list); + bus_ops->bus = bus; + bus_ops->ops = ops; +} + +static int pci_bus_set_aer_ops(struct pci_bus *bus) +{ + struct pci_ops *ops; + struct pci_bus_ops *bus_ops; + unsigned long flags; + + bus_ops = kmalloc(sizeof(*bus_ops), GFP_KERNEL); + if (!bus_ops) + return -ENOMEM; + ops = pci_bus_set_ops(bus, &pci_ops_aer); + spin_lock_irqsave(&inject_lock, flags); + if (ops == &pci_ops_aer) + goto out; + pci_bus_ops_init(bus_ops, bus, ops); + list_add(&bus_ops->list, &pci_bus_ops_list); + bus_ops = NULL; +out: + spin_unlock_irqrestore(&inject_lock, flags); + if (bus_ops) + kfree(bus_ops); + return 0; +} + +static struct pci_dev *pcie_find_root_port(struct pci_dev *dev) +{ + while (1) { + if (!dev->is_pcie) + break; + if (dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) + return dev; + if (!dev->bus->self) + break; + dev = dev->bus->self; + } + return NULL; +} + +static int find_aer_device_iter(struct device *device, void *data) +{ + struct pcie_device **result = data; + struct pcie_device *pcie_dev; + + if (device->bus == &pcie_port_bus_type) { + pcie_dev = to_pcie_device(device); + if (pcie_dev->service & PCIE_PORT_SERVICE_AER) { + *result = pcie_dev; + return 1; + } + } + return 0; +} + +static int find_aer_device(struct pci_dev *dev, struct pcie_device **result) +{ + return device_for_each_child(&dev->dev, result, find_aer_device_iter); +} + +static int aer_inject(struct aer_error_inj *einj) +{ + struct aer_error *err, *rperr; + struct aer_error *err_alloc = NULL, *rperr_alloc = NULL; + struct pci_dev *dev, *rpdev; + struct pcie_device *edev; + unsigned long flags; + unsigned int devfn = PCI_DEVFN(einj->dev, einj->fn); + int pos_cap_err, rp_pos_cap_err; + u32 sever; + int ret = 0; + + dev = pci_get_bus_and_slot(einj->bus, devfn); + if (!dev) + return -EINVAL; + rpdev = pcie_find_root_port(dev); + if (!rpdev) { + ret = -EINVAL; + goto out_put; + } + + pos_cap_err = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!pos_cap_err) { + ret = -EIO; + goto out_put; + } + pci_read_config_dword(dev, pos_cap_err + PCI_ERR_UNCOR_SEVER, &sever); + + rp_pos_cap_err = pci_find_ext_capability(rpdev, PCI_EXT_CAP_ID_ERR); + if (!rp_pos_cap_err) { + ret = -EIO; + goto out_put; + } + + err_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL); + if (!err_alloc) { + ret = -ENOMEM; + goto out_put; + } + rperr_alloc = kzalloc(sizeof(struct aer_error), GFP_KERNEL); + if (!rperr_alloc) { + ret = -ENOMEM; + goto out_put; + } + + spin_lock_irqsave(&inject_lock, flags); + + err = __find_aer_error_by_dev(dev); + if (!err) { + err = err_alloc; + err_alloc = NULL; + aer_error_init(err, einj->bus, devfn, pos_cap_err); + list_add(&err->list, &einjected); + } + err->uncor_status |= einj->uncor_status; + err->cor_status |= einj->cor_status; + err->header_log0 = einj->header_log0; + err->header_log1 = einj->header_log1; + err->header_log2 = einj->header_log2; + err->header_log3 = einj->header_log3; + + rperr = __find_aer_error_by_dev(rpdev); + if (!rperr) { + rperr = rperr_alloc; + rperr_alloc = NULL; + aer_error_init(rperr, rpdev->bus->number, rpdev->devfn, + rp_pos_cap_err); + list_add(&rperr->list, &einjected); + } + if (einj->cor_status) { + if (rperr->root_status & PCI_ERR_ROOT_COR_RCV) + rperr->root_status |= PCI_ERR_ROOT_MULTI_COR_RCV; + else + rperr->root_status |= PCI_ERR_ROOT_COR_RCV; + rperr->source_id &= 0xffff0000; + rperr->source_id |= (einj->bus << 8) | devfn; + } + if (einj->uncor_status) { + if (rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV) + rperr->root_status |= PCI_ERR_ROOT_MULTI_UNCOR_RCV; + if (sever & einj->uncor_status) { + rperr->root_status |= PCI_ERR_ROOT_FATAL_RCV; + if (!(rperr->root_status & PCI_ERR_ROOT_UNCOR_RCV)) + rperr->root_status |= PCI_ERR_ROOT_FIRST_FATAL; + } else + rperr->root_status |= PCI_ERR_ROOT_NONFATAL_RCV; + rperr->root_status |= PCI_ERR_ROOT_UNCOR_RCV; + rperr->source_id &= 0x0000ffff; + rperr->source_id |= ((einj->bus << 8) | devfn) << 16; + } + spin_unlock_irqrestore(&inject_lock, flags); + + ret = pci_bus_set_aer_ops(dev->bus); + if (ret) + goto out_put; + ret = pci_bus_set_aer_ops(rpdev->bus); + if (ret) + goto out_put; + + if (find_aer_device(rpdev, &edev)) + aer_irq(-1, edev); + else + ret = -EINVAL; +out_put: + if (err_alloc) + kfree(err_alloc); + if (rperr_alloc) + kfree(rperr_alloc); + pci_dev_put(dev); + return ret; +} + +static ssize_t aer_inject_write(struct file *filp, const char __user *ubuf, + size_t usize, loff_t *off) +{ + struct aer_error_inj einj; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (usize != sizeof(struct aer_error_inj)) + return -EINVAL; + + if (copy_from_user(&einj, ubuf, usize)) + return -EFAULT; + + ret = aer_inject(&einj); + return ret ? ret : usize; +} + +static const struct file_operations aer_inject_fops = { + .write = aer_inject_write, + .owner = THIS_MODULE, +}; + +static struct miscdevice aer_inject_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "aer_inject", + .fops = &aer_inject_fops, +}; + +static int __init aer_inject_init(void) +{ + return misc_register(&aer_inject_device); +} + +static void __exit aer_inject_exit(void) +{ + struct aer_error *err, *err_next; + unsigned long flags; + struct pci_bus_ops *bus_ops; + + misc_deregister(&aer_inject_device); + + while ((bus_ops = pci_bus_ops_pop())) { + pci_bus_set_ops(bus_ops->bus, bus_ops->ops); + kfree(bus_ops); + } + + spin_lock_irqsave(&inject_lock, flags); + list_for_each_entry_safe(err, err_next, + &pci_bus_ops_list, list) { + list_del(&err->list); + kfree(err); + } + spin_unlock_irqrestore(&inject_lock, flags); +} + +module_init(aer_inject_init); +module_exit(aer_inject_exit); + +MODULE_DESCRIPTION("PCIE AER software error injector"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 32ade5af927..4770f13b3ca 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -77,7 +77,7 @@ void pci_no_aer(void) * * Invoked when Root Port detects AER messages. **/ -static irqreturn_t aer_irq(int irq, void *context) +irqreturn_t aer_irq(int irq, void *context) { unsigned int status, id; struct pcie_device *pdev = (struct pcie_device *)context; @@ -126,6 +126,7 @@ static irqreturn_t aer_irq(int irq, void *context) return IRQ_HANDLED; } +EXPORT_SYMBOL_GPL(aer_irq); /** * aer_alloc_rpc - allocate Root Port data structure diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index aa14482a477..bbd7428ca2d 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -11,6 +11,7 @@ #include <linux/workqueue.h> #include <linux/pcieport_if.h> #include <linux/aer.h> +#include <linux/interrupt.h> #define AER_NONFATAL 0 #define AER_FATAL 1 @@ -56,7 +57,11 @@ struct header_log_regs { unsigned int dw3; }; +#define AER_MAX_MULTI_ERR_DEVICES 5 /* Not likely to have more */ struct aer_err_info { + struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; + int error_dev_num; + u16 id; int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */ int flags; unsigned int status; /* COR/UNCOR Error Status */ @@ -120,6 +125,7 @@ extern void aer_delete_rootport(struct aer_rpc *rpc); extern int aer_init(struct pcie_device *dev); extern void aer_isr(struct work_struct *work); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); +extern irqreturn_t aer_irq(int irq, void *context); #ifdef CONFIG_ACPI extern int aer_osc_setup(struct pcie_device *pciedev); diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 307452f3003..3d8872704a5 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -26,7 +26,9 @@ #include "aerdrv.h" static int forceload; +static int nosourceid; module_param(forceload, bool, 0); +module_param(nosourceid, bool, 0); int pci_enable_pcie_error_reporting(struct pci_dev *dev) { @@ -109,19 +111,23 @@ int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) #endif /* 0 */ -static void set_device_error_reporting(struct pci_dev *dev, void *data) +static int set_device_error_reporting(struct pci_dev *dev, void *data) { bool enable = *((bool *)data); - if (dev->pcie_type != PCIE_RC_PORT && - dev->pcie_type != PCIE_SW_UPSTREAM_PORT && - dev->pcie_type != PCIE_SW_DOWNSTREAM_PORT) - return; + if (dev->pcie_type == PCIE_RC_PORT || + dev->pcie_type == PCIE_SW_UPSTREAM_PORT || + dev->pcie_type == PCIE_SW_DOWNSTREAM_PORT) { + if (enable) + pci_enable_pcie_error_reporting(dev); + else + pci_disable_pcie_error_reporting(dev); + } if (enable) - pci_enable_pcie_error_reporting(dev); - else - pci_disable_pcie_error_reporting(dev); + pcie_set_ecrc_checking(dev); + + return 0; } /** @@ -139,73 +145,148 @@ static void set_downstream_devices_error_reporting(struct pci_dev *dev, pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable); } -static int find_device_iter(struct device *device, void *data) +static inline int compare_device_id(struct pci_dev *dev, + struct aer_err_info *e_info) { - struct pci_dev *dev; - u16 id = *(unsigned long *)data; - u8 secondary, subordinate, d_bus = id >> 8; + if (e_info->id == ((dev->bus->number << 8) | dev->devfn)) { + /* + * Device ID match + */ + return 1; + } - if (device->bus == &pci_bus_type) { - dev = to_pci_dev(device); - if (id == ((dev->bus->number << 8) | dev->devfn)) { - /* - * Device ID match - */ - *(unsigned long*)data = (unsigned long)device; - return 1; - } + return 0; +} + +static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) +{ + if (e_info->error_dev_num < AER_MAX_MULTI_ERR_DEVICES) { + e_info->dev[e_info->error_dev_num] = dev; + e_info->error_dev_num++; + return 1; + } else + return 0; +} + + +#define PCI_BUS(x) (((x) >> 8) & 0xff) + +static int find_device_iter(struct pci_dev *dev, void *data) +{ + int pos; + u32 status; + u32 mask; + u16 reg16; + int result; + struct aer_err_info *e_info = (struct aer_err_info *)data; + + /* + * When bus id is equal to 0, it might be a bad id + * reported by root port. + */ + if (!nosourceid && (PCI_BUS(e_info->id) != 0)) { + result = compare_device_id(dev, e_info); + if (result) + add_error_device(e_info, dev); /* - * If device is P2P, check if it is an upstream? + * If there is no multiple error, we stop + * or continue based on the id comparing. */ - if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) { - pci_read_config_byte(dev, PCI_SECONDARY_BUS, - &secondary); - pci_read_config_byte(dev, PCI_SUBORDINATE_BUS, - &subordinate); - if (d_bus >= secondary && d_bus <= subordinate) { - *(unsigned long*)data = (unsigned long)device; - return 1; - } + if (!(e_info->flags & AER_MULTI_ERROR_VALID_FLAG)) + return result; + + /* + * If there are multiple errors and id does match, + * We need continue to search other devices under + * the root port. Return 0 means that. + */ + if (result) + return 0; + } + + /* + * When either + * 1) nosourceid==y; + * 2) bus id is equal to 0. Some ports might lose the bus + * id of error source id; + * 3) There are multiple errors and prior id comparing fails; + * We check AER status registers to find the initial reporter. + */ + if (atomic_read(&dev->enable_cnt) == 0) + return 0; + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pos) + return 0; + /* Check if AER is enabled */ + pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, ®16); + if (!(reg16 & ( + PCI_EXP_DEVCTL_CERE | + PCI_EXP_DEVCTL_NFERE | + PCI_EXP_DEVCTL_FERE | + PCI_EXP_DEVCTL_URRE))) + return 0; + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!pos) + return 0; + + status = 0; + mask = 0; + if (e_info->severity == AER_CORRECTABLE) { + pci_read_config_dword(dev, + pos + PCI_ERR_COR_STATUS, + &status); + pci_read_config_dword(dev, + pos + PCI_ERR_COR_MASK, + &mask); + if (status & ERR_CORRECTABLE_ERROR_MASK & ~mask) { + add_error_device(e_info, dev); + goto added; + } + } else { + pci_read_config_dword(dev, + pos + PCI_ERR_UNCOR_STATUS, + &status); + pci_read_config_dword(dev, + pos + PCI_ERR_UNCOR_MASK, + &mask); + if (status & ERR_UNCORRECTABLE_ERROR_MASK & ~mask) { + add_error_device(e_info, dev); + goto added; } } return 0; + +added: + if (e_info->flags & AER_MULTI_ERROR_VALID_FLAG) + return 0; + else + return 1; } /** * find_source_device - search through device hierarchy for source device * @parent: pointer to Root Port pci_dev data structure - * @id: device ID of agent who sends an error message to this Root Port + * @err_info: including detailed error information such like id * * Invoked when error is detected at the Root Port. */ -static struct device* find_source_device(struct pci_dev *parent, u16 id) +static void find_source_device(struct pci_dev *parent, + struct aer_err_info *e_info) { struct pci_dev *dev = parent; - struct device *device; - unsigned long device_addr; - int status; + int result; /* Is Root Port an agent that sends error message? */ - if (id == ((dev->bus->number << 8) | dev->devfn)) - return &dev->dev; - - do { - device_addr = id; - if ((status = device_for_each_child(&dev->dev, - &device_addr, find_device_iter))) { - device = (struct device*)device_addr; - dev = to_pci_dev(device); - if (id == ((dev->bus->number << 8) | dev->devfn)) - return device; - } - }while (status); + result = find_device_iter(dev, e_info); + if (result) + return; - return NULL; + pci_walk_bus(parent->subordinate, find_device_iter, e_info); } -static void report_error_detected(struct pci_dev *dev, void *data) +static int report_error_detected(struct pci_dev *dev, void *data) { pci_ers_result_t vote; struct pci_error_handlers *err_handler; @@ -230,16 +311,16 @@ static void report_error_detected(struct pci_dev *dev, void *data) dev->driver ? "no AER-aware driver" : "no driver"); } - return; + return 0; } err_handler = dev->driver->err_handler; vote = err_handler->error_detected(dev, result_data->state); result_data->result = merge_result(result_data->result, vote); - return; + return 0; } -static void report_mmio_enabled(struct pci_dev *dev, void *data) +static int report_mmio_enabled(struct pci_dev *dev, void *data) { pci_ers_result_t vote; struct pci_error_handlers *err_handler; @@ -249,15 +330,15 @@ static void report_mmio_enabled(struct pci_dev *dev, void *data) if (!dev->driver || !dev->driver->err_handler || !dev->driver->err_handler->mmio_enabled) - return; + return 0; err_handler = dev->driver->err_handler; vote = err_handler->mmio_enabled(dev); result_data->result = merge_result(result_data->result, vote); - return; + return 0; } -static void report_slot_reset(struct pci_dev *dev, void *data) +static int report_slot_reset(struct pci_dev *dev, void *data) { pci_ers_result_t vote; struct pci_error_handlers *err_handler; @@ -267,15 +348,15 @@ static void report_slot_reset(struct pci_dev *dev, void *data) if (!dev->driver || !dev->driver->err_handler || !dev->driver->err_handler->slot_reset) - return; + return 0; err_handler = dev->driver->err_handler; vote = err_handler->slot_reset(dev); result_data->result = merge_result(result_data->result, vote); - return; + return 0; } -static void report_resume(struct pci_dev *dev, void *data) +static int report_resume(struct pci_dev *dev, void *data) { struct pci_error_handlers *err_handler; @@ -284,11 +365,11 @@ static void report_resume(struct pci_dev *dev, void *data) if (!dev->driver || !dev->driver->err_handler || !dev->driver->err_handler->resume) - return; + return 0; err_handler = dev->driver->err_handler; err_handler->resume(dev); - return; + return 0; } /** @@ -305,7 +386,7 @@ static void report_resume(struct pci_dev *dev, void *data) static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, enum pci_channel_state state, char *error_mesg, - void (*cb)(struct pci_dev *, void *)) + int (*cb)(struct pci_dev *, void *)) { struct aer_broadcast_data result_data; @@ -497,12 +578,12 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev, */ static void handle_error_source(struct pcie_device * aerdev, struct pci_dev *dev, - struct aer_err_info info) + struct aer_err_info *info) { pci_ers_result_t status = 0; int pos; - if (info.severity == AER_CORRECTABLE) { + if (info->severity == AER_CORRECTABLE) { /* * Correctable error does not need software intevention. * No need to go through error recovery process. @@ -510,9 +591,9 @@ static void handle_error_source(struct pcie_device * aerdev, pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); if (pos) pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, - info.status); + info->status); } else { - status = do_recovery(aerdev, dev, info.severity); + status = do_recovery(aerdev, dev, info->severity); if (status == PCI_ERS_RESULT_RECOVERED) { dev_printk(KERN_DEBUG, &dev->dev, "AER driver " "successfully recovered\n"); @@ -661,6 +742,28 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) return AER_SUCCESS; } +static inline void aer_process_err_devices(struct pcie_device *p_device, + struct aer_err_info *e_info) +{ + int i; + + if (!e_info->dev[0]) { + dev_printk(KERN_DEBUG, &p_device->port->dev, + "can't find device of ID%04x\n", + e_info->id); + } + + for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { + if (get_device_error_info(e_info->dev[i], e_info) == + AER_SUCCESS) { + aer_print_error(e_info->dev[i], e_info); + handle_error_source(p_device, + e_info->dev[i], + e_info); + } + } +} + /** * aer_isr_one_error - consume an error detected by root port * @p_device: pointer to error root port service device @@ -669,10 +772,16 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) static void aer_isr_one_error(struct pcie_device *p_device, struct aer_err_source *e_src) { - struct device *s_device; - struct aer_err_info e_info = {0, 0, 0,}; + struct aer_err_info *e_info; int i; - u16 id; + + /* struct aer_err_info might be big, so we allocate it with slab */ + e_info = kmalloc(sizeof(struct aer_err_info), GFP_KERNEL); + if (e_info == NULL) { + dev_printk(KERN_DEBUG, &p_device->port->dev, + "Can't allocate mem when processing AER errors\n"); + return; + } /* * There is a possibility that both correctable error and @@ -684,31 +793,26 @@ static void aer_isr_one_error(struct pcie_device *p_device, if (!(e_src->status & i)) continue; + memset(e_info, 0, sizeof(struct aer_err_info)); + /* Init comprehensive error information */ if (i & PCI_ERR_ROOT_COR_RCV) { - id = ERR_COR_ID(e_src->id); - e_info.severity = AER_CORRECTABLE; + e_info->id = ERR_COR_ID(e_src->id); + e_info->severity = AER_CORRECTABLE; } else { - id = ERR_UNCOR_ID(e_src->id); - e_info.severity = ((e_src->status >> 6) & 1); + e_info->id = ERR_UNCOR_ID(e_src->id); + e_info->severity = ((e_src->status >> 6) & 1); } if (e_src->status & (PCI_ERR_ROOT_MULTI_COR_RCV | PCI_ERR_ROOT_MULTI_UNCOR_RCV)) - e_info.flags |= AER_MULTI_ERROR_VALID_FLAG; - if (!(s_device = find_source_device(p_device->port, id))) { - printk(KERN_DEBUG "%s->can't find device of ID%04x\n", - __func__, id); - continue; - } - if (get_device_error_info(to_pci_dev(s_device), &e_info) == - AER_SUCCESS) { - aer_print_error(to_pci_dev(s_device), &e_info); - handle_error_source(p_device, - to_pci_dev(s_device), - e_info); - } + e_info->flags |= AER_MULTI_ERROR_VALID_FLAG; + + find_source_device(p_device->port, e_info); + aer_process_err_devices(p_device, e_info); } + + kfree(e_info); } /** diff --git a/drivers/pci/pcie/aer/ecrc.c b/drivers/pci/pcie/aer/ecrc.c new file mode 100644 index 00000000000..ece97df4df6 --- /dev/null +++ b/drivers/pci/pcie/aer/ecrc.c @@ -0,0 +1,131 @@ +/* + * Enables/disables PCIe ECRC checking. + * + * (C) Copyright 2009 Hewlett-Packard Development Company, L.P. + * Andrew Patterson <andrew.patterson@hp.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + * 02111-1307, USA. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/pci.h> +#include <linux/pci_regs.h> +#include <linux/errno.h> +#include "../../pci.h" + +#define ECRC_POLICY_DEFAULT 0 /* ECRC set by BIOS */ +#define ECRC_POLICY_OFF 1 /* ECRC off for performance */ +#define ECRC_POLICY_ON 2 /* ECRC on for data integrity */ + +static int ecrc_policy = ECRC_POLICY_DEFAULT; + +static const char *ecrc_policy_str[] = { + [ECRC_POLICY_DEFAULT] = "bios", + [ECRC_POLICY_OFF] = "off", + [ECRC_POLICY_ON] = "on" +}; + +/** + * enable_ercr_checking - enable PCIe ECRC checking for a device + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +static int enable_ecrc_checking(struct pci_dev *dev) +{ + int pos; + u32 reg32; + + if (!dev->is_pcie) + return -ENODEV; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!pos) + return -ENODEV; + + pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); + if (reg32 & PCI_ERR_CAP_ECRC_GENC) + reg32 |= PCI_ERR_CAP_ECRC_GENE; + if (reg32 & PCI_ERR_CAP_ECRC_CHKC) + reg32 |= PCI_ERR_CAP_ECRC_CHKE; + pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); + + return 0; +} + +/** + * disable_ercr_checking - disables PCIe ECRC checking for a device + * @dev: the PCI device + * + * Returns 0 on success, or negative on failure. + */ +static int disable_ecrc_checking(struct pci_dev *dev) +{ + int pos; + u32 reg32; + + if (!dev->is_pcie) + return -ENODEV; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!pos) + return -ENODEV; + + pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); + reg32 &= ~(PCI_ERR_CAP_ECRC_GENE | PCI_ERR_CAP_ECRC_CHKE); + pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); + + return 0; +} + +/** + * pcie_set_ecrc_checking - set/unset PCIe ECRC checking for a device based on global policy + * @dev: the PCI device + */ +void pcie_set_ecrc_checking(struct pci_dev *dev) +{ + switch (ecrc_policy) { + case ECRC_POLICY_DEFAULT: + return; + case ECRC_POLICY_OFF: + disable_ecrc_checking(dev); + break; + case ECRC_POLICY_ON: + enable_ecrc_checking(dev);; + break; + default: + return; + } +} + +/** + * pcie_ecrc_get_policy - parse kernel command-line ecrc option + */ +void pcie_ecrc_get_policy(char *str) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ecrc_policy_str); i++) + if (!strncmp(str, ecrc_policy_str[i], + strlen(ecrc_policy_str[i]))) + break; + if (i >= ARRAY_SIZE(ecrc_policy_str)) + return; + + ecrc_policy = i; +} diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index b0367f168af..3d27c97e048 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -26,40 +26,36 @@ #endif #define MODULE_PARAM_PREFIX "pcie_aspm." -struct endpoint_state { - unsigned int l0s_acceptable_latency; - unsigned int l1_acceptable_latency; +struct aspm_latency { + u32 l0s; /* L0s latency (nsec) */ + u32 l1; /* L1 latency (nsec) */ }; struct pcie_link_state { - struct list_head sibiling; - struct pci_dev *pdev; - bool downstream_has_switch; - - struct pcie_link_state *parent; - struct list_head children; - struct list_head link; + struct pci_dev *pdev; /* Upstream component of the Link */ + struct pcie_link_state *root; /* pointer to the root port link */ + struct pcie_link_state *parent; /* pointer to the parent Link state */ + struct list_head sibling; /* node in link_list */ + struct list_head children; /* list of child link states */ + struct list_head link; /* node in parent's children list */ /* ASPM state */ - unsigned int support_state; - unsigned int enabled_state; - unsigned int bios_aspm_state; - /* upstream component */ - unsigned int l0s_upper_latency; - unsigned int l1_upper_latency; - /* downstream component */ - unsigned int l0s_down_latency; - unsigned int l1_down_latency; - /* Clock PM state*/ - unsigned int clk_pm_capable; - unsigned int clk_pm_enabled; - unsigned int bios_clk_state; + u32 aspm_support:2; /* Supported ASPM state */ + u32 aspm_enabled:2; /* Enabled ASPM state */ + u32 aspm_default:2; /* Default ASPM state by BIOS */ + + /* Clock PM state */ + u32 clkpm_capable:1; /* Clock PM capable? */ + u32 clkpm_enabled:1; /* Current Clock PM state */ + u32 clkpm_default:1; /* Default Clock PM state by BIOS */ + /* Latencies */ + struct aspm_latency latency; /* Exit latency */ /* - * A pcie downstream port only has one slot under it, so at most there - * are 8 functions + * Endpoint acceptable latencies. A pcie downstream port only + * has one slot under it, so at most there are 8 functions. */ - struct endpoint_state endpoints[8]; + struct aspm_latency acceptable[8]; }; static int aspm_disabled, aspm_force; @@ -78,27 +74,23 @@ static const char *policy_str[] = { #define LINK_RETRAIN_TIMEOUT HZ -static int policy_to_aspm_state(struct pci_dev *pdev) +static int policy_to_aspm_state(struct pcie_link_state *link) { - struct pcie_link_state *link_state = pdev->link_state; - switch (aspm_policy) { case POLICY_PERFORMANCE: /* Disable ASPM and Clock PM */ return 0; case POLICY_POWERSAVE: /* Enable ASPM L0s/L1 */ - return PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; + return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; case POLICY_DEFAULT: - return link_state->bios_aspm_state; + return link->aspm_default; } return 0; } -static int policy_to_clkpm_state(struct pci_dev *pdev) +static int policy_to_clkpm_state(struct pcie_link_state *link) { - struct pcie_link_state *link_state = pdev->link_state; - switch (aspm_policy) { case POLICY_PERFORMANCE: /* Disable ASPM and Clock PM */ @@ -107,73 +99,78 @@ static int policy_to_clkpm_state(struct pci_dev *pdev) /* Disable Clock PM */ return 1; case POLICY_DEFAULT: - return link_state->bios_clk_state; + return link->clkpm_default; } return 0; } -static void pcie_set_clock_pm(struct pci_dev *pdev, int enable) +static void pcie_set_clkpm_nocheck(struct pcie_link_state *link, int enable) { - struct pci_dev *child_dev; int pos; u16 reg16; - struct pcie_link_state *link_state = pdev->link_state; + struct pci_dev *child; + struct pci_bus *linkbus = link->pdev->subordinate; - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { - pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); + list_for_each_entry(child, &linkbus->devices, bus_list) { + pos = pci_find_capability(child, PCI_CAP_ID_EXP); if (!pos) return; - pci_read_config_word(child_dev, pos + PCI_EXP_LNKCTL, ®16); + pci_read_config_word(child, pos + PCI_EXP_LNKCTL, ®16); if (enable) reg16 |= PCI_EXP_LNKCTL_CLKREQ_EN; else reg16 &= ~PCI_EXP_LNKCTL_CLKREQ_EN; - pci_write_config_word(child_dev, pos + PCI_EXP_LNKCTL, reg16); + pci_write_config_word(child, pos + PCI_EXP_LNKCTL, reg16); } - link_state->clk_pm_enabled = !!enable; + link->clkpm_enabled = !!enable; } -static void pcie_check_clock_pm(struct pci_dev *pdev, int blacklist) +static void pcie_set_clkpm(struct pcie_link_state *link, int enable) { - int pos; + /* Don't enable Clock PM if the link is not Clock PM capable */ + if (!link->clkpm_capable && enable) + return; + /* Need nothing if the specified equals to current state */ + if (link->clkpm_enabled == enable) + return; + pcie_set_clkpm_nocheck(link, enable); +} + +static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) +{ + int pos, capable = 1, enabled = 1; u32 reg32; u16 reg16; - int capable = 1, enabled = 1; - struct pci_dev *child_dev; - struct pcie_link_state *link_state = pdev->link_state; + struct pci_dev *child; + struct pci_bus *linkbus = link->pdev->subordinate; /* All functions should have the same cap and state, take the worst */ - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { - pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); + list_for_each_entry(child, &linkbus->devices, bus_list) { + pos = pci_find_capability(child, PCI_CAP_ID_EXP); if (!pos) return; - pci_read_config_dword(child_dev, pos + PCI_EXP_LNKCAP, ®32); + pci_read_config_dword(child, pos + PCI_EXP_LNKCAP, ®32); if (!(reg32 & PCI_EXP_LNKCAP_CLKPM)) { capable = 0; enabled = 0; break; } - pci_read_config_word(child_dev, pos + PCI_EXP_LNKCTL, ®16); + pci_read_config_word(child, pos + PCI_EXP_LNKCTL, ®16); if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN)) enabled = 0; } - link_state->clk_pm_enabled = enabled; - link_state->bios_clk_state = enabled; - if (!blacklist) { - link_state->clk_pm_capable = capable; - pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); - } else { - link_state->clk_pm_capable = 0; - pcie_set_clock_pm(pdev, 0); - } + link->clkpm_enabled = enabled; + link->clkpm_default = enabled; + link->clkpm_capable = (blacklist) ? 0 : capable; } -static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev) +static bool pcie_aspm_downstream_has_switch(struct pcie_link_state *link) { - struct pci_dev *child_dev; + struct pci_dev *child; + struct pci_bus *linkbus = link->pdev->subordinate; - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { - if (child_dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) + list_for_each_entry(child, &linkbus->devices, bus_list) { + if (child->pcie_type == PCI_EXP_TYPE_UPSTREAM) return true; } return false; @@ -184,289 +181,263 @@ static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev) * could use common clock. If they are, configure them to use the * common clock. That will reduce the ASPM state exit latency. */ -static void pcie_aspm_configure_common_clock(struct pci_dev *pdev) +static void pcie_aspm_configure_common_clock(struct pcie_link_state *link) { - int pos, child_pos, i = 0; - u16 reg16 = 0; - struct pci_dev *child_dev; - int same_clock = 1; + int ppos, cpos, same_clock = 1; + u16 reg16, parent_reg, child_reg[8]; unsigned long start_jiffies; - u16 child_regs[8], parent_reg; + struct pci_dev *child, *parent = link->pdev; + struct pci_bus *linkbus = parent->subordinate; /* - * all functions of a slot should have the same Slot Clock + * All functions of a slot should have the same Slot Clock * Configuration, so just check one function - * */ - child_dev = list_entry(pdev->subordinate->devices.next, struct pci_dev, - bus_list); - BUG_ON(!child_dev->is_pcie); + */ + child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); + BUG_ON(!child->is_pcie); /* Check downstream component if bit Slot Clock Configuration is 1 */ - child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); - pci_read_config_word(child_dev, child_pos + PCI_EXP_LNKSTA, ®16); + cpos = pci_find_capability(child, PCI_CAP_ID_EXP); + pci_read_config_word(child, cpos + PCI_EXP_LNKSTA, ®16); if (!(reg16 & PCI_EXP_LNKSTA_SLC)) same_clock = 0; /* Check upstream component if bit Slot Clock Configuration is 1 */ - pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); - pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, ®16); + ppos = pci_find_capability(parent, PCI_CAP_ID_EXP); + pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, ®16); if (!(reg16 & PCI_EXP_LNKSTA_SLC)) same_clock = 0; /* Configure downstream component, all functions */ - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { - child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); - pci_read_config_word(child_dev, child_pos + PCI_EXP_LNKCTL, - ®16); - child_regs[i] = reg16; + list_for_each_entry(child, &linkbus->devices, bus_list) { + cpos = pci_find_capability(child, PCI_CAP_ID_EXP); + pci_read_config_word(child, cpos + PCI_EXP_LNKCTL, ®16); + child_reg[PCI_FUNC(child->devfn)] = reg16; if (same_clock) reg16 |= PCI_EXP_LNKCTL_CCC; else reg16 &= ~PCI_EXP_LNKCTL_CCC; - pci_write_config_word(child_dev, child_pos + PCI_EXP_LNKCTL, - reg16); - i++; + pci_write_config_word(child, cpos + PCI_EXP_LNKCTL, reg16); } /* Configure upstream component */ - pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); + pci_read_config_word(parent, ppos + PCI_EXP_LNKCTL, ®16); parent_reg = reg16; if (same_clock) reg16 |= PCI_EXP_LNKCTL_CCC; else reg16 &= ~PCI_EXP_LNKCTL_CCC; - pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); + pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16); - /* retrain link */ + /* Retrain link */ reg16 |= PCI_EXP_LNKCTL_RL; - pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); + pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, reg16); - /* Wait for link training end */ - /* break out after waiting for timeout */ + /* Wait for link training end. Break out after waiting for timeout */ start_jiffies = jiffies; for (;;) { - pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, ®16); + pci_read_config_word(parent, ppos + PCI_EXP_LNKSTA, ®16); if (!(reg16 & PCI_EXP_LNKSTA_LT)) break; if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) break; msleep(1); } - /* training failed -> recover */ - if (reg16 & PCI_EXP_LNKSTA_LT) { - dev_printk (KERN_ERR, &pdev->dev, "ASPM: Could not configure" - " common clock\n"); - i = 0; - list_for_each_entry(child_dev, &pdev->subordinate->devices, - bus_list) { - child_pos = pci_find_capability(child_dev, - PCI_CAP_ID_EXP); - pci_write_config_word(child_dev, - child_pos + PCI_EXP_LNKCTL, - child_regs[i]); - i++; - } - pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, parent_reg); + if (!(reg16 & PCI_EXP_LNKSTA_LT)) + return; + + /* Training failed. Restore common clock configurations */ + dev_printk(KERN_ERR, &parent->dev, + "ASPM: Could not configure common clock\n"); + list_for_each_entry(child, &linkbus->devices, bus_list) { + cpos = pci_find_capability(child, PCI_CAP_ID_EXP); + pci_write_config_word(child, cpos + PCI_EXP_LNKCTL, + child_reg[PCI_FUNC(child->devfn)]); } + pci_write_config_word(parent, ppos + PCI_EXP_LNKCTL, parent_reg); } -/* - * calc_L0S_latency: Convert L0s latency encoding to ns - */ -static unsigned int calc_L0S_latency(unsigned int latency_encoding, int ac) +/* Convert L0s latency encoding to ns */ +static u32 calc_l0s_latency(u32 encoding) { - unsigned int ns = 64; + if (encoding == 0x7) + return (5 * 1000); /* > 4us */ + return (64 << encoding); +} - if (latency_encoding == 0x7) { - if (ac) - ns = -1U; - else - ns = 5*1000; /* > 4us */ - } else - ns *= (1 << latency_encoding); - return ns; +/* Convert L0s acceptable latency encoding to ns */ +static u32 calc_l0s_acceptable(u32 encoding) +{ + if (encoding == 0x7) + return -1U; + return (64 << encoding); } -/* - * calc_L1_latency: Convert L1 latency encoding to ns - */ -static unsigned int calc_L1_latency(unsigned int latency_encoding, int ac) +/* Convert L1 latency encoding to ns */ +static u32 calc_l1_latency(u32 encoding) { - unsigned int ns = 1000; + if (encoding == 0x7) + return (65 * 1000); /* > 64us */ + return (1000 << encoding); +} - if (latency_encoding == 0x7) { - if (ac) - ns = -1U; - else - ns = 65*1000; /* > 64us */ - } else - ns *= (1 << latency_encoding); - return ns; +/* Convert L1 acceptable latency encoding to ns */ +static u32 calc_l1_acceptable(u32 encoding) +{ + if (encoding == 0x7) + return -1U; + return (1000 << encoding); } static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state, - unsigned int *l0s, unsigned int *l1, unsigned int *enabled) + u32 *l0s, u32 *l1, u32 *enabled) { int pos; u16 reg16; - u32 reg32; - unsigned int latency; + u32 reg32, encoding; + *l0s = *l1 = *enabled = 0; pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, ®32); *state = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10; if (*state != PCIE_LINK_STATE_L0S && - *state != (PCIE_LINK_STATE_L1|PCIE_LINK_STATE_L0S)) + *state != (PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L0S)) *state = 0; if (*state == 0) return; - latency = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12; - *l0s = calc_L0S_latency(latency, 0); + encoding = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12; + *l0s = calc_l0s_latency(encoding); if (*state & PCIE_LINK_STATE_L1) { - latency = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; - *l1 = calc_L1_latency(latency, 0); + encoding = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; + *l1 = calc_l1_latency(encoding); } pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); - *enabled = reg16 & (PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1); + *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); } -static void pcie_aspm_cap_init(struct pci_dev *pdev) +static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { - struct pci_dev *child_dev; - u32 state, tmp; - struct pcie_link_state *link_state = pdev->link_state; + u32 support, l0s, l1, enabled; + struct pci_dev *child, *parent = link->pdev; + struct pci_bus *linkbus = parent->subordinate; + + if (blacklist) { + /* Set support state to 0, so we will disable ASPM later */ + link->aspm_support = 0; + link->aspm_default = 0; + link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + return; + } + + /* Configure common clock before checking latencies */ + pcie_aspm_configure_common_clock(link); /* upstream component states */ - pcie_aspm_get_cap_device(pdev, &link_state->support_state, - &link_state->l0s_upper_latency, - &link_state->l1_upper_latency, - &link_state->enabled_state); + pcie_aspm_get_cap_device(parent, &support, &l0s, &l1, &enabled); + link->aspm_support = support; + link->latency.l0s = l0s; + link->latency.l1 = l1; + link->aspm_enabled = enabled; + /* downstream component states, all functions have the same setting */ - child_dev = list_entry(pdev->subordinate->devices.next, struct pci_dev, - bus_list); - pcie_aspm_get_cap_device(child_dev, &state, - &link_state->l0s_down_latency, - &link_state->l1_down_latency, - &tmp); - link_state->support_state &= state; - if (!link_state->support_state) + child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); + pcie_aspm_get_cap_device(child, &support, &l0s, &l1, &enabled); + link->aspm_support &= support; + link->latency.l0s = max_t(u32, link->latency.l0s, l0s); + link->latency.l1 = max_t(u32, link->latency.l1, l1); + + if (!link->aspm_support) return; - link_state->enabled_state &= link_state->support_state; - link_state->bios_aspm_state = link_state->enabled_state; + + link->aspm_enabled &= link->aspm_support; + link->aspm_default = link->aspm_enabled; /* ENDPOINT states*/ - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { + list_for_each_entry(child, &linkbus->devices, bus_list) { int pos; - u32 reg32; - unsigned int latency; - struct endpoint_state *ep_state = - &link_state->endpoints[PCI_FUNC(child_dev->devfn)]; + u32 reg32, encoding; + struct aspm_latency *acceptable = + &link->acceptable[PCI_FUNC(child->devfn)]; - if (child_dev->pcie_type != PCI_EXP_TYPE_ENDPOINT && - child_dev->pcie_type != PCI_EXP_TYPE_LEG_END) + if (child->pcie_type != PCI_EXP_TYPE_ENDPOINT && + child->pcie_type != PCI_EXP_TYPE_LEG_END) continue; - pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); - pci_read_config_dword(child_dev, pos + PCI_EXP_DEVCAP, ®32); - latency = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6; - latency = calc_L0S_latency(latency, 1); - ep_state->l0s_acceptable_latency = latency; - if (link_state->support_state & PCIE_LINK_STATE_L1) { - latency = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; - latency = calc_L1_latency(latency, 1); - ep_state->l1_acceptable_latency = latency; + pos = pci_find_capability(child, PCI_CAP_ID_EXP); + pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, ®32); + encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6; + acceptable->l0s = calc_l0s_acceptable(encoding); + if (link->aspm_support & PCIE_LINK_STATE_L1) { + encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; + acceptable->l1 = calc_l1_acceptable(encoding); } } } -static unsigned int __pcie_aspm_check_state_one(struct pci_dev *pdev, - unsigned int state) -{ - struct pci_dev *parent_dev, *tmp_dev; - unsigned int latency, l1_latency = 0; - struct pcie_link_state *link_state; - struct endpoint_state *ep_state; - - parent_dev = pdev->bus->self; - link_state = parent_dev->link_state; - state &= link_state->support_state; - if (state == 0) - return 0; - ep_state = &link_state->endpoints[PCI_FUNC(pdev->devfn)]; - - /* - * Check latency for endpoint device. - * TBD: The latency from the endpoint to root complex vary per - * switch's upstream link state above the device. Here we just do a - * simple check which assumes all links above the device can be in L1 - * state, that is we just consider the worst case. If switch's upstream - * link can't be put into L0S/L1, then our check is too strictly. - */ - tmp_dev = pdev; - while (state & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1)) { - parent_dev = tmp_dev->bus->self; - link_state = parent_dev->link_state; - if (state & PCIE_LINK_STATE_L0S) { - latency = max_t(unsigned int, - link_state->l0s_upper_latency, - link_state->l0s_down_latency); - if (latency > ep_state->l0s_acceptable_latency) - state &= ~PCIE_LINK_STATE_L0S; - } - if (state & PCIE_LINK_STATE_L1) { - latency = max_t(unsigned int, - link_state->l1_upper_latency, - link_state->l1_down_latency); - if (latency + l1_latency > - ep_state->l1_acceptable_latency) - state &= ~PCIE_LINK_STATE_L1; - } - if (!parent_dev->bus->self) /* parent_dev is a root port */ - break; - else { - /* - * parent_dev is the downstream port of a switch, make - * tmp_dev the upstream port of the switch - */ - tmp_dev = parent_dev->bus->self; - /* - * every switch on the path to root complex need 1 more - * microsecond for L1. Spec doesn't mention L0S. - */ - if (state & PCIE_LINK_STATE_L1) - l1_latency += 1000; - } +/** + * __pcie_aspm_check_state_one - check latency for endpoint device. + * @endpoint: pointer to the struct pci_dev of endpoint device + * + * TBD: The latency from the endpoint to root complex vary per switch's + * upstream link state above the device. Here we just do a simple check + * which assumes all links above the device can be in L1 state, that + * is we just consider the worst case. If switch's upstream link can't + * be put into L0S/L1, then our check is too strictly. + */ +static u32 __pcie_aspm_check_state_one(struct pci_dev *endpoint, u32 state) +{ + u32 l1_switch_latency = 0; + struct aspm_latency *acceptable; + struct pcie_link_state *link; + + link = endpoint->bus->self->link_state; + state &= link->aspm_support; + acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; + + while (link && state) { + if ((state & PCIE_LINK_STATE_L0S) && + (link->latency.l0s > acceptable->l0s)) + state &= ~PCIE_LINK_STATE_L0S; + if ((state & PCIE_LINK_STATE_L1) && + (link->latency.l1 + l1_switch_latency > acceptable->l1)) + state &= ~PCIE_LINK_STATE_L1; + link = link->parent; + /* + * Every switch on the path to root complex need 1 + * more microsecond for L1. Spec doesn't mention L0s. + */ + l1_switch_latency += 1000; } return state; } -static unsigned int pcie_aspm_check_state(struct pci_dev *pdev, - unsigned int state) +static u32 pcie_aspm_check_state(struct pcie_link_state *link, u32 state) { - struct pci_dev *child_dev; + pci_power_t power_state; + struct pci_dev *child; + struct pci_bus *linkbus = link->pdev->subordinate; /* If no child, ignore the link */ - if (list_empty(&pdev->subordinate->devices)) + if (list_empty(&linkbus->devices)) return state; - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { - if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { - /* - * If downstream component of a link is pci bridge, we - * disable ASPM for now for the link - * */ - state = 0; - break; - } - if ((child_dev->pcie_type != PCI_EXP_TYPE_ENDPOINT && - child_dev->pcie_type != PCI_EXP_TYPE_LEG_END)) + + list_for_each_entry(child, &linkbus->devices, bus_list) { + /* + * If downstream component of a link is pci bridge, we + * disable ASPM for now for the link + */ + if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) + return 0; + + if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT && + child->pcie_type != PCI_EXP_TYPE_LEG_END)) continue; /* Device not in D0 doesn't need check latency */ - if (child_dev->current_state == PCI_D1 || - child_dev->current_state == PCI_D2 || - child_dev->current_state == PCI_D3hot || - child_dev->current_state == PCI_D3cold) + power_state = child->current_state; + if (power_state == PCI_D1 || power_state == PCI_D2 || + power_state == PCI_D3hot || power_state == PCI_D3cold) continue; - state = __pcie_aspm_check_state_one(child_dev, state); + state = __pcie_aspm_check_state_one(child, state); } return state; } @@ -482,90 +453,71 @@ static void __pcie_aspm_config_one_dev(struct pci_dev *pdev, unsigned int state) pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); } -static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state) +static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) { - struct pci_dev *child_dev; - int valid = 1; - struct pcie_link_state *link_state = pdev->link_state; + struct pci_dev *child, *parent = link->pdev; + struct pci_bus *linkbus = parent->subordinate; /* If no child, disable the link */ - if (list_empty(&pdev->subordinate->devices)) + if (list_empty(&linkbus->devices)) state = 0; /* - * if the downstream component has pci bridge function, don't do ASPM - * now + * If the downstream component has pci bridge function, don't + * do ASPM now. */ - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { - if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { - valid = 0; - break; - } + list_for_each_entry(child, &linkbus->devices, bus_list) { + if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) + return; } - if (!valid) - return; - /* - * spec 2.0 suggests all functions should be configured the same - * setting for ASPM. Enabling ASPM L1 should be done in upstream - * component first and then downstream, and vice versa for disabling - * ASPM L1. Spec doesn't mention L0S. + * Spec 2.0 suggests all functions should be configured the + * same setting for ASPM. Enabling ASPM L1 should be done in + * upstream component first and then downstream, and vice + * versa for disabling ASPM L1. Spec doesn't mention L0S. */ if (state & PCIE_LINK_STATE_L1) - __pcie_aspm_config_one_dev(pdev, state); + __pcie_aspm_config_one_dev(parent, state); - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) - __pcie_aspm_config_one_dev(child_dev, state); + list_for_each_entry(child, &linkbus->devices, bus_list) + __pcie_aspm_config_one_dev(child, state); if (!(state & PCIE_LINK_STATE_L1)) - __pcie_aspm_config_one_dev(pdev, state); + __pcie_aspm_config_one_dev(parent, state); - link_state->enabled_state = state; + link->aspm_enabled = state; } -static struct pcie_link_state *get_root_port_link(struct pcie_link_state *link) +/* Check the whole hierarchy, and configure each link in the hierarchy */ +static void __pcie_aspm_configure_link_state(struct pcie_link_state *link, + u32 state) { - struct pcie_link_state *root_port_link = link; - while (root_port_link->parent) - root_port_link = root_port_link->parent; - return root_port_link; -} + struct pcie_link_state *leaf, *root = link->root; -/* check the whole hierarchy, and configure each link in the hierarchy */ -static void __pcie_aspm_configure_link_state(struct pci_dev *pdev, - unsigned int state) -{ - struct pcie_link_state *link_state = pdev->link_state; - struct pcie_link_state *root_port_link = get_root_port_link(link_state); - struct pcie_link_state *leaf; + state &= (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); - state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; - - /* check all links who have specific root port link */ - list_for_each_entry(leaf, &link_list, sibiling) { - if (!list_empty(&leaf->children) || - get_root_port_link(leaf) != root_port_link) + /* Check all links who have specific root port link */ + list_for_each_entry(leaf, &link_list, sibling) { + if (!list_empty(&leaf->children) || (leaf->root != root)) continue; - state = pcie_aspm_check_state(leaf->pdev, state); + state = pcie_aspm_check_state(leaf, state); } - /* check root port link too in case it hasn't children */ - state = pcie_aspm_check_state(root_port_link->pdev, state); - - if (link_state->enabled_state == state) + /* Check root port link too in case it hasn't children */ + state = pcie_aspm_check_state(root, state); + if (link->aspm_enabled == state) return; - /* - * we must change the hierarchy. See comments in + * We must change the hierarchy. See comments in * __pcie_aspm_config_link for the order **/ if (state & PCIE_LINK_STATE_L1) { - list_for_each_entry(leaf, &link_list, sibiling) { - if (get_root_port_link(leaf) == root_port_link) - __pcie_aspm_config_link(leaf->pdev, state); + list_for_each_entry(leaf, &link_list, sibling) { + if (leaf->root == root) + __pcie_aspm_config_link(leaf, state); } } else { - list_for_each_entry_reverse(leaf, &link_list, sibiling) { - if (get_root_port_link(leaf) == root_port_link) - __pcie_aspm_config_link(leaf->pdev, state); + list_for_each_entry_reverse(leaf, &link_list, sibling) { + if (leaf->root == root) + __pcie_aspm_config_link(leaf, state); } } } @@ -574,45 +526,42 @@ static void __pcie_aspm_configure_link_state(struct pci_dev *pdev, * pcie_aspm_configure_link_state: enable/disable PCI express link state * @pdev: the root port or switch downstream port */ -static void pcie_aspm_configure_link_state(struct pci_dev *pdev, - unsigned int state) +static void pcie_aspm_configure_link_state(struct pcie_link_state *link, + u32 state) { down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - __pcie_aspm_configure_link_state(pdev, state); + __pcie_aspm_configure_link_state(link, state); mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); } -static void free_link_state(struct pci_dev *pdev) +static void free_link_state(struct pcie_link_state *link) { - kfree(pdev->link_state); - pdev->link_state = NULL; + link->pdev->link_state = NULL; + kfree(link); } static int pcie_aspm_sanity_check(struct pci_dev *pdev) { - struct pci_dev *child_dev; - int child_pos; + struct pci_dev *child; + int pos; u32 reg32; - /* - * Some functions in a slot might not all be PCIE functions, very - * strange. Disable ASPM for the whole slot + * Some functions in a slot might not all be PCIE functions, + * very strange. Disable ASPM for the whole slot */ - list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { - child_pos = pci_find_capability(child_dev, PCI_CAP_ID_EXP); - if (!child_pos) + list_for_each_entry(child, &pdev->subordinate->devices, bus_list) { + pos = pci_find_capability(child, PCI_CAP_ID_EXP); + if (!pos) return -EINVAL; - /* * Disable ASPM for pre-1.1 PCIe device, we follow MS to use * RBER bit to determine if a function is 1.1 version device */ - pci_read_config_dword(child_dev, child_pos + PCI_EXP_DEVCAP, - ®32); + pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, ®32); if (!(reg32 & PCI_EXP_DEVCAP_RBER) && !aspm_force) { - dev_printk(KERN_INFO, &child_dev->dev, "disabling ASPM" + dev_printk(KERN_INFO, &child->dev, "disabling ASPM" " on pre-1.1 PCIe device. You can enable it" " with 'pcie_aspm=force'\n"); return -EINVAL; @@ -621,6 +570,47 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) return 0; } +static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev) +{ + struct pcie_link_state *link; + int blacklist = !!pcie_aspm_sanity_check(pdev); + + link = kzalloc(sizeof(*link), GFP_KERNEL); + if (!link) + return NULL; + INIT_LIST_HEAD(&link->sibling); + INIT_LIST_HEAD(&link->children); + INIT_LIST_HEAD(&link->link); + link->pdev = pdev; + if (pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) { + struct pcie_link_state *parent; + parent = pdev->bus->parent->self->link_state; + if (!parent) { + kfree(link); + return NULL; + } + link->parent = parent; + list_add(&link->link, &parent->children); + } + /* Setup a pointer to the root port link */ + if (!link->parent) + link->root = link; + else + link->root = link->parent->root; + + list_add(&link->sibling, &link_list); + + pdev->link_state = link; + + /* Check ASPM capability */ + pcie_aspm_cap_init(link, blacklist); + + /* Check Clock PM capability */ + pcie_clkpm_cap_init(link, blacklist); + + return link; +} + /* * pcie_aspm_init_link_state: Initiate PCI express link state. * It is called after the pcie and its children devices are scaned. @@ -628,75 +618,47 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) */ void pcie_aspm_init_link_state(struct pci_dev *pdev) { - unsigned int state; - struct pcie_link_state *link_state; - int error = 0; - int blacklist; + u32 state; + struct pcie_link_state *link; if (aspm_disabled || !pdev->is_pcie || pdev->link_state) return; if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && - pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) + pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) + return; + + /* VIA has a strange chipset, root port is under a bridge */ + if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT && + pdev->bus->self) return; + down_read(&pci_bus_sem); if (list_empty(&pdev->subordinate->devices)) goto out; - blacklist = !!pcie_aspm_sanity_check(pdev); - mutex_lock(&aspm_lock); - - link_state = kzalloc(sizeof(*link_state), GFP_KERNEL); - if (!link_state) - goto unlock_out; - - link_state->downstream_has_switch = pcie_aspm_downstream_has_switch(pdev); - INIT_LIST_HEAD(&link_state->children); - INIT_LIST_HEAD(&link_state->link); - if (pdev->bus->self) {/* this is a switch */ - struct pcie_link_state *parent_link_state; - - parent_link_state = pdev->bus->parent->self->link_state; - if (!parent_link_state) { - kfree(link_state); - goto unlock_out; - } - list_add(&link_state->link, &parent_link_state->children); - link_state->parent = parent_link_state; - } - - pdev->link_state = link_state; - - if (!blacklist) { - pcie_aspm_configure_common_clock(pdev); - pcie_aspm_cap_init(pdev); + link = pcie_aspm_setup_link_state(pdev); + if (!link) + goto unlock; + /* + * Setup initial ASPM state + * + * If link has switch, delay the link config. The leaf link + * initialization will config the whole hierarchy. But we must + * make sure BIOS doesn't set unsupported link state. + */ + if (pcie_aspm_downstream_has_switch(link)) { + state = pcie_aspm_check_state(link, link->aspm_default); + __pcie_aspm_config_link(link, state); } else { - link_state->enabled_state = PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; - link_state->bios_aspm_state = 0; - /* Set support state to 0, so we will disable ASPM later */ - link_state->support_state = 0; + state = policy_to_aspm_state(link); + __pcie_aspm_configure_link_state(link, state); } - link_state->pdev = pdev; - list_add(&link_state->sibiling, &link_list); - - if (link_state->downstream_has_switch) { - /* - * If link has switch, delay the link config. The leaf link - * initialization will config the whole hierarchy. but we must - * make sure BIOS doesn't set unsupported link state - **/ - state = pcie_aspm_check_state(pdev, link_state->bios_aspm_state); - __pcie_aspm_config_link(pdev, state); - } else - __pcie_aspm_configure_link_state(pdev, - policy_to_aspm_state(pdev)); - - pcie_check_clock_pm(pdev, blacklist); - -unlock_out: - if (error) - free_link_state(pdev); + /* Setup initial Clock PM state */ + state = (link->clkpm_capable) ? policy_to_clkpm_state(link) : 0; + pcie_set_clkpm(link, state); +unlock: mutex_unlock(&aspm_lock); out: up_read(&pci_bus_sem); @@ -725,11 +687,11 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) /* All functions are removed, so just disable ASPM for the link */ __pcie_aspm_config_one_dev(parent, 0); - list_del(&link_state->sibiling); + list_del(&link_state->sibling); list_del(&link_state->link); /* Clock PM is for endpoint device */ - free_link_state(parent); + free_link_state(link_state); out: mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -749,7 +711,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) * devices changed PM state, we should recheck if latency meets all * functions' requirement */ - pcie_aspm_configure_link_state(pdev, link_state->enabled_state); + pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled); } /* @@ -772,14 +734,12 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); link_state = parent->link_state; - link_state->support_state &= - ~(state & (PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1)); - if (state & PCIE_LINK_STATE_CLKPM) - link_state->clk_pm_capable = 0; - - __pcie_aspm_configure_link_state(parent, link_state->enabled_state); - if (!link_state->clk_pm_capable && link_state->clk_pm_enabled) - pcie_set_clock_pm(parent, 0); + link_state->aspm_support &= ~state; + __pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled); + if (state & PCIE_LINK_STATE_CLKPM) { + link_state->clkpm_capable = 0; + pcie_set_clkpm(link_state, 0); + } mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); } @@ -788,7 +748,6 @@ EXPORT_SYMBOL(pci_disable_link_state); static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) { int i; - struct pci_dev *pdev; struct pcie_link_state *link_state; for (i = 0; i < ARRAY_SIZE(policy_str); i++) @@ -802,14 +761,10 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); aspm_policy = i; - list_for_each_entry(link_state, &link_list, sibiling) { - pdev = link_state->pdev; - __pcie_aspm_configure_link_state(pdev, - policy_to_aspm_state(pdev)); - if (link_state->clk_pm_capable && - link_state->clk_pm_enabled != policy_to_clkpm_state(pdev)) - pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); - + list_for_each_entry(link_state, &link_list, sibling) { + __pcie_aspm_configure_link_state(link_state, + policy_to_aspm_state(link_state)); + pcie_set_clkpm(link_state, policy_to_clkpm_state(link_state)); } mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -838,7 +793,7 @@ static ssize_t link_state_show(struct device *dev, struct pci_dev *pci_device = to_pci_dev(dev); struct pcie_link_state *link_state = pci_device->link_state; - return sprintf(buf, "%d\n", link_state->enabled_state); + return sprintf(buf, "%d\n", link_state->aspm_enabled); } static ssize_t link_state_store(struct device *dev, @@ -846,7 +801,7 @@ static ssize_t link_state_store(struct device *dev, const char *buf, size_t n) { - struct pci_dev *pci_device = to_pci_dev(dev); + struct pci_dev *pdev = to_pci_dev(dev); int state; if (n < 1) @@ -854,7 +809,7 @@ static ssize_t link_state_store(struct device *dev, state = buf[0]-'0'; if (state >= 0 && state <= 3) { /* setup link aspm state */ - pcie_aspm_configure_link_state(pci_device, state); + pcie_aspm_configure_link_state(pdev->link_state, state); return n; } @@ -868,7 +823,7 @@ static ssize_t clk_ctl_show(struct device *dev, struct pci_dev *pci_device = to_pci_dev(dev); struct pcie_link_state *link_state = pci_device->link_state; - return sprintf(buf, "%d\n", link_state->clk_pm_enabled); + return sprintf(buf, "%d\n", link_state->clkpm_enabled); } static ssize_t clk_ctl_store(struct device *dev, @@ -876,7 +831,7 @@ static ssize_t clk_ctl_store(struct device *dev, const char *buf, size_t n) { - struct pci_dev *pci_device = to_pci_dev(dev); + struct pci_dev *pdev = to_pci_dev(dev); int state; if (n < 1) @@ -885,7 +840,7 @@ static ssize_t clk_ctl_store(struct device *dev, down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - pcie_set_clock_pm(pci_device, !!state); + pcie_set_clkpm_nocheck(pdev->link_state, !!state); mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -904,10 +859,10 @@ void pcie_aspm_create_sysfs_dev_files(struct pci_dev *pdev) pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state) return; - if (link_state->support_state) + if (link_state->aspm_support) sysfs_add_file_to_group(&pdev->dev.kobj, &dev_attr_link_state.attr, power_group); - if (link_state->clk_pm_capable) + if (link_state->clkpm_capable) sysfs_add_file_to_group(&pdev->dev.kobj, &dev_attr_clk_ctl.attr, power_group); } @@ -920,10 +875,10 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev) pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) || !link_state) return; - if (link_state->support_state) + if (link_state->aspm_support) sysfs_remove_file_from_group(&pdev->dev.kobj, &dev_attr_link_state.attr, power_group); - if (link_state->clk_pm_capable) + if (link_state->clkpm_capable) sysfs_remove_file_from_group(&pdev->dev.kobj, &dev_attr_clk_ctl.attr, power_group); } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f1ae2475fff..40e75f6a505 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -193,7 +193,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, res->flags |= pci_calc_resource_flags(l) | IORESOURCE_SIZEALIGN; if (type == pci_bar_io) { l &= PCI_BASE_ADDRESS_IO_MASK; - mask = PCI_BASE_ADDRESS_IO_MASK & 0xffff; + mask = PCI_BASE_ADDRESS_IO_MASK & IO_SPACE_LIMIT; } else { l &= PCI_BASE_ADDRESS_MEM_MASK; mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; @@ -237,6 +237,8 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, dev_printk(KERN_DEBUG, &dev->dev, "reg %x 64bit mmio: %pR\n", pos, res); } + + res->flags |= IORESOURCE_MEM_64; } else { sz = pci_size(l, sz, mask); @@ -287,7 +289,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) struct resource *res; int i; - if (!child->parent) /* It's a host bus, nothing to read */ + if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ return; if (dev->transparent) { @@ -362,7 +364,10 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) } } if (base <= limit) { - res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; + res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | + IORESOURCE_MEM | IORESOURCE_PREFETCH; + if (res->flags & PCI_PREF_RANGE_TYPE_64) + res->flags |= IORESOURCE_MEM_64; res->start = base; res->end = limit + 0xfffff; dev_printk(KERN_DEBUG, &dev->dev, "bridge %sbit mmio pref: %pR\n", diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index bd4253f93d5..56552d74abe 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1133,6 +1133,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev) switch (dev->subsystem_device) { case 0x1751: /* M2N notebook */ case 0x1821: /* M5N notebook */ + case 0x1897: /* A6L notebook */ asus_hides_smbus = 1; } else if (dev->device == PCI_DEVICE_ID_INTEL_82855PM_HB) @@ -1163,6 +1164,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev) switch (dev->subsystem_device) { case 0x12bc: /* HP D330L */ case 0x12bd: /* HP D530 */ + case 0x006a: /* HP Compaq nx9500 */ asus_hides_smbus = 1; } else if (dev->device == PCI_DEVICE_ID_INTEL_82875_HB) @@ -2016,6 +2018,28 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_NX2_5709S, quirk_brcm_570x_limit_vpd); +/* Originally in EDAC sources for i82875P: + * Intel tells BIOS developers to hide device 6 which + * configures the overflow device access containing + * the DRBs - this is where we expose device 6. + * http://www.x86-secret.com/articles/tweak/pat/patsecrets-2.htm + */ +static void __devinit quirk_unhide_mch_dev6(struct pci_dev *dev) +{ + u8 reg; + + if (pci_read_config_byte(dev, 0xF4, ®) == 0 && !(reg & 0x02)) { + dev_info(&dev->dev, "Enabling MCH 'Overflow' Device\n"); + pci_write_config_byte(dev, 0xF4, reg | 0x02); + } +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82865_HB, + quirk_unhide_mch_dev6); +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82875_HB, + quirk_unhide_mch_dev6); + + #ifdef CONFIG_PCI_MSI /* Some chipsets do not support MSI. We cannot easily rely on setting * PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 86503c14ce7..176615e7231 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -32,8 +32,6 @@ static void pci_stop_dev(struct pci_dev *dev) static void pci_destroy_dev(struct pci_dev *dev) { - pci_stop_dev(dev); - /* Remove the device from the device lists, and prevent any further * list accesses from this device */ down_write(&pci_bus_sem); diff --git a/drivers/pci/search.c b/drivers/pci/search.c index 710d4ea6956..e8cb5051c31 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -29,7 +29,7 @@ pci_find_upstream_pcie_bridge(struct pci_dev *pdev) if (pdev->is_pcie) return NULL; while (1) { - if (!pdev->bus->parent) + if (pci_is_root_bus(pdev->bus)) break; pdev = pdev->bus->self; /* a p2p bridge */ @@ -115,36 +115,6 @@ pci_find_next_bus(const struct pci_bus *from) #ifdef CONFIG_PCI_LEGACY /** - * pci_find_slot - locate PCI device from a given PCI slot - * @bus: number of PCI bus on which desired PCI device resides - * @devfn: encodes number of PCI slot in which the desired PCI - * device resides and the logical device number within that slot - * in case of multi-function devices. - * - * Given a PCI bus and slot/function number, the desired PCI device - * is located in system global list of PCI devices. If the device - * is found, a pointer to its data structure is returned. If no - * device is found, %NULL is returned. - * - * NOTE: Do not use this function any more; use pci_get_slot() instead, as - * the PCI device returned by this function can disappear at any moment in - * time. - */ -struct pci_dev *pci_find_slot(unsigned int bus, unsigned int devfn) -{ - struct pci_dev *dev = NULL; - - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { - if (dev->bus->number == bus && dev->devfn == devfn) { - pci_dev_put(dev); - return dev; - } - } - return NULL; -} -EXPORT_SYMBOL(pci_find_slot); - -/** * pci_find_device - begin or continue searching for a PCI device by vendor/device id * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index a00f85471b6..b636e245445 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -58,7 +58,6 @@ static void pbus_assign_resources_sorted(const struct pci_bus *bus) res = list->res; idx = res - &list->dev->resource[0]; if (pci_assign_resource(list->dev, idx)) { - /* FIXME: get rid of this */ res->start = 0; res->end = 0; res->flags = 0; @@ -143,6 +142,7 @@ static void pci_setup_bridge(struct pci_bus *bus) struct pci_dev *bridge = bus->self; struct pci_bus_region region; u32 l, bu, lu, io_upper16; + int pref_mem64; if (pci_is_enabled(bridge)) return; @@ -198,16 +198,22 @@ static void pci_setup_bridge(struct pci_bus *bus) pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, 0); /* Set up PREF base/limit. */ + pref_mem64 = 0; bu = lu = 0; pcibios_resource_to_bus(bridge, ®ion, bus->resource[2]); if (bus->resource[2]->flags & IORESOURCE_PREFETCH) { + int width = 8; l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; - bu = upper_32_bits(region.start); - lu = upper_32_bits(region.end); - dev_info(&bridge->dev, " PREFETCH window: %#016llx-%#016llx\n", - (unsigned long long)region.start, - (unsigned long long)region.end); + if (bus->resource[2]->flags & IORESOURCE_MEM_64) { + pref_mem64 = 1; + bu = upper_32_bits(region.start); + lu = upper_32_bits(region.end); + width = 16; + } + dev_info(&bridge->dev, " PREFETCH window: %#0*llx-%#0*llx\n", + width, (unsigned long long)region.start, + width, (unsigned long long)region.end); } else { l = 0x0000fff0; @@ -215,9 +221,11 @@ static void pci_setup_bridge(struct pci_bus *bus) } pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, l); - /* Set the upper 32 bits of PREF base & limit. */ - pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); - pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); + if (pref_mem64) { + /* Set the upper 32 bits of PREF base & limit. */ + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, bu); + pci_write_config_dword(bridge, PCI_PREF_LIMIT_UPPER32, lu); + } pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); } @@ -255,8 +263,25 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) pci_read_config_dword(bridge, PCI_PREF_MEMORY_BASE, &pmem); pci_write_config_dword(bridge, PCI_PREF_MEMORY_BASE, 0x0); } - if (pmem) + if (pmem) { b_res[2].flags |= IORESOURCE_MEM | IORESOURCE_PREFETCH; + if ((pmem & PCI_PREF_RANGE_TYPE_MASK) == PCI_PREF_RANGE_TYPE_64) + b_res[2].flags |= IORESOURCE_MEM_64; + } + + /* double check if bridge does support 64 bit pref */ + if (b_res[2].flags & IORESOURCE_MEM_64) { + u32 mem_base_hi, tmp; + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, + &mem_base_hi); + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, + 0xffffffff); + pci_read_config_dword(bridge, PCI_PREF_BASE_UPPER32, &tmp); + if (!tmp) + b_res[2].flags &= ~IORESOURCE_MEM_64; + pci_write_config_dword(bridge, PCI_PREF_BASE_UPPER32, + mem_base_hi); + } } /* Helper function for sizing routines: find first available @@ -336,6 +361,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long resource_size_t aligns[12]; /* Alignments from 1Mb to 2Gb */ int order, max_order; struct resource *b_res = find_free_bus_resource(bus, type); + unsigned int mem64_mask = 0; if (!b_res) return 0; @@ -344,9 +370,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long max_order = 0; size = 0; + mem64_mask = b_res->flags & IORESOURCE_MEM_64; + b_res->flags &= ~IORESOURCE_MEM_64; + list_for_each_entry(dev, &bus->devices, bus_list) { int i; - + for (i = 0; i < PCI_NUM_RESOURCES; i++) { struct resource *r = &dev->resource[i]; resource_size_t r_size; @@ -372,6 +401,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long aligns[order] += align; if (order > max_order) max_order = order; + mem64_mask &= r->flags & IORESOURCE_MEM_64; } } @@ -396,6 +426,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long b_res->start = min_align; b_res->end = size + min_align - 1; b_res->flags |= IORESOURCE_STARTALIGN; + b_res->flags |= mem64_mask; return 1; } diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 12403516776..b711fb7181e 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -135,23 +135,16 @@ void pci_disable_bridge_window(struct pci_dev *dev) } #endif /* CONFIG_PCI_QUIRKS */ -int pci_assign_resource(struct pci_dev *dev, int resno) +static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, + int resno) { - struct pci_bus *bus = dev->bus; struct resource *res = dev->resource + resno; resource_size_t size, min, align; int ret; size = resource_size(res); min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; - align = resource_alignment(res); - if (!align) { - dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " - "alignment) %pR flags %#lx\n", - resno, res, res->flags); - return -EINVAL; - } /* First, try exact prefetching match.. */ ret = pci_bus_alloc_resource(bus, res, size, align, min, @@ -169,10 +162,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) pcibios_align_resource, dev); } - if (ret) { - dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", - resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); - } else { + if (!ret) { res->flags &= ~IORESOURCE_STARTALIGN; if (resno < PCI_BRIDGE_RESOURCES) pci_update_resource(dev, resno); @@ -181,6 +171,39 @@ int pci_assign_resource(struct pci_dev *dev, int resno) return ret; } +int pci_assign_resource(struct pci_dev *dev, int resno) +{ + struct resource *res = dev->resource + resno; + resource_size_t align; + struct pci_bus *bus; + int ret; + + align = resource_alignment(res); + if (!align) { + dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " + "alignment) %pR flags %#lx\n", + resno, res, res->flags); + return -EINVAL; + } + + bus = dev->bus; + while ((ret = __pci_assign_resource(bus, dev, resno))) { + if (bus->parent && bus->self->transparent) + bus = bus->parent; + else + bus = NULL; + if (bus) + continue; + break; + } + + if (ret) + dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", + resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); + + return ret; +} + #if 0 int pci_assign_resource_fixed(struct pci_dev *dev, int resno) { diff --git a/drivers/pci/slot.c b/drivers/pci/slot.c index fe95ce20bcb..eddb0748b0e 100644 --- a/drivers/pci/slot.c +++ b/drivers/pci/slot.c @@ -307,6 +307,45 @@ void pci_destroy_slot(struct pci_slot *slot) } EXPORT_SYMBOL_GPL(pci_destroy_slot); +#if defined(CONFIG_HOTPLUG_PCI) || defined(CONFIG_HOTPLUG_PCI_MODULE) +#include <linux/pci_hotplug.h> +/** + * pci_hp_create_link - create symbolic link to the hotplug driver module. + * @slot: struct pci_slot + * + * Helper function for pci_hotplug_core.c to create symbolic link to + * the hotplug driver module. + */ +void pci_hp_create_module_link(struct pci_slot *pci_slot) +{ + struct hotplug_slot *slot = pci_slot->hotplug; + struct kobject *kobj = NULL; + int no_warn; + + if (!slot || !slot->ops) + return; + kobj = kset_find_obj(module_kset, slot->ops->mod_name); + if (!kobj) + return; + no_warn = sysfs_create_link(&pci_slot->kobj, kobj, "module"); + kobject_put(kobj); +} +EXPORT_SYMBOL_GPL(pci_hp_create_module_link); + +/** + * pci_hp_remove_link - remove symbolic link to the hotplug driver module. + * @slot: struct pci_slot + * + * Helper function for pci_hotplug_core.c to remove symbolic link to + * the hotplug driver module. + */ +void pci_hp_remove_module_link(struct pci_slot *pci_slot) +{ + sysfs_remove_link(&pci_slot->kobj, "module"); +} +EXPORT_SYMBOL_GPL(pci_hp_remove_module_link); +#endif + static int pci_slot_init(void) { struct kset *pci_bus_kset; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index c682ac53641..7232fe7104a 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -34,10 +34,27 @@ config ACER_WMI If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M here. +config ACERHDF + tristate "Acer Aspire One temperature and fan driver" + depends on THERMAL && THERMAL_HWMON && ACPI + ---help--- + This is a driver for Acer Aspire One netbooks. It allows to access + the temperature sensor and to control the fan. + + After loading this driver the BIOS is still in control of the fan. + To let the kernel handle the fan, do: + echo -n enabled > /sys/class/thermal/thermal_zone0/mode + + For more information about this driver see + <http://piie.net/files/acerhdf_README.txt> + + If you have an Acer Aspire One netbook, say Y or M + here. + config ASUS_LAPTOP - tristate "Asus Laptop Extras (EXPERIMENTAL)" + tristate "Asus Laptop Extras" depends on ACPI - depends on EXPERIMENTAL && !ACPI_ASUS + depends on !ACPI_ASUS select LEDS_CLASS select NEW_LEDS select BACKLIGHT_CLASS_DEVICE @@ -45,12 +62,12 @@ config ASUS_LAPTOP ---help--- This is the new Linux driver for Asus laptops. It may also support some MEDION, JVC or VICTOR laptops. It makes all the extra buttons generate - standard ACPI events that go through /proc/acpi/events. It also adds + standard ACPI events and input events. It also adds support for video output switching, LCD backlight control, Bluetooth and Wlan control, and most importantly, allows you to blink those fancy LEDs. For more information and a userspace daemon for handling the extra - buttons see <http://acpi4asus.sf.net/>. + buttons see <http://acpi4asus.sf.net>. If you have an ACPI-compatible ASUS laptop, say Y or M here. @@ -342,7 +359,10 @@ config EEEPC_LAPTOP select HWMON ---help--- This driver supports the Fn-Fx keys on Eee PC laptops. - It also adds the ability to switch camera/wlan on/off. + + It also gives access to some extra laptop functionalities like + Bluetooth, backlight and allows powering on/off some other + devices. If you have an Eee PC laptop, say Y or M here. @@ -369,7 +389,7 @@ config ACPI_WMI any ACPI-WMI devices. config ACPI_ASUS - tristate "ASUS/Medion Laptop Extras" + tristate "ASUS/Medion Laptop Extras (DEPRECATED)" depends on ACPI select BACKLIGHT_CLASS_DEVICE ---help--- @@ -390,7 +410,7 @@ config ACPI_ASUS parameters. More information and a userspace daemon for handling the extra buttons - at <http://sourceforge.net/projects/acpi4asus/>. + at <http://acpi4asus.sf.net>. If you have an ACPI-compatible ASUS laptop, say Y or M here. This driver is still under development, so if your laptop is unsupported or diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index e40c7bd1b87..641b8bfa553 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -9,6 +9,7 @@ obj-$(CONFIG_COMPAL_LAPTOP) += compal-laptop.o obj-$(CONFIG_DELL_LAPTOP) += dell-laptop.o obj-$(CONFIG_DELL_WMI) += dell-wmi.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o +obj-$(CONFIG_ACERHDF) += acerhdf.o obj-$(CONFIG_HP_WMI) += hp-wmi.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_SONY_LAPTOP) += sony-laptop.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 09a503e5da6..be2fd6f9163 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -958,12 +958,12 @@ static void acer_rfkill_update(struct work_struct *ignored) status = get_u32(&state, ACER_CAP_WIRELESS); if (ACPI_SUCCESS(status)) - rfkill_set_sw_state(wireless_rfkill, !!state); + rfkill_set_sw_state(wireless_rfkill, !state); if (has_cap(ACER_CAP_BLUETOOTH)) { status = get_u32(&state, ACER_CAP_BLUETOOTH); if (ACPI_SUCCESS(status)) - rfkill_set_sw_state(bluetooth_rfkill, !!state); + rfkill_set_sw_state(bluetooth_rfkill, !state); } schedule_delayed_work(&acer_rfkill_work, round_jiffies_relative(HZ)); diff --git a/drivers/platform/x86/acerhdf.c b/drivers/platform/x86/acerhdf.c new file mode 100644 index 00000000000..bdfee177eef --- /dev/null +++ b/drivers/platform/x86/acerhdf.c @@ -0,0 +1,602 @@ +/* + * acerhdf - A driver which monitors the temperature + * of the aspire one netbook, turns on/off the fan + * as soon as the upper/lower threshold is reached. + * + * (C) 2009 - Peter Feuerer peter (a) piie.net + * http://piie.net + * 2009 Borislav Petkov <petkovbb@gmail.com> + * + * Inspired by and many thanks to: + * o acerfand - Rachel Greenham + * o acer_ec.pl - Michael Kurz michi.kurz (at) googlemail.com + * - Petr Tomasek tomasek (#) etf,cuni,cz + * - Carlos Corbacho cathectic (at) gmail.com + * o lkml - Matthew Garrett + * - Borislav Petkov + * - Andreas Mohr + * + * 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 + */ + +#define pr_fmt(fmt) "acerhdf: " fmt + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/dmi.h> +#include <acpi/acpi_drivers.h> +#include <linux/sched.h> +#include <linux/thermal.h> +#include <linux/platform_device.h> + +/* + * The driver is started with "kernel mode off" by default. That means, the BIOS + * is still in control of the fan. In this mode the driver allows to read the + * temperature of the cpu and a userspace tool may take over control of the fan. + * If the driver is switched to "kernel mode" (e.g. via module parameter) the + * driver is in full control of the fan. If you want the module to be started in + * kernel mode by default, define the following: + */ +#undef START_IN_KERNEL_MODE + +#define DRV_VER "0.5.13" + +/* + * According to the Atom N270 datasheet, + * (http://download.intel.com/design/processor/datashts/320032.pdf) the + * CPU's optimal operating limits denoted in junction temperature as + * 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_FAN_OFF 0 +#define ACERHDF_FAN_AUTO 1 + +/* + * 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 + +/* + * Maximum interval between two temperature checks is 15 seconds, as the die + * can get hot really fast under heavy load (plus we shouldn't forget about + * possible impact of _external_ aggressive sources such as heaters, sun etc.) + */ +#define ACERHDF_MAX_INTERVAL 15 + +#ifdef START_IN_KERNEL_MODE +static int kernelmode = 1; +#else +static int kernelmode; +#endif + +static unsigned int interval = 10; +static unsigned int fanon = 63; +static unsigned int fanoff = 58; +static unsigned int verbose; +static unsigned int fanstate = ACERHDF_FAN_AUTO; +static char force_bios[16]; +static unsigned int prev_interval; +struct thermal_zone_device *thz_dev; +struct thermal_cooling_device *cl_dev; +struct platform_device *acerhdf_dev; + +module_param(kernelmode, uint, 0); +MODULE_PARM_DESC(kernelmode, "Kernel mode fan control on / off"); +module_param(interval, uint, 0600); +MODULE_PARM_DESC(interval, "Polling interval of temperature check"); +module_param(fanon, uint, 0600); +MODULE_PARM_DESC(fanon, "Turn the fan on above this temperature"); +module_param(fanoff, uint, 0600); +MODULE_PARM_DESC(fanoff, "Turn the fan off below this temperature"); +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"); + +/* BIOS settings */ +struct bios_settings_t { + const char *vendor; + const char *version; + unsigned char fanreg; + unsigned char tempreg; + unsigned char fancmd[2]; /* fan off and auto commands */ +}; + +/* 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} } +}; + +static const struct bios_settings_t *bios_cfg __read_mostly; + + +static int acerhdf_get_temp(int *temp) +{ + u8 read_temp; + + if (ec_read(bios_cfg->tempreg, &read_temp)) + return -EINVAL; + + *temp = read_temp; + + return 0; +} + +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; + + return 0; +} + +static void acerhdf_change_fanstate(int state) +{ + unsigned char cmd; + + if (verbose) + pr_notice("fan %s\n", (state == ACERHDF_FAN_OFF) ? + "OFF" : "ON"); + + if ((state != ACERHDF_FAN_OFF) && (state != ACERHDF_FAN_AUTO)) { + pr_err("invalid fan state %d requested, setting to auto!\n", + state); + state = ACERHDF_FAN_AUTO; + } + + cmd = bios_cfg->fancmd[state]; + fanstate = state; + + ec_write(bios_cfg->fanreg, cmd); +} + +static void acerhdf_check_param(struct thermal_zone_device *thermal) +{ + if (fanon > ACERHDF_MAX_FANON) { + pr_err("fanon temperature too high, set to %d\n", + ACERHDF_MAX_FANON); + fanon = ACERHDF_MAX_FANON; + } + + if (kernelmode && prev_interval != interval) { + if (interval > ACERHDF_MAX_INTERVAL) { + pr_err("interval too high, set to %d\n", + ACERHDF_MAX_INTERVAL); + interval = ACERHDF_MAX_INTERVAL; + } + if (verbose) + pr_notice("interval changed to: %d\n", + interval); + thermal->polling_delay = interval*1000; + prev_interval = interval; + } +} + +/* + * This is the thermal zone callback which does the delayed polling of the fan + * state. We do check /sysfs-originating settings here in acerhdf_check_param() + * as late as the polling interval is since we can't do that in the respective + * accessors of the module parameters. + */ +static int acerhdf_get_ec_temp(struct thermal_zone_device *thermal, + unsigned long *t) +{ + int temp, err = 0; + + acerhdf_check_param(thermal); + + err = acerhdf_get_temp(&temp); + if (err) + return err; + + if (verbose) + pr_notice("temp %d\n", temp); + + *t = temp; + return 0; +} + +static int acerhdf_bind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + /* if the cooling device is the one from acerhdf bind it */ + if (cdev != cl_dev) + return 0; + + if (thermal_zone_bind_cooling_device(thermal, 0, cdev)) { + pr_err("error binding cooling dev\n"); + return -EINVAL; + } + return 0; +} + +static int acerhdf_unbind(struct thermal_zone_device *thermal, + struct thermal_cooling_device *cdev) +{ + if (cdev != cl_dev) + return 0; + + if (thermal_zone_unbind_cooling_device(thermal, 0, cdev)) { + pr_err("error unbinding cooling dev\n"); + return -EINVAL; + } + return 0; +} + +static inline void acerhdf_revert_to_bios_mode(void) +{ + acerhdf_change_fanstate(ACERHDF_FAN_AUTO); + kernelmode = 0; + if (thz_dev) + thz_dev->polling_delay = 0; + pr_notice("kernel mode fan control OFF\n"); +} +static inline void acerhdf_enable_kernelmode(void) +{ + kernelmode = 1; + + thz_dev->polling_delay = interval*1000; + thermal_zone_device_update(thz_dev); + pr_notice("kernel mode fan control ON\n"); +} + +static int acerhdf_get_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode *mode) +{ + if (verbose) + pr_notice("kernel mode fan control %d\n", kernelmode); + + *mode = (kernelmode) ? THERMAL_DEVICE_ENABLED + : THERMAL_DEVICE_DISABLED; + + return 0; +} + +/* + * set operation mode; + * enabled: the thermal layer of the kernel takes care about + * the temperature and the fan. + * disabled: the BIOS takes control of the fan. + */ +static int acerhdf_set_mode(struct thermal_zone_device *thermal, + enum thermal_device_mode mode) +{ + if (mode == THERMAL_DEVICE_DISABLED && kernelmode) + acerhdf_revert_to_bios_mode(); + else if (mode == THERMAL_DEVICE_ENABLED && !kernelmode) + acerhdf_enable_kernelmode(); + + return 0; +} + +static int acerhdf_get_trip_type(struct thermal_zone_device *thermal, int trip, + enum thermal_trip_type *type) +{ + if (trip == 0) + *type = THERMAL_TRIP_ACTIVE; + + return 0; +} + +static int acerhdf_get_trip_temp(struct thermal_zone_device *thermal, int trip, + unsigned long *temp) +{ + if (trip == 0) + *temp = fanon; + + return 0; +} + +static int acerhdf_get_crit_temp(struct thermal_zone_device *thermal, + unsigned long *temperature) +{ + *temperature = ACERHDF_TEMP_CRIT; + return 0; +} + +/* bind callback functions to thermalzone */ +struct thermal_zone_device_ops acerhdf_dev_ops = { + .bind = acerhdf_bind, + .unbind = acerhdf_unbind, + .get_temp = acerhdf_get_ec_temp, + .get_mode = acerhdf_get_mode, + .set_mode = acerhdf_set_mode, + .get_trip_type = acerhdf_get_trip_type, + .get_trip_temp = acerhdf_get_trip_temp, + .get_crit_temp = acerhdf_get_crit_temp, +}; + + +/* + * cooling device callback functions + * get maximal fan cooling state + */ +static int acerhdf_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = 1; + + return 0; +} + +static int acerhdf_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + int err = 0, tmp; + + err = acerhdf_get_fanstate(&tmp); + if (err) + return err; + + *state = (tmp == ACERHDF_FAN_AUTO) ? 1 : 0; + return 0; +} + +/* change current fan state - is overwritten when running in kernel mode */ +static int acerhdf_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + int cur_temp, cur_state, err = 0; + + if (!kernelmode) + return 0; + + err = acerhdf_get_temp(&cur_temp); + if (err) { + pr_err("error reading temperature, hand off control to BIOS\n"); + goto err_out; + } + + err = acerhdf_get_fanstate(&cur_state); + if (err) { + pr_err("error reading fan state, hand off control to BIOS\n"); + goto err_out; + } + + if (state == 0) { + /* turn fan off only if below fanoff temperature */ + if ((cur_state == ACERHDF_FAN_AUTO) && + (cur_temp < fanoff)) + acerhdf_change_fanstate(ACERHDF_FAN_OFF); + } else { + if (cur_state == ACERHDF_FAN_OFF) + acerhdf_change_fanstate(ACERHDF_FAN_AUTO); + } + return 0; + +err_out: + acerhdf_revert_to_bios_mode(); + return -EINVAL; +} + +/* bind fan callbacks to fan device */ +struct thermal_cooling_device_ops acerhdf_cooling_ops = { + .get_max_state = acerhdf_get_max_state, + .get_cur_state = acerhdf_get_cur_state, + .set_cur_state = acerhdf_set_cur_state, +}; + +/* suspend / resume functionality */ +static int acerhdf_suspend(struct platform_device *dev, pm_message_t state) +{ + if (kernelmode) + acerhdf_change_fanstate(ACERHDF_FAN_AUTO); + + if (verbose) + pr_notice("going suspend\n"); + + 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; +} + +static int acerhdf_remove(struct platform_device *device) +{ + return 0; +} + +struct platform_driver acerhdf_drv = { + .driver = { + .name = "acerhdf", + .owner = THIS_MODULE, + }, + .probe = acerhdf_probe, + .remove = acerhdf_remove, + .suspend = acerhdf_suspend, + .resume = acerhdf_resume, +}; + + +/* check hardware */ +static int acerhdf_check_hardware(void) +{ + char const *vendor, *version, *product; + int i; + + /* 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); + version = force_bios; + kernelmode = 0; + } + + 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) && + !strcmp(bios_tbl[i].version, version)) { + bios_cfg = &bios_tbl[i]; + break; + } + } + + if (!bios_cfg) { + pr_err("unknown (unsupported) BIOS version %s/%s, " + "please report, aborting!\n", vendor, version); + return -EINVAL; + } + + /* + * if started with kernel mode off, prevent the kernel from switching + * off the fan + */ + if (!kernelmode) { + pr_notice("Fan control off, to enable do:\n"); + pr_notice("echo -n \"enabled\" > " + "/sys/class/thermal/thermal_zone0/mode\n"); + } + + return 0; +} + +static int acerhdf_register_platform(void) +{ + int err = 0; + + err = platform_driver_register(&acerhdf_drv); + if (err) + return err; + + acerhdf_dev = platform_device_alloc("acerhdf", -1); + platform_device_add(acerhdf_dev); + + return 0; +} + +static void acerhdf_unregister_platform(void) +{ + if (!acerhdf_dev) + return; + + platform_device_del(acerhdf_dev); + platform_driver_unregister(&acerhdf_drv); +} + +static int acerhdf_register_thermal(void) +{ + cl_dev = thermal_cooling_device_register("acerhdf-fan", NULL, + &acerhdf_cooling_ops); + + if (IS_ERR(cl_dev)) + return -EINVAL; + + thz_dev = thermal_zone_device_register("acerhdf", 1, NULL, + &acerhdf_dev_ops, 0, 0, 0, + (kernelmode) ? interval*1000 : 0); + if (IS_ERR(thz_dev)) + return -EINVAL; + + return 0; +} + +static void acerhdf_unregister_thermal(void) +{ + if (cl_dev) { + thermal_cooling_device_unregister(cl_dev); + cl_dev = NULL; + } + + if (thz_dev) { + thermal_zone_device_unregister(thz_dev); + thz_dev = NULL; + } +} + +static int __init acerhdf_init(void) +{ + int err = 0; + + err = acerhdf_check_hardware(); + if (err) + goto out_err; + + err = acerhdf_register_platform(); + if (err) + goto err_unreg; + + err = acerhdf_register_thermal(); + if (err) + goto err_unreg; + + return 0; + +err_unreg: + acerhdf_unregister_thermal(); + acerhdf_unregister_platform(); + +out_err: + return -ENODEV; +} + +static void __exit acerhdf_exit(void) +{ + acerhdf_change_fanstate(ACERHDF_FAN_AUTO); + acerhdf_unregister_thermal(); + acerhdf_unregister_platform(); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Peter Feuerer"); +MODULE_DESCRIPTION("Aspire One temperature and fan driver"); +MODULE_ALIAS("dmi:*:*Acer*:*:"); +MODULE_ALIAS("dmi:*:*Gateway*:*:"); +MODULE_ALIAS("dmi:*:*Packard Bell*:*:"); + +module_init(acerhdf_init); +module_exit(acerhdf_exit); diff --git a/drivers/platform/x86/asus-laptop.c b/drivers/platform/x86/asus-laptop.c index bfc1a8892a3..db657bbeec9 100644 --- a/drivers/platform/x86/asus-laptop.c +++ b/drivers/platform/x86/asus-laptop.c @@ -33,6 +33,8 @@ * Sam Lin - GPS support */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> @@ -53,9 +55,10 @@ #define ASUS_HOTK_NAME "Asus Laptop Support" #define ASUS_HOTK_CLASS "hotkey" #define ASUS_HOTK_DEVICE_NAME "Hotkey" -#define ASUS_HOTK_FILE "asus-laptop" +#define ASUS_HOTK_FILE KBUILD_MODNAME #define ASUS_HOTK_PREFIX "\\_SB.ATKD." + /* * Some events we use, same for all Asus */ @@ -207,13 +210,17 @@ MODULE_DEVICE_TABLE(acpi, asus_device_ids); static int asus_hotk_add(struct acpi_device *device); static int asus_hotk_remove(struct acpi_device *device, int type); +static void asus_hotk_notify(struct acpi_device *device, u32 event); + static struct acpi_driver asus_hotk_driver = { .name = ASUS_HOTK_NAME, .class = ASUS_HOTK_CLASS, .ids = asus_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = asus_hotk_add, .remove = asus_hotk_remove, + .notify = asus_hotk_notify, }, }; @@ -323,7 +330,7 @@ static int read_wireless_status(int mask) rv = acpi_evaluate_integer(wireless_status_handle, NULL, NULL, &status); if (ACPI_FAILURE(rv)) - printk(ASUS_WARNING "Error reading Wireless status\n"); + pr_warning("Error reading Wireless status\n"); else return (status & mask) ? 1 : 0; @@ -337,7 +344,7 @@ static int read_gps_status(void) rv = acpi_evaluate_integer(gps_status_handle, NULL, NULL, &status); if (ACPI_FAILURE(rv)) - printk(ASUS_WARNING "Error reading GPS status\n"); + pr_warning("Error reading GPS status\n"); else return status ? 1 : 0; @@ -377,7 +384,7 @@ static void write_status(acpi_handle handle, int out, int mask) } if (write_acpi_int(handle, NULL, out, NULL)) - printk(ASUS_WARNING " write failed %x\n", mask); + pr_warning(" write failed %x\n", mask); } /* /sys/class/led handlers */ @@ -420,7 +427,7 @@ static int set_lcd_state(int value) NULL, NULL, NULL); if (ACPI_FAILURE(status)) - printk(ASUS_WARNING "Error switching LCD\n"); + pr_warning("Error switching LCD\n"); } write_status(NULL, lcd, LCD_ON); @@ -444,7 +451,7 @@ static int read_brightness(struct backlight_device *bd) rv = acpi_evaluate_integer(brightness_get_handle, NULL, NULL, &value); if (ACPI_FAILURE(rv)) - printk(ASUS_WARNING "Error reading brightness\n"); + pr_warning("Error reading brightness\n"); return value; } @@ -457,7 +464,7 @@ static int set_brightness(struct backlight_device *bd, int value) /* 0 <= value <= 15 */ if (write_acpi_int(brightness_set_handle, NULL, value, NULL)) { - printk(ASUS_WARNING "Error changing brightness\n"); + pr_warning("Error changing brightness\n"); ret = -EIO; } @@ -587,7 +594,7 @@ static ssize_t store_ledd(struct device *dev, struct device_attribute *attr, rv = parse_arg(buf, count, &value); if (rv > 0) { if (write_acpi_int(ledd_set_handle, NULL, value, NULL)) - printk(ASUS_WARNING "LED display write failed\n"); + pr_warning("LED display write failed\n"); else hotk->ledd_status = (u32) value; } @@ -632,7 +639,7 @@ static void set_display(int value) { /* no sanity check needed for now */ if (write_acpi_int(display_set_handle, NULL, value, NULL)) - printk(ASUS_WARNING "Error setting display\n"); + pr_warning("Error setting display\n"); return; } @@ -647,7 +654,7 @@ static int read_display(void) rv = acpi_evaluate_integer(display_get_handle, NULL, NULL, &value); if (ACPI_FAILURE(rv)) - printk(ASUS_WARNING "Error reading display status\n"); + pr_warning("Error reading display status\n"); } value &= 0x0F; /* needed for some models, shouldn't hurt others */ @@ -689,7 +696,7 @@ static ssize_t store_disp(struct device *dev, struct device_attribute *attr, static void set_light_sens_switch(int value) { if (write_acpi_int(ls_switch_handle, NULL, value, NULL)) - printk(ASUS_WARNING "Error setting light sensor switch\n"); + pr_warning("Error setting light sensor switch\n"); hotk->light_switch = value; } @@ -714,7 +721,7 @@ static ssize_t store_lssw(struct device *dev, struct device_attribute *attr, static void set_light_sens_level(int value) { if (write_acpi_int(ls_level_handle, NULL, value, NULL)) - printk(ASUS_WARNING "Error setting light sensor level\n"); + pr_warning("Error setting light sensor level\n"); hotk->light_level = value; } @@ -812,7 +819,7 @@ static int asus_setkeycode(struct input_dev *dev, int scancode, int keycode) return -EINVAL; } -static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) +static void asus_hotk_notify(struct acpi_device *device, u32 event) { static struct key_entry *key; u16 count; @@ -975,11 +982,11 @@ static int asus_hotk_get_info(void) */ status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info); if (ACPI_FAILURE(status)) - printk(ASUS_WARNING "Couldn't get the DSDT table header\n"); + pr_warning("Couldn't get the DSDT table header\n"); /* We have to write 0 on init this far for all ASUS models */ if (write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { - printk(ASUS_ERR "Hotkey initialization failed\n"); + pr_err("Hotkey initialization failed\n"); return -ENODEV; } @@ -987,9 +994,9 @@ static int asus_hotk_get_info(void) status = acpi_evaluate_integer(hotk->handle, "BSTS", NULL, &bsts_result); if (ACPI_FAILURE(status)) - printk(ASUS_WARNING "Error calling BSTS\n"); + pr_warning("Error calling BSTS\n"); else if (bsts_result) - printk(ASUS_NOTICE "BSTS called, 0x%02x returned\n", + pr_notice("BSTS called, 0x%02x returned\n", (uint) bsts_result); /* This too ... */ @@ -1020,7 +1027,7 @@ static int asus_hotk_get_info(void) return -ENOMEM; if (*string) - printk(ASUS_NOTICE " %s model detected\n", string); + pr_notice(" %s model detected\n", string); ASUS_HANDLE_INIT(mled_set); ASUS_HANDLE_INIT(tled_set); @@ -1077,7 +1084,7 @@ static int asus_input_init(void) hotk->inputdev = input_allocate_device(); if (!hotk->inputdev) { - printk(ASUS_INFO "Unable to allocate input device\n"); + pr_info("Unable to allocate input device\n"); return 0; } hotk->inputdev->name = "Asus Laptop extra buttons"; @@ -1096,7 +1103,7 @@ static int asus_input_init(void) } result = input_register_device(hotk->inputdev); if (result) { - printk(ASUS_INFO "Unable to register input device\n"); + pr_info("Unable to register input device\n"); input_free_device(hotk->inputdev); } return result; @@ -1113,7 +1120,7 @@ static int asus_hotk_check(void) if (hotk->device->status.present) { result = asus_hotk_get_info(); } else { - printk(ASUS_ERR "Hotkey device not present, aborting\n"); + pr_err("Hotkey device not present, aborting\n"); return -EINVAL; } @@ -1124,13 +1131,12 @@ static int asus_hotk_found; static int asus_hotk_add(struct acpi_device *device) { - acpi_status status = AE_OK; int result; if (!device) return -EINVAL; - printk(ASUS_NOTICE "Asus Laptop Support version %s\n", + pr_notice("Asus Laptop Support version %s\n", ASUS_LAPTOP_VERSION); hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL); @@ -1149,15 +1155,6 @@ static int asus_hotk_add(struct acpi_device *device) asus_hotk_add_fs(); - /* - * We install the handler, it will receive the hotk in parameter, so, we - * could add other data to the hotk struct - */ - status = acpi_install_notify_handler(hotk->handle, ACPI_ALL_NOTIFY, - asus_hotk_notify, hotk); - if (ACPI_FAILURE(status)) - printk(ASUS_ERR "Error installing notify handler\n"); - asus_hotk_found = 1; /* WLED and BLED are on by default */ @@ -1198,16 +1195,9 @@ end: static int asus_hotk_remove(struct acpi_device *device, int type) { - acpi_status status = 0; - if (!device || !acpi_driver_data(device)) return -EINVAL; - status = acpi_remove_notify_handler(hotk->handle, ACPI_ALL_NOTIFY, - asus_hotk_notify); - if (ACPI_FAILURE(status)) - printk(ASUS_ERR "Error removing notify handler\n"); - kfree(hotk->name); kfree(hotk); @@ -1260,8 +1250,7 @@ static int asus_backlight_init(struct device *dev) bd = backlight_device_register(ASUS_HOTK_FILE, dev, NULL, &asusbl_ops); if (IS_ERR(bd)) { - printk(ASUS_ERR - "Could not register asus backlight device\n"); + pr_err("Could not register asus backlight device\n"); asus_backlight_device = NULL; return PTR_ERR(bd); } @@ -1334,7 +1323,6 @@ out: static int __init asus_laptop_init(void) { - struct device *dev; int result; if (acpi_disabled) @@ -1356,24 +1344,10 @@ static int __init asus_laptop_init(void) return -ENODEV; } - dev = acpi_get_physical_device(hotk->device->handle); - - if (!acpi_video_backlight_support()) { - result = asus_backlight_init(dev); - if (result) - goto fail_backlight; - } else - printk(ASUS_INFO "Brightness ignored, must be controlled by " - "ACPI video driver\n"); - result = asus_input_init(); if (result) goto fail_input; - result = asus_led_init(dev); - if (result) - goto fail_led; - /* Register platform stuff */ result = platform_driver_register(&asuspf_driver); if (result) @@ -1394,8 +1368,27 @@ static int __init asus_laptop_init(void) if (result) goto fail_sysfs; + result = asus_led_init(&asuspf_device->dev); + if (result) + goto fail_led; + + if (!acpi_video_backlight_support()) { + result = asus_backlight_init(&asuspf_device->dev); + if (result) + goto fail_backlight; + } else + pr_info("Brightness ignored, must be controlled by " + "ACPI video driver\n"); + return 0; +fail_backlight: + asus_led_exit(); + +fail_led: + sysfs_remove_group(&asuspf_device->dev.kobj, + &asuspf_attribute_group); + fail_sysfs: platform_device_del(asuspf_device); @@ -1406,15 +1399,9 @@ fail_platform_device1: platform_driver_unregister(&asuspf_driver); fail_platform_driver: - asus_led_exit(); - -fail_led: asus_input_exit(); fail_input: - asus_backlight_exit(); - -fail_backlight: return result; } diff --git a/drivers/platform/x86/asus_acpi.c b/drivers/platform/x86/asus_acpi.c index ba1f7497e4b..ddf5240ade8 100644 --- a/drivers/platform/x86/asus_acpi.c +++ b/drivers/platform/x86/asus_acpi.c @@ -455,6 +455,8 @@ static struct asus_hotk *hotk; */ static int asus_hotk_add(struct acpi_device *device); static int asus_hotk_remove(struct acpi_device *device, int type); +static void asus_hotk_notify(struct acpi_device *device, u32 event); + static const struct acpi_device_id asus_device_ids[] = { {"ATK0100", 0}, {"", 0}, @@ -465,9 +467,11 @@ static struct acpi_driver asus_hotk_driver = { .name = "asus_acpi", .class = ACPI_HOTK_CLASS, .ids = asus_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = asus_hotk_add, .remove = asus_hotk_remove, + .notify = asus_hotk_notify, }, }; @@ -1101,12 +1105,20 @@ static int asus_hotk_remove_fs(struct acpi_device *device) return 0; } -static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) +static void asus_hotk_notify(struct acpi_device *device, u32 event) { /* TODO Find a better way to handle events count. */ if (!hotk) return; + /* + * The BIOS *should* be sending us device events, but apparently + * Asus uses system events instead, so just ignore any device + * events we get. + */ + if (event > ACPI_MAX_SYS_NOTIFY) + return; + if ((event & ~((u32) BR_UP)) < 16) hotk->brightness = (event & ~((u32) BR_UP)); else if ((event & ~((u32) BR_DOWN)) < 16) @@ -1346,15 +1358,6 @@ static int asus_hotk_add(struct acpi_device *device) if (result) goto end; - /* - * We install the handler, it will receive the hotk in parameter, so, we - * could add other data to the hotk struct - */ - status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, - asus_hotk_notify, hotk); - if (ACPI_FAILURE(status)) - printk(KERN_ERR " Error installing notify handler\n"); - /* For laptops without GPLV: init the hotk->brightness value */ if ((!hotk->methods->brightness_get) && (!hotk->methods->brightness_status) @@ -1389,16 +1392,9 @@ end: static int asus_hotk_remove(struct acpi_device *device, int type) { - acpi_status status = 0; - if (!device || !acpi_driver_data(device)) return -EINVAL; - status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, - asus_hotk_notify); - if (ACPI_FAILURE(status)) - printk(KERN_ERR "Asus ACPI: Error removing notify handler\n"); - asus_hotk_remove_fs(device); kfree(hotk); diff --git a/drivers/platform/x86/dell-wmi.c b/drivers/platform/x86/dell-wmi.c index 2fab9416214..0f900cc9fa7 100644 --- a/drivers/platform/x86/dell-wmi.c +++ b/drivers/platform/x86/dell-wmi.c @@ -46,10 +46,53 @@ struct key_entry { u16 keycode; }; -enum { KE_KEY, KE_SW, KE_END }; +enum { KE_KEY, KE_SW, KE_IGNORE, KE_END }; + +/* + * Certain keys are flagged as KE_IGNORE. All of these are either + * notifications (rather than requests for change) or are also sent + * via the keyboard controller so should not be sent again. + */ static struct key_entry dell_wmi_keymap[] = { {KE_KEY, 0xe045, KEY_PROG1}, + {KE_KEY, 0xe009, KEY_EJECTCD}, + + /* These also contain the brightness level at offset 6 */ + {KE_KEY, 0xe006, KEY_BRIGHTNESSUP}, + {KE_KEY, 0xe005, KEY_BRIGHTNESSDOWN}, + + /* Battery health status button */ + {KE_KEY, 0xe007, KEY_BATTERY}, + + /* This is actually for all radios. Although physically a + * switch, the notification does not provide an indication of + * state and so it should be reported as a key */ + {KE_KEY, 0xe008, KEY_WLAN}, + + /* The next device is at offset 6, the active devices are at + offset 8 and the attached devices at offset 10 */ + {KE_KEY, 0xe00b, KEY_DISPLAYTOGGLE}, + + {KE_IGNORE, 0xe00c, KEY_KBDILLUMTOGGLE}, + + /* BIOS error detected */ + {KE_IGNORE, 0xe00d, KEY_RESERVED}, + + /* Wifi Catcher */ + {KE_KEY, 0xe011, KEY_PROG2}, + + /* Ambient light sensor toggle */ + {KE_IGNORE, 0xe013, KEY_RESERVED}, + + {KE_IGNORE, 0xe020, KEY_MUTE}, + {KE_IGNORE, 0xe02e, KEY_VOLUMEDOWN}, + {KE_IGNORE, 0xe030, KEY_VOLUMEUP}, + {KE_IGNORE, 0xe033, KEY_KBDILLUMUP}, + {KE_IGNORE, 0xe034, KEY_KBDILLUMDOWN}, + {KE_IGNORE, 0xe03a, KEY_CAPSLOCK}, + {KE_IGNORE, 0xe045, KEY_NUMLOCK}, + {KE_IGNORE, 0xe046, KEY_SCROLLLOCK}, {KE_END, 0} }; @@ -122,15 +165,20 @@ static void dell_wmi_notify(u32 value, void *context) if (obj && obj->type == ACPI_TYPE_BUFFER) { int *buffer = (int *)obj->buffer.pointer; - key = dell_wmi_get_entry_by_scancode(buffer[1]); + /* + * The upper bytes of the event may contain + * additional information, so mask them off for the + * scancode lookup + */ + key = dell_wmi_get_entry_by_scancode(buffer[1] & 0xFFFF); if (key) { input_report_key(dell_wmi_input_dev, key->keycode, 1); input_sync(dell_wmi_input_dev); input_report_key(dell_wmi_input_dev, key->keycode, 0); input_sync(dell_wmi_input_dev); - } else + } else if (buffer[1] & 0xFFFF) printk(KERN_INFO "dell-wmi: Unknown key %x pressed\n", - buffer[1]); + buffer[1] & 0xFFFF); } } diff --git a/drivers/platform/x86/eeepc-laptop.c b/drivers/platform/x86/eeepc-laptop.c index 03bf522bd7a..4207b26ff99 100644 --- a/drivers/platform/x86/eeepc-laptop.c +++ b/drivers/platform/x86/eeepc-laptop.c @@ -62,7 +62,10 @@ enum { DISABLE_ASL_GPS = 0x0020, DISABLE_ASL_DISPLAYSWITCH = 0x0040, DISABLE_ASL_MODEM = 0x0080, - DISABLE_ASL_CARDREADER = 0x0100 + DISABLE_ASL_CARDREADER = 0x0100, + DISABLE_ASL_3G = 0x0200, + DISABLE_ASL_WIMAX = 0x0400, + DISABLE_ASL_HWCF = 0x0800 }; enum { @@ -87,7 +90,13 @@ enum { CM_ASL_USBPORT3, CM_ASL_MODEM, CM_ASL_CARDREADER, - CM_ASL_LID + CM_ASL_3G, + CM_ASL_WIMAX, + CM_ASL_HWCF, + CM_ASL_LID, + CM_ASL_TYPE, + CM_ASL_PANELPOWER, /*P901*/ + CM_ASL_TPD }; static const char *cm_getv[] = { @@ -96,7 +105,8 @@ static const char *cm_getv[] = { NULL, "PBLG", NULL, NULL, "CFVG", NULL, NULL, NULL, "USBG", NULL, NULL, "MODG", - "CRDG", "LIDG" + "CRDG", "M3GG", "WIMG", "HWCF", + "LIDG", "TYPE", "PBPG", "TPDG" }; static const char *cm_setv[] = { @@ -105,7 +115,8 @@ static const char *cm_setv[] = { "SDSP", "PBLS", "HDPS", NULL, "CFVS", NULL, NULL, NULL, "USBG", NULL, NULL, "MODS", - "CRDS", NULL + "CRDS", "M3GS", "WIMS", NULL, + NULL, NULL, "PBPS", "TPDS" }; #define EEEPC_EC "\\_SB.PCI0.SBRG.EC0." @@ -180,6 +191,8 @@ 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[] = { {EEEPC_HOTK_HID, 0}, @@ -191,9 +204,12 @@ static struct acpi_driver eeepc_hotk_driver = { .name = EEEPC_HOTK_NAME, .class = EEEPC_HOTK_CLASS, .ids = eeepc_device_ids, + .flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS, .ops = { .add = eeepc_hotk_add, .remove = eeepc_hotk_remove, + .resume = eeepc_hotk_resume, + .notify = eeepc_hotk_notify, }, }; @@ -316,6 +332,15 @@ static const struct rfkill_ops eeepc_rfkill_ops = { .set_block = eeepc_rfkill_set, }; +static void __init 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); +} + /* * Sys helpers */ @@ -367,13 +392,88 @@ static ssize_t show_sys_acpi(int cm, char *buf) EEEPC_CREATE_DEVICE_ATTR(camera, CM_ASL_CAMERA); EEEPC_CREATE_DEVICE_ATTR(cardr, CM_ASL_CARDREADER); EEEPC_CREATE_DEVICE_ATTR(disp, CM_ASL_DISPLAYSWITCH); -EEEPC_CREATE_DEVICE_ATTR(cpufv, CM_ASL_CPUFV); + +struct eeepc_cpufv { + int num; + int cur; +}; + +static int get_cpufv(struct eeepc_cpufv *c) +{ + c->cur = get_acpi(CM_ASL_CPUFV); + c->num = (c->cur >> 8) & 0xff; + c->cur &= 0xff; + if (c->cur < 0 || c->num <= 0 || c->num > 12) + return -ENODEV; + return 0; +} + +static ssize_t show_available_cpufv(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct eeepc_cpufv c; + int i; + ssize_t len = 0; + + if (get_cpufv(&c)) + return -ENODEV; + for (i = 0; i < c.num; i++) + len += sprintf(buf + len, "%d ", i); + len += sprintf(buf + len, "\n"); + return len; +} + +static ssize_t show_cpufv(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct eeepc_cpufv c; + + if (get_cpufv(&c)) + return -ENODEV; + return sprintf(buf, "%#x\n", (c.num << 8) | c.cur); +} + +static ssize_t store_cpufv(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct eeepc_cpufv c; + int rv, value; + + if (get_cpufv(&c)) + return -ENODEV; + rv = parse_arg(buf, count, &value); + if (rv < 0) + return rv; + if (!rv || value < 0 || value >= c.num) + return -EINVAL; + set_acpi(CM_ASL_CPUFV, value); + return rv; +} + +static struct device_attribute dev_attr_cpufv = { + .attr = { + .name = "cpufv", + .mode = 0644 }, + .show = show_cpufv, + .store = store_cpufv +}; + +static struct device_attribute dev_attr_available_cpufv = { + .attr = { + .name = "available_cpufv", + .mode = 0444 }, + .show = show_available_cpufv +}; static struct attribute *platform_attributes[] = { &dev_attr_camera.attr, &dev_attr_cardr.attr, &dev_attr_disp.attr, &dev_attr_cpufv.attr, + &dev_attr_available_cpufv.attr, NULL }; @@ -512,15 +612,12 @@ static int notify_brn(void) return -1; } -static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) +static void eeepc_rfkill_hotplug(void) { struct pci_dev *dev; struct pci_bus *bus = pci_find_bus(0, 1); bool blocked; - if (event != ACPI_NOTIFY_BUS_CHECK) - return; - if (!bus) { printk(EEEPC_WARNING "Unable to find PCI bus 1?\n"); return; @@ -551,7 +648,15 @@ static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, blocked); } -static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) +static void eeepc_rfkill_notify(acpi_handle handle, u32 event, void *data) +{ + if (event != ACPI_NOTIFY_BUS_CHECK) + return; + + eeepc_rfkill_hotplug(); +} + +static void eeepc_hotk_notify(struct acpi_device *device, u32 event) { static struct key_entry *key; u16 count; @@ -559,6 +664,8 @@ static void eeepc_hotk_notify(acpi_handle handle, u32 event, void *data) if (!ehotk) return; + if (event > ACPI_MAX_SYS_NOTIFY) + return; if (event >= NOTIFY_BRN_MIN && event <= NOTIFY_BRN_MAX) brn = notify_brn(); count = ehotk->event_count[event % 128]++; @@ -639,7 +746,6 @@ static void eeepc_unregister_rfkill_notifier(char *node) static int eeepc_hotk_add(struct acpi_device *device) { - acpi_status status = AE_OK; int result; if (!device) @@ -657,10 +763,6 @@ static int eeepc_hotk_add(struct acpi_device *device) result = eeepc_hotk_check(); if (result) goto ehotk_fail; - status = acpi_install_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, - eeepc_hotk_notify, ehotk); - if (ACPI_FAILURE(status)) - printk(EEEPC_ERR "Error installing notify handler\n"); eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P6"); eeepc_register_rfkill_notifier("\\_SB.PCI0.P0P7"); @@ -675,8 +777,8 @@ static int eeepc_hotk_add(struct acpi_device *device) if (!ehotk->eeepc_wlan_rfkill) goto wlan_fail; - rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, - get_acpi(CM_ASL_WLAN) != 1); + rfkill_init_sw_state(ehotk->eeepc_wlan_rfkill, + get_acpi(CM_ASL_WLAN) != 1); result = rfkill_register(ehotk->eeepc_wlan_rfkill); if (result) goto wlan_fail; @@ -693,8 +795,8 @@ static int eeepc_hotk_add(struct acpi_device *device) if (!ehotk->eeepc_bluetooth_rfkill) goto bluetooth_fail; - rfkill_set_sw_state(ehotk->eeepc_bluetooth_rfkill, - get_acpi(CM_ASL_BLUETOOTH) != 1); + rfkill_init_sw_state(ehotk->eeepc_bluetooth_rfkill, + get_acpi(CM_ASL_BLUETOOTH) != 1); result = rfkill_register(ehotk->eeepc_bluetooth_rfkill); if (result) goto bluetooth_fail; @@ -718,14 +820,8 @@ static int eeepc_hotk_add(struct acpi_device *device) static int eeepc_hotk_remove(struct acpi_device *device, int type) { - acpi_status status = 0; - if (!device || !acpi_driver_data(device)) return -EINVAL; - status = acpi_remove_notify_handler(ehotk->handle, ACPI_SYSTEM_NOTIFY, - eeepc_hotk_notify); - if (ACPI_FAILURE(status)) - printk(EEEPC_ERR "Error removing notify handler\n"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P6"); eeepc_unregister_rfkill_notifier("\\_SB.PCI0.P0P7"); @@ -734,6 +830,33 @@ static int eeepc_hotk_remove(struct acpi_device *device, int type) return 0; } +static int eeepc_hotk_resume(struct acpi_device *device) +{ + if (ehotk->eeepc_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. + */ + wlan = get_acpi(CM_ASL_WLAN); + set_acpi(CM_ASL_WLAN, wlan); + + rfkill_set_sw_state(ehotk->eeepc_wlan_rfkill, + wlan != 1); + + eeepc_rfkill_hotplug(); + } + + if (ehotk->eeepc_bluetooth_rfkill) + rfkill_set_sw_state(ehotk->eeepc_bluetooth_rfkill, + get_acpi(CM_ASL_BLUETOOTH) != 1); + + return 0; +} + /* * Hwmon */ @@ -955,6 +1078,9 @@ static int __init eeepc_laptop_init(void) result = eeepc_hwmon_init(dev); if (result) goto fail_hwmon; + + eeepc_enable_camera(); + /* Register platform stuff */ result = platform_driver_register(&platform_driver); if (result) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index 16fffe44e33..4ac2311c00a 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -47,7 +47,7 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4"); #define HPWMI_DISPLAY_QUERY 0x1 #define HPWMI_HDDTEMP_QUERY 0x2 #define HPWMI_ALS_QUERY 0x3 -#define HPWMI_DOCK_QUERY 0x4 +#define HPWMI_HARDWARE_QUERY 0x4 #define HPWMI_WIRELESS_QUERY 0x5 #define HPWMI_HOTKEY_QUERY 0xc @@ -75,10 +75,9 @@ struct key_entry { u16 keycode; }; -enum { KE_KEY, KE_SW, KE_END }; +enum { KE_KEY, KE_END }; static struct key_entry hp_wmi_keymap[] = { - {KE_SW, 0x01, SW_DOCK}, {KE_KEY, 0x02, KEY_BRIGHTNESSUP}, {KE_KEY, 0x03, KEY_BRIGHTNESSDOWN}, {KE_KEY, 0x20e6, KEY_PROG1}, @@ -151,7 +150,22 @@ static int hp_wmi_als_state(void) static int hp_wmi_dock_state(void) { - return hp_wmi_perform_query(HPWMI_DOCK_QUERY, 0, 0); + int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); + + if (ret < 0) + return ret; + + return ret & 0x1; +} + +static int hp_wmi_tablet_state(void) +{ + int ret = hp_wmi_perform_query(HPWMI_HARDWARE_QUERY, 0, 0); + + if (ret < 0) + return ret; + + return (ret & 0x4) ? 1 : 0; } static int hp_wmi_set_block(void *data, bool blocked) @@ -232,6 +246,15 @@ static ssize_t show_dock(struct device *dev, struct device_attribute *attr, return sprintf(buf, "%d\n", value); } +static ssize_t show_tablet(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int value = hp_wmi_tablet_state(); + if (value < 0) + return -EINVAL; + return sprintf(buf, "%d\n", value); +} + static ssize_t set_als(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { @@ -244,6 +267,7 @@ static DEVICE_ATTR(display, S_IRUGO, show_display, NULL); static DEVICE_ATTR(hddtemp, S_IRUGO, show_hddtemp, NULL); static DEVICE_ATTR(als, S_IRUGO | S_IWUSR, show_als, set_als); static DEVICE_ATTR(dock, S_IRUGO, show_dock, NULL); +static DEVICE_ATTR(tablet, S_IRUGO, show_tablet, NULL); static struct key_entry *hp_wmi_get_entry_by_scancode(int code) { @@ -326,13 +350,13 @@ static void hp_wmi_notify(u32 value, void *context) key->keycode, 0); input_sync(hp_wmi_input_dev); break; - case KE_SW: - input_report_switch(hp_wmi_input_dev, - key->keycode, - hp_wmi_dock_state()); - input_sync(hp_wmi_input_dev); - break; } + } else if (eventcode == 0x1) { + input_report_switch(hp_wmi_input_dev, SW_DOCK, + hp_wmi_dock_state()); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); } else if (eventcode == 0x5) { if (wifi_rfkill) rfkill_set_sw_state(wifi_rfkill, @@ -369,18 +393,19 @@ static int __init hp_wmi_input_setup(void) set_bit(EV_KEY, hp_wmi_input_dev->evbit); set_bit(key->keycode, hp_wmi_input_dev->keybit); break; - case KE_SW: - set_bit(EV_SW, hp_wmi_input_dev->evbit); - set_bit(key->keycode, hp_wmi_input_dev->swbit); - - /* Set initial dock state */ - input_report_switch(hp_wmi_input_dev, key->keycode, - hp_wmi_dock_state()); - input_sync(hp_wmi_input_dev); - break; } } + set_bit(EV_SW, hp_wmi_input_dev->evbit); + set_bit(SW_DOCK, hp_wmi_input_dev->swbit); + set_bit(SW_TABLET_MODE, hp_wmi_input_dev->swbit); + + /* Set initial hardware state */ + input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); + err = input_register_device(hp_wmi_input_dev); if (err) { @@ -397,6 +422,7 @@ static void cleanup_sysfs(struct platform_device *device) device_remove_file(&device->dev, &dev_attr_hddtemp); device_remove_file(&device->dev, &dev_attr_als); device_remove_file(&device->dev, &dev_attr_dock); + device_remove_file(&device->dev, &dev_attr_tablet); } static int __init hp_wmi_bios_setup(struct platform_device *device) @@ -416,6 +442,9 @@ static int __init hp_wmi_bios_setup(struct platform_device *device) err = device_create_file(&device->dev, &dev_attr_dock); if (err) goto add_sysfs_error; + err = device_create_file(&device->dev, &dev_attr_tablet); + if (err) + goto add_sysfs_error; if (wireless & 0x1) { wifi_rfkill = rfkill_alloc("hp-wifi", &device->dev, @@ -485,23 +514,17 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device) static int hp_wmi_resume_handler(struct platform_device *device) { - struct key_entry *key; - /* - * Docking state may have changed while suspended, so trigger - * an input event for the current state. As this is a switch, + * Hardware state may have changed while suspended, so trigger + * input events for the current state. As this is a switch, * the input layer will only actually pass it on if the state * changed. */ - for (key = hp_wmi_keymap; key->type != KE_END; key++) { - switch (key->type) { - case KE_SW: - input_report_switch(hp_wmi_input_dev, key->keycode, - hp_wmi_dock_state()); - input_sync(hp_wmi_input_dev); - break; - } - } + + input_report_switch(hp_wmi_input_dev, SW_DOCK, hp_wmi_dock_state()); + input_report_switch(hp_wmi_input_dev, SW_TABLET_MODE, + hp_wmi_tablet_state()); + input_sync(hp_wmi_input_dev); return 0; } diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 86e958539f4..a463fd72c49 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 0x020300 +#define TPACPI_SYSFS_VERSION 0x020400 /* * Changelog: @@ -257,6 +257,8 @@ static struct { u32 wan:1; u32 uwb:1; u32 fan_ctrl_status_undef:1; + u32 second_fan:1; + u32 beep_needs_two_args:1; u32 input_device_registered:1; u32 platform_drv_registered:1; u32 platform_drv_attrs_registered:1; @@ -277,8 +279,10 @@ struct thinkpad_id_data { char *bios_version_str; /* Something like 1ZET51WW (1.03z) */ char *ec_version_str; /* Something like 1ZHT51WW-1.04a */ - u16 bios_model; /* Big Endian, TP-1Y = 0x5931, 0 = unknown */ + u16 bios_model; /* 1Y = 0x5931, 0 = unknown */ u16 ec_model; + u16 bios_release; /* 1ZETK1WW = 0x314b, 0 = unknown */ + u16 ec_release; char *model_str; /* ThinkPad T43 */ char *nummodel_str; /* 9384A9C for a 9384-A9C model */ @@ -355,6 +359,73 @@ static void tpacpi_log_usertask(const char * const what) } \ } while (0) +/* + * Quirk handling helpers + * + * ThinkPad IDs and versions seen in the field so far + * are two-characters from the set [0-9A-Z], i.e. base 36. + * + * We use values well outside that range as specials. + */ + +#define TPACPI_MATCH_ANY 0xffffU +#define TPACPI_MATCH_UNKNOWN 0U + +/* TPID('1', 'Y') == 0x5931 */ +#define TPID(__c1, __c2) (((__c2) << 8) | (__c1)) + +#define TPACPI_Q_IBM(__id1, __id2, __quirk) \ + { .vendor = PCI_VENDOR_ID_IBM, \ + .bios = TPID(__id1, __id2), \ + .ec = TPACPI_MATCH_ANY, \ + .quirks = (__quirk) } + +#define TPACPI_Q_LNV(__id1, __id2, __quirk) \ + { .vendor = PCI_VENDOR_ID_LENOVO, \ + .bios = TPID(__id1, __id2), \ + .ec = TPACPI_MATCH_ANY, \ + .quirks = (__quirk) } + +struct tpacpi_quirk { + unsigned int vendor; + u16 bios; + u16 ec; + unsigned long quirks; +}; + +/** + * tpacpi_check_quirks() - search BIOS/EC version on a list + * @qlist: array of &struct tpacpi_quirk + * @qlist_size: number of elements in @qlist + * + * Iterates over a quirks list until one is found that matches the + * ThinkPad's vendor, BIOS and EC model. + * + * Returns 0 if nothing matches, otherwise returns the quirks field of + * the matching &struct tpacpi_quirk entry. + * + * The match criteria is: vendor, ec and bios much match. + */ +static unsigned long __init tpacpi_check_quirks( + const struct tpacpi_quirk *qlist, + unsigned int qlist_size) +{ + while (qlist_size) { + if ((qlist->vendor == thinkpad_id.vendor || + qlist->vendor == TPACPI_MATCH_ANY) && + (qlist->bios == thinkpad_id.bios_model || + qlist->bios == TPACPI_MATCH_ANY) && + (qlist->ec == thinkpad_id.ec_model || + qlist->ec == TPACPI_MATCH_ANY)) + return qlist->quirks; + + qlist_size--; + qlist++; + } + return 0; +} + + /**************************************************************************** **************************************************************************** * @@ -1163,8 +1234,8 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, { struct tpacpi_rfk *atp_rfk; int res; - bool initial_sw_state = false; - int initial_sw_status; + bool sw_state = false; + int sw_status; BUG_ON(id >= TPACPI_RFK_SW_MAX || tpacpi_rfkill_switches[id]); @@ -1185,17 +1256,17 @@ static int __init tpacpi_new_rfkill(const enum tpacpi_rfk_id id, atp_rfk->id = id; atp_rfk->ops = tp_rfkops; - initial_sw_status = (tp_rfkops->get_status)(); - if (initial_sw_status < 0) { + sw_status = (tp_rfkops->get_status)(); + if (sw_status < 0) { printk(TPACPI_ERR "failed to read initial state for %s, error %d\n", - name, initial_sw_status); + name, sw_status); } else { - initial_sw_state = (initial_sw_status == TPACPI_RFK_RADIO_OFF); + sw_state = (sw_status == TPACPI_RFK_RADIO_OFF); if (set_default) { /* try to keep the initial state, since we ask the * firmware to preserve it across S5 in NVRAM */ - rfkill_set_sw_state(atp_rfk->rfkill, initial_sw_state); + rfkill_init_sw_state(atp_rfk->rfkill, sw_state); } } rfkill_set_hw_state(atp_rfk->rfkill, tpacpi_rfk_check_hwblock_state()); @@ -2880,7 +2951,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) /* update bright_acpimode... */ tpacpi_check_std_acpi_brightness_support(); - if (tp_features.bright_acpimode) { + if (tp_features.bright_acpimode && acpi_video_backlight_support()) { printk(TPACPI_INFO "This ThinkPad has standard ACPI backlight " "brightness control, supported by the ACPI " @@ -4773,7 +4844,7 @@ TPACPI_HANDLE(led, ec, "SLED", /* 570 */ "LED", /* all others */ ); /* R30, R31 */ -#define TPACPI_LED_NUMLEDS 8 +#define TPACPI_LED_NUMLEDS 16 static struct tpacpi_led_classdev *tpacpi_leds; static enum led_status_t tpacpi_led_state_cache[TPACPI_LED_NUMLEDS]; static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { @@ -4786,15 +4857,20 @@ static const char * const tpacpi_led_names[TPACPI_LED_NUMLEDS] = { "tpacpi::dock_batt", "tpacpi::unknown_led", "tpacpi::standby", + "tpacpi::dock_status1", + "tpacpi::dock_status2", + "tpacpi::unknown_led2", + "tpacpi::unknown_led3", + "tpacpi::thinkvantage", }; -#define TPACPI_SAFE_LEDS 0x0081U +#define TPACPI_SAFE_LEDS 0x1081U static inline bool tpacpi_is_led_restricted(const unsigned int led) { #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS return false; #else - return (TPACPI_SAFE_LEDS & (1 << led)) == 0; + return (1U & (TPACPI_SAFE_LEDS >> led)) == 0; #endif } @@ -4956,6 +5032,10 @@ static int __init tpacpi_init_led(unsigned int led) tpacpi_leds[led].led = led; + /* LEDs with no name don't get registered */ + if (!tpacpi_led_names[led]) + return 0; + tpacpi_leds[led].led_classdev.brightness_set = &led_sysfs_set; tpacpi_leds[led].led_classdev.blink_set = &led_sysfs_blink_set; if (led_supported == TPACPI_LED_570) @@ -4974,10 +5054,59 @@ static int __init tpacpi_init_led(unsigned int led) return rc; } +static const struct tpacpi_quirk led_useful_qtable[] __initconst = { + TPACPI_Q_IBM('1', 'E', 0x009f), /* A30 */ + TPACPI_Q_IBM('1', 'N', 0x009f), /* A31 */ + TPACPI_Q_IBM('1', 'G', 0x009f), /* A31 */ + + TPACPI_Q_IBM('1', 'I', 0x0097), /* T30 */ + TPACPI_Q_IBM('1', 'R', 0x0097), /* T40, T41, T42, R50, R51 */ + TPACPI_Q_IBM('7', '0', 0x0097), /* T43, R52 */ + TPACPI_Q_IBM('1', 'Y', 0x0097), /* T43 */ + TPACPI_Q_IBM('1', 'W', 0x0097), /* R50e */ + TPACPI_Q_IBM('1', 'V', 0x0097), /* R51 */ + TPACPI_Q_IBM('7', '8', 0x0097), /* R51e */ + TPACPI_Q_IBM('7', '6', 0x0097), /* R52 */ + + TPACPI_Q_IBM('1', 'K', 0x00bf), /* X30 */ + TPACPI_Q_IBM('1', 'Q', 0x00bf), /* X31, X32 */ + TPACPI_Q_IBM('1', 'U', 0x00bf), /* X40 */ + TPACPI_Q_IBM('7', '4', 0x00bf), /* X41 */ + TPACPI_Q_IBM('7', '5', 0x00bf), /* X41t */ + + TPACPI_Q_IBM('7', '9', 0x1f97), /* T60 (1) */ + TPACPI_Q_IBM('7', '7', 0x1f97), /* Z60* (1) */ + TPACPI_Q_IBM('7', 'F', 0x1f97), /* Z61* (1) */ + TPACPI_Q_IBM('7', 'B', 0x1fb7), /* X60 (1) */ + + /* (1) - may have excess leds enabled on MSB */ + + /* Defaults (order matters, keep last, don't reorder!) */ + { /* Lenovo */ + .vendor = PCI_VENDOR_ID_LENOVO, + .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, + .quirks = 0x1fffU, + }, + { /* IBM ThinkPads with no EC version string */ + .vendor = PCI_VENDOR_ID_IBM, + .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_UNKNOWN, + .quirks = 0x00ffU, + }, + { /* IBM ThinkPads with EC version string */ + .vendor = PCI_VENDOR_ID_IBM, + .bios = TPACPI_MATCH_ANY, .ec = TPACPI_MATCH_ANY, + .quirks = 0x00bfU, + }, +}; + +#undef TPACPI_LEDQ_IBM +#undef TPACPI_LEDQ_LNV + static int __init led_init(struct ibm_init_struct *iibm) { unsigned int i; int rc; + unsigned long useful_leds; vdbg_printk(TPACPI_DBG_INIT, "initializing LED subdriver\n"); @@ -4999,6 +5128,9 @@ static int __init led_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "LED commands are %s, mode %d\n", str_supported(led_supported), led_supported); + if (led_supported == TPACPI_LED_NONE) + return 1; + tpacpi_leds = kzalloc(sizeof(*tpacpi_leds) * TPACPI_LED_NUMLEDS, GFP_KERNEL); if (!tpacpi_leds) { @@ -5006,8 +5138,12 @@ static int __init led_init(struct ibm_init_struct *iibm) return -ENOMEM; } + useful_leds = tpacpi_check_quirks(led_useful_qtable, + ARRAY_SIZE(led_useful_qtable)); + for (i = 0; i < TPACPI_LED_NUMLEDS; i++) { - if (!tpacpi_is_led_restricted(i)) { + if (!tpacpi_is_led_restricted(i) && + test_bit(i, &useful_leds)) { rc = tpacpi_init_led(i); if (rc < 0) { led_exit(); @@ -5017,12 +5153,11 @@ static int __init led_init(struct ibm_init_struct *iibm) } #ifdef CONFIG_THINKPAD_ACPI_UNSAFE_LEDS - if (led_supported != TPACPI_LED_NONE) - printk(TPACPI_NOTICE - "warning: userspace override of important " - "firmware LEDs is enabled\n"); + printk(TPACPI_NOTICE + "warning: userspace override of important " + "firmware LEDs is enabled\n"); #endif - return (led_supported != TPACPI_LED_NONE)? 0 : 1; + return 0; } #define str_led_status(s) \ @@ -5052,7 +5187,7 @@ static int led_read(char *p) } len += sprintf(p + len, "commands:\t" - "<led> on, <led> off, <led> blink (<led> is 0-7)\n"); + "<led> on, <led> off, <led> blink (<led> is 0-15)\n"); return len; } @@ -5067,7 +5202,7 @@ static int led_write(char *buf) return -ENODEV; while ((cmd = next_cmd(&buf))) { - if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 7) + if (sscanf(cmd, "%d", &led) != 1 || led < 0 || led > 15) return -EINVAL; if (strstr(cmd, "off")) { @@ -5101,8 +5236,17 @@ static struct ibm_struct led_driver_data = { TPACPI_HANDLE(beep, ec, "BEEP"); /* all except R30, R31 */ +#define TPACPI_BEEP_Q1 0x0001 + +static const struct tpacpi_quirk beep_quirk_table[] __initconst = { + TPACPI_Q_IBM('I', 'M', TPACPI_BEEP_Q1), /* 570 */ + TPACPI_Q_IBM('I', 'U', TPACPI_BEEP_Q1), /* 570E - unverified */ +}; + static int __init beep_init(struct ibm_init_struct *iibm) { + unsigned long quirks; + vdbg_printk(TPACPI_DBG_INIT, "initializing beep subdriver\n"); TPACPI_ACPIHANDLE_INIT(beep); @@ -5110,6 +5254,11 @@ static int __init beep_init(struct ibm_init_struct *iibm) vdbg_printk(TPACPI_DBG_INIT, "beep is %s\n", str_supported(beep_handle != NULL)); + quirks = tpacpi_check_quirks(beep_quirk_table, + ARRAY_SIZE(beep_quirk_table)); + + tp_features.beep_needs_two_args = !!(quirks & TPACPI_BEEP_Q1); + return (beep_handle)? 0 : 1; } @@ -5141,8 +5290,15 @@ static int beep_write(char *buf) /* beep_cmd set */ } else return -EINVAL; - if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", beep_cmd, 0)) - return -EIO; + if (tp_features.beep_needs_two_args) { + if (!acpi_evalf(beep_handle, NULL, NULL, "vdd", + beep_cmd, 0)) + return -EIO; + } else { + if (!acpi_evalf(beep_handle, NULL, NULL, "vd", + beep_cmd)) + return -EIO; + } } return 0; @@ -5569,6 +5725,10 @@ static struct ibm_struct ecdump_driver_data = { * Bit 3-0: backlight brightness level * * brightness_get_raw returns status data in the HBRV layout + * + * WARNING: The X61 has been verified to use HBRV for something else, so + * this should be used _only_ on IBM ThinkPads, and maybe with some careful + * testing on the very early *60 Lenovo models... */ enum { @@ -5869,6 +6029,12 @@ static int __init brightness_init(struct ibm_init_struct *iibm) brightness_mode); } + /* Safety */ + if (thinkpad_id.vendor != PCI_VENDOR_ID_IBM && + (brightness_mode == TPACPI_BRGHT_MODE_ECNVRAM || + brightness_mode == TPACPI_BRGHT_MODE_EC)) + return -EINVAL; + if (tpacpi_brightness_get_raw(&b) < 0) return 1; @@ -6161,6 +6327,21 @@ static struct ibm_struct volume_driver_data = { * For firmware bugs, refer to: * http://thinkwiki.org/wiki/Embedded_Controller_Firmware#Firmware_Issues * + * ---- + * + * ThinkPad EC register 0x31 bit 0 (only on select models) + * + * When bit 0 of EC register 0x31 is zero, the tachometer registers + * show the speed of the main fan. When bit 0 of EC register 0x31 + * is one, the tachometer registers show the speed of the auxiliary + * fan. + * + * Fan control seems to affect both fans, regardless of the state + * of this bit. + * + * So far, only the firmware for the X60/X61 non-tablet versions + * seem to support this (firmware TP-7M). + * * TPACPI_FAN_WR_ACPI_FANS: * ThinkPad X31, X40, X41. Not available in the X60. * @@ -6187,6 +6368,8 @@ enum { /* Fan control constants */ fan_status_offset = 0x2f, /* EC register 0x2f */ fan_rpm_offset = 0x84, /* EC register 0x84: LSB, 0x85 MSB (RPM) * 0x84 must be read before 0x85 */ + fan_select_offset = 0x31, /* EC register 0x31 (Firmware 7M) + bit 0 selects which fan is active */ TP_EC_FAN_FULLSPEED = 0x40, /* EC fan mode: full speed */ TP_EC_FAN_AUTO = 0x80, /* EC fan mode: auto fan control */ @@ -6249,30 +6432,18 @@ TPACPI_HANDLE(sfan, ec, "SFAN", /* 570 */ * We assume 0x07 really means auto mode while this quirk is active, * as this is far more likely than the ThinkPad being in level 7, * which is only used by the firmware during thermal emergencies. + * + * Enable for TP-1Y (T43), TP-78 (R51e), TP-76 (R52), + * TP-70 (T43, R52), which are known to be buggy. */ -static void fan_quirk1_detect(void) +static void fan_quirk1_setup(void) { - /* In some ThinkPads, neither the EC nor the ACPI - * DSDT initialize the HFSP register, and it ends up - * being initially set to 0x07 when it *could* be - * either 0x07 or 0x80. - * - * Enable for TP-1Y (T43), TP-78 (R51e), - * TP-76 (R52), TP-70 (T43, R52), which are known - * to be buggy. */ if (fan_control_initial_status == 0x07) { - switch (thinkpad_id.ec_model) { - case 0x5931: /* TP-1Y */ - case 0x3837: /* TP-78 */ - case 0x3637: /* TP-76 */ - case 0x3037: /* TP-70 */ - printk(TPACPI_NOTICE - "fan_init: initial fan status is unknown, " - "assuming it is in auto mode\n"); - tp_features.fan_ctrl_status_undef = 1; - ;; - } + printk(TPACPI_NOTICE + "fan_init: initial fan status is unknown, " + "assuming it is in auto mode\n"); + tp_features.fan_ctrl_status_undef = 1; } } @@ -6292,6 +6463,38 @@ static void fan_quirk1_handle(u8 *fan_status) } } +/* Select main fan on X60/X61, NOOP on others */ +static bool fan_select_fan1(void) +{ + if (tp_features.second_fan) { + u8 val; + + if (ec_read(fan_select_offset, &val) < 0) + return false; + val &= 0xFEU; + if (ec_write(fan_select_offset, val) < 0) + return false; + } + return true; +} + +/* Select secondary fan on X60/X61 */ +static bool fan_select_fan2(void) +{ + u8 val; + + if (!tp_features.second_fan) + return false; + + if (ec_read(fan_select_offset, &val) < 0) + return false; + val |= 0x01U; + if (ec_write(fan_select_offset, val) < 0) + return false; + + return true; +} + /* * Call with fan_mutex held */ @@ -6369,6 +6572,8 @@ static int fan_get_speed(unsigned int *speed) switch (fan_status_access_mode) { case TPACPI_FAN_RD_TPEC: /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!fan_select_fan1())) + return -EIO; if (unlikely(!acpi_ec_read(fan_rpm_offset, &lo) || !acpi_ec_read(fan_rpm_offset + 1, &hi))) return -EIO; @@ -6385,6 +6590,34 @@ static int fan_get_speed(unsigned int *speed) return 0; } +static int fan2_get_speed(unsigned int *speed) +{ + u8 hi, lo; + bool rc; + + switch (fan_status_access_mode) { + case TPACPI_FAN_RD_TPEC: + /* all except 570, 600e/x, 770e, 770x */ + if (unlikely(!fan_select_fan2())) + return -EIO; + rc = !acpi_ec_read(fan_rpm_offset, &lo) || + !acpi_ec_read(fan_rpm_offset + 1, &hi); + fan_select_fan1(); /* play it safe */ + if (rc) + return -EIO; + + if (likely(speed)) + *speed = (hi << 8) | lo; + + break; + + default: + return -ENXIO; + } + + return 0; +} + static int fan_set_level(int level) { if (!fan_control_allowed) @@ -6790,6 +7023,25 @@ static struct device_attribute dev_attr_fan_fan1_input = __ATTR(fan1_input, S_IRUGO, fan_fan1_input_show, NULL); +/* sysfs fan fan2_input ------------------------------------------------ */ +static ssize_t fan_fan2_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int res; + unsigned int speed; + + res = fan2_get_speed(&speed); + if (res < 0) + return res; + + return snprintf(buf, PAGE_SIZE, "%u\n", speed); +} + +static struct device_attribute dev_attr_fan_fan2_input = + __ATTR(fan2_input, S_IRUGO, + fan_fan2_input_show, NULL); + /* sysfs fan fan_watchdog (hwmon driver) ------------------------------- */ static ssize_t fan_fan_watchdog_show(struct device_driver *drv, char *buf) @@ -6823,6 +7075,7 @@ static DRIVER_ATTR(fan_watchdog, S_IWUSR | S_IRUGO, static struct attribute *fan_attributes[] = { &dev_attr_fan_pwm1_enable.attr, &dev_attr_fan_pwm1.attr, &dev_attr_fan_fan1_input.attr, + NULL, /* for fan2_input */ NULL }; @@ -6830,9 +7083,36 @@ static const struct attribute_group fan_attr_group = { .attrs = fan_attributes, }; +#define TPACPI_FAN_Q1 0x0001 /* Unitialized HFSP */ +#define TPACPI_FAN_2FAN 0x0002 /* EC 0x31 bit 0 selects fan2 */ + +#define TPACPI_FAN_QI(__id1, __id2, __quirks) \ + { .vendor = PCI_VENDOR_ID_IBM, \ + .bios = TPACPI_MATCH_ANY, \ + .ec = TPID(__id1, __id2), \ + .quirks = __quirks } + +#define TPACPI_FAN_QL(__id1, __id2, __quirks) \ + { .vendor = PCI_VENDOR_ID_LENOVO, \ + .bios = TPACPI_MATCH_ANY, \ + .ec = TPID(__id1, __id2), \ + .quirks = __quirks } + +static const struct tpacpi_quirk fan_quirk_table[] __initconst = { + TPACPI_FAN_QI('1', 'Y', TPACPI_FAN_Q1), + TPACPI_FAN_QI('7', '8', TPACPI_FAN_Q1), + TPACPI_FAN_QI('7', '6', TPACPI_FAN_Q1), + TPACPI_FAN_QI('7', '0', TPACPI_FAN_Q1), + TPACPI_FAN_QL('7', 'M', TPACPI_FAN_2FAN), +}; + +#undef TPACPI_FAN_QL +#undef TPACPI_FAN_QI + static int __init fan_init(struct ibm_init_struct *iibm) { int rc; + unsigned long quirks; vdbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, "initializing fan subdriver\n"); @@ -6843,12 +7123,16 @@ static int __init fan_init(struct ibm_init_struct *iibm) fan_control_commands = 0; fan_watchdog_maxinterval = 0; tp_features.fan_ctrl_status_undef = 0; + tp_features.second_fan = 0; fan_control_desired_level = 7; TPACPI_ACPIHANDLE_INIT(fans); TPACPI_ACPIHANDLE_INIT(gfan); TPACPI_ACPIHANDLE_INIT(sfan); + quirks = tpacpi_check_quirks(fan_quirk_table, + ARRAY_SIZE(fan_quirk_table)); + if (gfan_handle) { /* 570, 600e/x, 770e, 770x */ fan_status_access_mode = TPACPI_FAN_RD_ACPI_GFAN; @@ -6858,7 +7142,13 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (likely(acpi_ec_read(fan_status_offset, &fan_control_initial_status))) { fan_status_access_mode = TPACPI_FAN_RD_TPEC; - fan_quirk1_detect(); + if (quirks & TPACPI_FAN_Q1) + fan_quirk1_setup(); + if (quirks & TPACPI_FAN_2FAN) { + tp_features.second_fan = 1; + dbg_printk(TPACPI_DBG_INIT | TPACPI_DBG_FAN, + "secondary fan support enabled\n"); + } } else { printk(TPACPI_ERR "ThinkPad ACPI EC access misbehaving, " @@ -6914,6 +7204,11 @@ static int __init fan_init(struct ibm_init_struct *iibm) if (fan_status_access_mode != TPACPI_FAN_NONE || fan_control_access_mode != TPACPI_FAN_WR_NONE) { + if (tp_features.second_fan) { + /* attach second fan tachometer */ + fan_attributes[ARRAY_SIZE(fan_attributes)-2] = + &dev_attr_fan_fan2_input.attr; + } rc = sysfs_create_group(&tpacpi_sensors_pdev->dev.kobj, &fan_attr_group); if (rc < 0) @@ -7385,6 +7680,24 @@ err_out: /* Probing */ +static bool __pure __init tpacpi_is_fw_digit(const char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); +} + +/* Most models: xxyTkkWW (#.##c); Ancient 570/600 and -SL lacks (#.##c) */ +static bool __pure __init tpacpi_is_valid_fw_id(const char* const s, + const char t) +{ + return s && strlen(s) >= 8 && + tpacpi_is_fw_digit(s[0]) && + tpacpi_is_fw_digit(s[1]) && + s[2] == t && s[3] == 'T' && + tpacpi_is_fw_digit(s[4]) && + tpacpi_is_fw_digit(s[5]) && + s[6] == 'W' && s[7] == 'W'; +} + /* returns 0 - probe ok, or < 0 - probe error. * Probe ok doesn't mean thinkpad found. * On error, kfree() cleanup on tp->* is not performed, caller must do it */ @@ -7411,10 +7724,15 @@ static int __must_check __init get_thinkpad_model_data( tp->bios_version_str = kstrdup(s, GFP_KERNEL); if (s && !tp->bios_version_str) return -ENOMEM; - if (!tp->bios_version_str) + + /* Really ancient ThinkPad 240X will fail this, which is fine */ + if (!tpacpi_is_valid_fw_id(tp->bios_version_str, 'E')) return 0; + tp->bios_model = tp->bios_version_str[0] | (tp->bios_version_str[1] << 8); + tp->bios_release = (tp->bios_version_str[4] << 8) + | tp->bios_version_str[5]; /* * ThinkPad T23 or newer, A31 or newer, R50e or newer, @@ -7433,8 +7751,21 @@ static int __must_check __init get_thinkpad_model_data( tp->ec_version_str = kstrdup(ec_fw_string, GFP_KERNEL); if (!tp->ec_version_str) return -ENOMEM; - tp->ec_model = ec_fw_string[0] - | (ec_fw_string[1] << 8); + + if (tpacpi_is_valid_fw_id(ec_fw_string, 'H')) { + tp->ec_model = ec_fw_string[0] + | (ec_fw_string[1] << 8); + tp->ec_release = (ec_fw_string[4] << 8) + | ec_fw_string[5]; + } else { + printk(TPACPI_NOTICE + "ThinkPad firmware release %s " + "doesn't match the known patterns\n", + ec_fw_string); + printk(TPACPI_NOTICE + "please report this to %s\n", + TPACPI_MAIL); + } break; } } diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 7f207f335be..ef3a2cd3a7a 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -287,6 +287,25 @@ static void pnpacpi_parse_allocated_address_space(struct pnp_dev *dev, ACPI_DECODE_16); } +static void pnpacpi_parse_allocated_ext_address_space(struct pnp_dev *dev, + struct acpi_resource *res) +{ + struct acpi_resource_extended_address64 *p = &res->data.ext_address64; + + if (p->producer_consumer == ACPI_PRODUCER) + return; + + if (p->resource_type == ACPI_MEMORY_RANGE) + pnpacpi_parse_allocated_memresource(dev, + p->minimum, p->address_length, + p->info.mem.write_protect); + else if (p->resource_type == ACPI_IO_RANGE) + pnpacpi_parse_allocated_ioresource(dev, + p->minimum, p->address_length, + p->granularity == 0xfff ? ACPI_DECODE_10 : + ACPI_DECODE_16); +} + static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, void *data) { @@ -400,8 +419,7 @@ static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res, break; case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: - if (res->data.ext_address64.producer_consumer == ACPI_PRODUCER) - return AE_OK; + pnpacpi_parse_allocated_ext_address_space(dev, res); break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: @@ -630,6 +648,28 @@ static __init void pnpacpi_parse_address_option(struct pnp_dev *dev, IORESOURCE_IO_FIXED); } +static __init void pnpacpi_parse_ext_address_option(struct pnp_dev *dev, + unsigned int option_flags, + struct acpi_resource *r) +{ + struct acpi_resource_extended_address64 *p = &r->data.ext_address64; + unsigned char flags = 0; + + if (p->address_length == 0) + return; + + if (p->resource_type == ACPI_MEMORY_RANGE) { + if (p->info.mem.write_protect == ACPI_READ_WRITE_MEMORY) + flags = IORESOURCE_MEM_WRITEABLE; + pnp_register_mem_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, + flags); + } else if (p->resource_type == ACPI_IO_RANGE) + pnp_register_port_resource(dev, option_flags, p->minimum, + p->minimum, 0, p->address_length, + IORESOURCE_IO_FIXED); +} + struct acpipnp_parse_option_s { struct pnp_dev *dev; unsigned int option_flags; @@ -711,6 +751,7 @@ static __init acpi_status pnpacpi_option_resource(struct acpi_resource *res, break; case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: + pnpacpi_parse_ext_address_option(dev, option_flags, res); break; case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: @@ -765,6 +806,7 @@ static int pnpacpi_supported_resource(struct acpi_resource *res) case ACPI_RESOURCE_TYPE_ADDRESS16: case ACPI_RESOURCE_TYPE_ADDRESS32: case ACPI_RESOURCE_TYPE_ADDRESS64: + case ACPI_RESOURCE_TYPE_EXTENDED_ADDRESS64: case ACPI_RESOURCE_TYPE_EXTENDED_IRQ: return 1; } diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index e5b84db0aa0..74983666865 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -470,7 +470,7 @@ static int dasd_decrease_state(struct dasd_device *device) */ static void dasd_change_state(struct dasd_device *device) { - int rc; + int rc; if (device->state == device->target) /* Already where we want to go today... */ @@ -479,8 +479,10 @@ static void dasd_change_state(struct dasd_device *device) rc = dasd_increase_state(device); else rc = dasd_decrease_state(device); - if (rc && rc != -EAGAIN) - device->target = device->state; + if (rc == -EAGAIN) + return; + if (rc) + device->target = device->state; if (device->state == device->target) { wake_up(&dasd_init_waitq); @@ -2503,15 +2505,25 @@ int dasd_generic_restore_device(struct ccw_device *cdev) if (IS_ERR(device)) return PTR_ERR(device); + /* allow new IO again */ + device->stopped &= ~DASD_STOPPED_PM; + 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); + if (rc) + /* + * if the resume failed for the DASD we put it in + * an UNRESUMED stop state + */ + device->stopped |= DASD_UNRESUMED_PM; dasd_put_device(device); - return rc; + return 0; } EXPORT_SYMBOL_GPL(dasd_generic_restore_device); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 1c28ec3e4cc..f8b1f04f26b 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -3243,9 +3243,6 @@ int dasd_eckd_restore_device(struct dasd_device *device) int is_known, rc; struct dasd_uid temp_uid; - /* allow new IO again */ - device->stopped &= ~DASD_STOPPED_PM; - private = (struct dasd_eckd_private *) device->private; /* Read Configuration Data */ @@ -3295,12 +3292,7 @@ int dasd_eckd_restore_device(struct dasd_device *device) return 0; out_err: - /* - * if the resume failed for the DASD we put it in - * an UNRESUMED stop state - */ - device->stopped |= DASD_UNRESUMED_PM; - return 0; + return -1; } static struct ccw_driver dasd_eckd_driver = { diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 04dc734805c..21639d6c996 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -20,10 +20,7 @@ #include <linux/interrupt.h> #include <linux/err.h> #include <linux/reboot.h> - #include <linux/slab.h> -#include <linux/bootmem.h> - #include <asm/ccwdev.h> #include <asm/cio.h> #include <asm/io.h> @@ -735,7 +732,7 @@ static int raw3215_pm_stop(struct ccw_device *cdev) unsigned long flags; /* Empty the output buffer, then prevent new I/O. */ - raw = cdev->dev.driver_data; + raw = dev_get_drvdata(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw3215_make_room(raw, RAW3215_BUFFER_SIZE); raw->flags |= RAW3215_FROZEN; @@ -749,7 +746,7 @@ static int raw3215_pm_start(struct ccw_device *cdev) unsigned long flags; /* Allow I/O again and flush output buffer. */ - raw = cdev->dev.driver_data; + raw = dev_get_drvdata(&cdev->dev); spin_lock_irqsave(get_ccwdev_lock(raw->cdev), flags); raw->flags &= ~RAW3215_FROZEN; raw->flags |= RAW3215_FLUSHING; @@ -883,7 +880,7 @@ static int __init con3215_init(void) raw3215_freelist = NULL; spin_lock_init(&raw3215_freelist_lock); for (i = 0; i < NR_3215_REQ; i++) { - req = (struct raw3215_req *) alloc_bootmem_low(sizeof(struct raw3215_req)); + req = kzalloc(sizeof(struct raw3215_req), GFP_KERNEL | GFP_DMA); req->next = raw3215_freelist; raw3215_freelist = req; } @@ -893,10 +890,9 @@ static int __init con3215_init(void) return -ENODEV; raw3215[0] = raw = (struct raw3215_info *) - alloc_bootmem_low(sizeof(struct raw3215_info)); - memset(raw, 0, sizeof(struct raw3215_info)); - raw->buffer = (char *) alloc_bootmem_low(RAW3215_BUFFER_SIZE); - raw->inbuf = (char *) alloc_bootmem_low(RAW3215_INBUF_SIZE); + kzalloc(sizeof(struct raw3215_info), GFP_KERNEL | GFP_DMA); + raw->buffer = kzalloc(RAW3215_BUFFER_SIZE, GFP_KERNEL | GFP_DMA); + raw->inbuf = kzalloc(RAW3215_INBUF_SIZE, GFP_KERNEL | GFP_DMA); raw->cdev = cdev; dev_set_drvdata(&cdev->dev, raw); cdev->handler = raw3215_irq; @@ -906,9 +902,9 @@ static int __init con3215_init(void) /* Request the console irq */ if (raw3215_startup(raw) != 0) { - free_bootmem((unsigned long) raw->inbuf, RAW3215_INBUF_SIZE); - free_bootmem((unsigned long) raw->buffer, RAW3215_BUFFER_SIZE); - free_bootmem((unsigned long) raw, sizeof(struct raw3215_info)); + kfree(raw->inbuf); + kfree(raw->buffer); + kfree(raw); raw3215[0] = NULL; return -ENODEV; } diff --git a/drivers/s390/char/con3270.c b/drivers/s390/char/con3270.c index 44d02e371c0..bb838bdf829 100644 --- a/drivers/s390/char/con3270.c +++ b/drivers/s390/char/con3270.c @@ -7,7 +7,6 @@ * Copyright IBM Corp. 2003, 2009 */ -#include <linux/bootmem.h> #include <linux/console.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -600,16 +599,14 @@ con3270_init(void) if (IS_ERR(rp)) return PTR_ERR(rp); - condev = (struct con3270 *) alloc_bootmem_low(sizeof(struct con3270)); - memset(condev, 0, sizeof(struct con3270)); + condev = kzalloc(sizeof(struct con3270), GFP_KERNEL | GFP_DMA); condev->view.dev = rp; - condev->read = raw3270_request_alloc_bootmem(0); + condev->read = raw3270_request_alloc(0); condev->read->callback = con3270_read_callback; condev->read->callback_data = condev; - condev->write = - raw3270_request_alloc_bootmem(CON3270_OUTPUT_BUFFER_SIZE); - condev->kreset = raw3270_request_alloc_bootmem(1); + condev->write = raw3270_request_alloc(CON3270_OUTPUT_BUFFER_SIZE); + condev->kreset = raw3270_request_alloc(1); INIT_LIST_HEAD(&condev->lines); INIT_LIST_HEAD(&condev->update); @@ -623,7 +620,7 @@ con3270_init(void) INIT_LIST_HEAD(&condev->freemem); for (i = 0; i < CON3270_STRING_PAGES; i++) { - cbuf = (void *) alloc_bootmem_low_pages(PAGE_SIZE); + cbuf = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); add_string_memory(&condev->freemem, cbuf, PAGE_SIZE); } condev->cline = alloc_string(&condev->freemem, condev->view.cols); diff --git a/drivers/s390/char/monreader.c b/drivers/s390/char/monreader.c index 75a8831eebb..7892550d793 100644 --- a/drivers/s390/char/monreader.c +++ b/drivers/s390/char/monreader.c @@ -320,7 +320,7 @@ static int mon_open(struct inode *inode, struct file *filp) goto out_path; } filp->private_data = monpriv; - monreader_device->driver_data = monpriv; + dev_set_drvdata(&monreader_device, monpriv); unlock_kernel(); return nonseekable_open(inode, filp); @@ -463,7 +463,7 @@ static struct miscdevice mon_dev = { *****************************************************************************/ static int monreader_freeze(struct device *dev) { - struct mon_private *monpriv = dev->driver_data; + struct mon_private *monpriv = dev_get_drvdata(&dev); int rc; if (!monpriv) @@ -487,7 +487,7 @@ static int monreader_freeze(struct device *dev) static int monreader_thaw(struct device *dev) { - struct mon_private *monpriv = dev->driver_data; + struct mon_private *monpriv = dev_get_drvdata(dev); int rc; if (!monpriv) diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index acab7b2dfe8..d6a022f55e9 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -7,7 +7,6 @@ * Copyright IBM Corp. 2003, 2009 */ -#include <linux/bootmem.h> #include <linux/module.h> #include <linux/err.h> #include <linux/init.h> @@ -143,33 +142,6 @@ raw3270_request_alloc(size_t size) return rq; } -#ifdef CONFIG_TN3270_CONSOLE -/* - * Allocate a new 3270 ccw request from bootmem. Only works very - * early in the boot process. Only con3270.c should be using this. - */ -struct raw3270_request __init *raw3270_request_alloc_bootmem(size_t size) -{ - struct raw3270_request *rq; - - rq = alloc_bootmem_low(sizeof(struct raw3270)); - - /* alloc output buffer. */ - if (size > 0) - rq->buffer = alloc_bootmem_low(size); - rq->size = size; - INIT_LIST_HEAD(&rq->list); - - /* - * Setup ccw. - */ - rq->ccw.cda = __pa(rq->buffer); - rq->ccw.flags = CCW_FLAG_SLI; - - return rq; -} -#endif - /* * Free 3270 ccw request */ @@ -846,8 +818,8 @@ struct raw3270 __init *raw3270_setup_console(struct ccw_device *cdev) char *ascebc; int rc; - rp = (struct raw3270 *) alloc_bootmem_low(sizeof(struct raw3270)); - ascebc = (char *) alloc_bootmem(256); + rp = kzalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA); + ascebc = kzalloc(256, GFP_KERNEL); rc = raw3270_setup_device(cdev, rp, ascebc); if (rc) return ERR_PTR(rc); @@ -1350,7 +1322,7 @@ static int raw3270_pm_stop(struct ccw_device *cdev) struct raw3270_view *view; unsigned long flags; - rp = cdev->dev.driver_data; + rp = dev_get_drvdata(&cdev->dev); if (!rp) return 0; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); @@ -1376,7 +1348,7 @@ static int raw3270_pm_start(struct ccw_device *cdev) struct raw3270 *rp; unsigned long flags; - rp = cdev->dev.driver_data; + rp = dev_get_drvdata(&cdev->dev); if (!rp) return 0; spin_lock_irqsave(get_ccwdev_lock(rp->cdev), flags); diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c index 336811a7767..ad698d30cb3 100644 --- a/drivers/s390/char/sclp_con.c +++ b/drivers/s390/char/sclp_con.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/timer.h> #include <linux/jiffies.h> -#include <linux/bootmem.h> #include <linux/termios.h> #include <linux/err.h> #include <linux/reboot.h> @@ -110,7 +109,7 @@ static void sclp_console_sync_queue(void) spin_lock_irqsave(&sclp_con_lock, flags); if (timer_pending(&sclp_con_timer)) - del_timer_sync(&sclp_con_timer); + del_timer(&sclp_con_timer); while (sclp_con_queue_running) { spin_unlock_irqrestore(&sclp_con_lock, flags); sclp_sync_wait(); @@ -298,8 +297,8 @@ sclp_console_init(void) /* Allocate pages for output buffering */ INIT_LIST_HEAD(&sclp_con_pages); for (i = 0; i < MAX_CONSOLE_PAGES; i++) { - page = alloc_bootmem_low_pages(PAGE_SIZE); - list_add_tail((struct list_head *) page, &sclp_con_pages); + page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + list_add_tail(page, &sclp_con_pages); } INIT_LIST_HEAD(&sclp_con_outqueue); spin_lock_init(&sclp_con_lock); diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c index 5518e24946a..178724f2a4c 100644 --- a/drivers/s390/char/sclp_vt220.c +++ b/drivers/s390/char/sclp_vt220.c @@ -20,7 +20,6 @@ #include <linux/major.h> #include <linux/console.h> #include <linux/kdev_t.h> -#include <linux/bootmem.h> #include <linux/interrupt.h> #include <linux/init.h> #include <linux/reboot.h> @@ -601,10 +600,7 @@ static void __init __sclp_vt220_free_pages(void) list_for_each_safe(page, p, &sclp_vt220_empty) { list_del(page); - if (slab_is_available()) - free_page((unsigned long) page); - else - free_bootmem((unsigned long) page, PAGE_SIZE); + free_page((unsigned long) page); } } @@ -640,16 +636,12 @@ static int __init __sclp_vt220_init(int num_pages) sclp_vt220_flush_later = 0; /* Allocate pages for output buffering */ + rc = -ENOMEM; for (i = 0; i < num_pages; i++) { - if (slab_is_available()) - page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); - else - page = alloc_bootmem_low_pages(PAGE_SIZE); - if (!page) { - rc = -ENOMEM; + page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA); + if (!page) goto out; - } - list_add_tail((struct list_head *) page, &sclp_vt220_empty); + list_add_tail(page, &sclp_vt220_empty); } rc = sclp_register(&sclp_vt220_register); out: diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index 595aa04cfd0..1d420d94759 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -396,7 +396,7 @@ int tape_generic_pm_suspend(struct ccw_device *cdev) { struct tape_device *device; - device = cdev->dev.driver_data; + device = dev_get_drvdata(&cdev->dev); if (!device) { return -ENODEV; } diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c index 411cfa3c771..c20a4fe6da5 100644 --- a/drivers/s390/char/vmlogrdr.c +++ b/drivers/s390/char/vmlogrdr.c @@ -663,7 +663,7 @@ static struct attribute *vmlogrdr_attrs[] = { static int vmlogrdr_pm_prepare(struct device *dev) { int rc; - struct vmlogrdr_priv_t *priv = dev->driver_data; + struct vmlogrdr_priv_t *priv = dev_get_drvdata(dev); rc = 0; if (priv) { @@ -753,7 +753,7 @@ static int vmlogrdr_register_device(struct vmlogrdr_priv_t *priv) dev->bus = &iucv_bus; dev->parent = iucv_root; dev->driver = &vmlogrdr_driver; - dev->driver_data = priv; + dev_set_drvdata(dev, priv); /* * The release function could be called after the * module has been unloaded. It's _only_ task is to diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index 7d9e67cb647..31b902e94f7 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -170,7 +170,7 @@ static void urdev_put(struct urdev *urd) */ static int ur_pm_suspend(struct ccw_device *cdev) { - struct urdev *urd = cdev->dev.driver_data; + struct urdev *urd = dev_get_drvdata(&cdev->dev); TRACE("ur_pm_suspend: cdev=%p\n", cdev); if (urd->open_flag) { diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 13bcb811438..b1241f8fae8 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -351,15 +351,6 @@ static inline unsigned long long get_usecs(void) ((bufnr - dec) & QDIO_MAX_BUFFERS_MASK) /* prototypes for thin interrupt */ -void qdio_sync_after_thinint(struct qdio_q *q); -int get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state, - int auto_ack); -void qdio_check_outbound_after_thinint(struct qdio_q *q); -int qdio_inbound_q_moved(struct qdio_q *q); -void qdio_kick_handler(struct qdio_q *q); -void qdio_stop_polling(struct qdio_q *q); -int qdio_siga_sync_q(struct qdio_q *q); - void qdio_setup_thinint(struct qdio_irq *irq_ptr); int qdio_establish_thinint(struct qdio_irq *irq_ptr); void qdio_shutdown_thinint(struct qdio_irq *irq_ptr); @@ -392,4 +383,6 @@ void qdio_setup_destroy_sysfs(struct ccw_device *cdev); int qdio_setup_init(void); void qdio_setup_exit(void); +int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, + unsigned char *state); #endif /* _CIO_QDIO_H */ diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index e3434b34f86..b8626d4df11 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -70,9 +70,8 @@ static int qstat_show(struct seq_file *m, void *v) seq_printf(m, "slsb buffer states:\n"); seq_printf(m, "|0 |8 |16 |24 |32 |40 |48 |56 63|\n"); - qdio_siga_sync_q(q); for (i = 0; i < QDIO_MAX_BUFFERS_PER_Q; i++) { - get_buf_state(q, i, &state, 0); + debug_get_buf_state(q, i, &state); switch (state) { case SLSB_P_INPUT_NOT_INIT: case SLSB_P_OUTPUT_NOT_INIT: diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index d79cf5bf0e6..0038750ad94 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -231,8 +231,8 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, return i; } -inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, - unsigned char *state, int auto_ack) +static inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, + unsigned char *state, int auto_ack) { return get_buf_states(q, bufnr, state, 1, auto_ack); } @@ -276,7 +276,7 @@ void qdio_init_buf_states(struct qdio_irq *irq_ptr) QDIO_MAX_BUFFERS_PER_Q); } -static int qdio_siga_sync(struct qdio_q *q, unsigned int output, +static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, unsigned int input) { int cc; @@ -293,7 +293,7 @@ static int qdio_siga_sync(struct qdio_q *q, unsigned int output, return cc; } -inline int qdio_siga_sync_q(struct qdio_q *q) +static inline int qdio_siga_sync_q(struct qdio_q *q) { if (q->is_input_q) return qdio_siga_sync(q, 0, q->mask); @@ -358,8 +358,7 @@ static inline int qdio_siga_input(struct qdio_q *q) return cc; } -/* called from thinint inbound handler */ -void qdio_sync_after_thinint(struct qdio_q *q) +static inline void qdio_sync_after_thinint(struct qdio_q *q) { if (pci_out_supported(q)) { if (need_siga_sync_thinint(q)) @@ -370,7 +369,14 @@ void qdio_sync_after_thinint(struct qdio_q *q) qdio_siga_sync_q(q); } -inline void qdio_stop_polling(struct qdio_q *q) +int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, + unsigned char *state) +{ + qdio_siga_sync_q(q); + return get_buf_states(q, bufnr, state, 1, 0); +} + +static inline void qdio_stop_polling(struct qdio_q *q) { if (!q->u.in.polling) return; @@ -449,13 +455,6 @@ static inline void inbound_primed(struct qdio_q *q, int count) count--; if (!count) return; - - /* - * Need to change all PRIMED buffers to NOT_INIT, otherwise - * we're loosing initiative in the thinint code. - */ - set_buf_states(q, q->first_to_check, SLSB_P_INPUT_NOT_INIT, - count); } static int get_inbound_buffer_frontier(struct qdio_q *q) @@ -470,19 +469,13 @@ static int get_inbound_buffer_frontier(struct qdio_q *q) count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK); stop = add_buf(q->first_to_check, count); - /* - * No siga sync here, as a PCI or we after a thin interrupt - * will sync the queues. - */ - - /* need to set count to 1 for non-qebsm */ - if (!is_qebsm(q)) - count = 1; - -check_next: if (q->first_to_check == stop) goto out; + /* + * No siga sync here, as a PCI or we after a thin interrupt + * already sync'ed the queues. + */ count = get_buf_states(q, q->first_to_check, &state, count, 1); if (!count) goto out; @@ -490,14 +483,9 @@ check_next: switch (state) { case SLSB_P_INPUT_PRIMED: inbound_primed(q, count); - /* - * No siga-sync needed for non-qebsm here, as the inbound queue - * will be synced on the next siga-r, resp. - * tiqdio_is_inbound_q_done will do the siga-sync. - */ q->first_to_check = add_buf(q->first_to_check, count); atomic_sub(count, &q->nr_buf_used); - goto check_next; + break; case SLSB_P_INPUT_ERROR: announce_buffer_error(q, count); /* process the buffer, the upper layer will take care of it */ @@ -516,7 +504,7 @@ out: return q->first_to_check; } -int qdio_inbound_q_moved(struct qdio_q *q) +static int qdio_inbound_q_moved(struct qdio_q *q) { int bufnr; @@ -524,35 +512,32 @@ int qdio_inbound_q_moved(struct qdio_q *q) if ((bufnr != q->last_move) || q->qdio_error) { q->last_move = bufnr; - if (!need_siga_sync(q) && !pci_out_supported(q)) + if (!is_thinint_irq(q->irq_ptr) && !MACHINE_IS_VM) q->u.in.timestamp = get_usecs(); - - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in moved"); return 1; } else return 0; } -static int qdio_inbound_q_done(struct qdio_q *q) +static inline int qdio_inbound_q_done(struct qdio_q *q) { unsigned char state = 0; if (!atomic_read(&q->nr_buf_used)) return 1; - /* - * We need that one for synchronization with the adapter, as it - * does a kind of PCI avoidance. - */ qdio_siga_sync_q(q); - get_buf_state(q, q->first_to_check, &state, 0); + if (state == SLSB_P_INPUT_PRIMED) - /* we got something to do */ + /* more work coming */ return 0; - /* on VM, we don't poll, so the q is always done here */ - if (need_siga_sync(q) || pci_out_supported(q)) + if (is_thinint_irq(q->irq_ptr)) + return 1; + + /* don't poll under z/VM */ + if (MACHINE_IS_VM) return 1; /* @@ -563,14 +548,11 @@ static int qdio_inbound_q_done(struct qdio_q *q) DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in done:%3d", q->first_to_check); return 1; - } else { - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in notd:%3d", - q->first_to_check); + } else return 0; - } } -void qdio_kick_handler(struct qdio_q *q) +static void qdio_kick_handler(struct qdio_q *q) { int start = q->first_to_kick; int end = q->first_to_check; @@ -619,7 +601,6 @@ again: goto again; } -/* inbound tasklet */ void qdio_inbound_processing(unsigned long data) { struct qdio_q *q = (struct qdio_q *)data; @@ -642,11 +623,6 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK); stop = add_buf(q->first_to_check, count); - /* need to set count to 1 for non-qebsm */ - if (!is_qebsm(q)) - count = 1; - -check_next: if (q->first_to_check == stop) return q->first_to_check; @@ -661,13 +637,7 @@ check_next: atomic_sub(count, &q->nr_buf_used); q->first_to_check = add_buf(q->first_to_check, count); - /* - * We fetch all buffer states at once. get_buf_states may - * return count < stop. For QEBSM we do not loop. - */ - if (is_qebsm(q)) - break; - goto check_next; + break; case SLSB_P_OUTPUT_ERROR: announce_buffer_error(q, count); /* process the buffer, the upper layer will take care of it */ @@ -797,8 +767,7 @@ void qdio_outbound_timer(unsigned long data) tasklet_schedule(&q->tasklet); } -/* called from thinint inbound tasklet */ -void qdio_check_outbound_after_thinint(struct qdio_q *q) +static inline void qdio_check_outbound_after_thinint(struct qdio_q *q) { struct qdio_q *out; int i; @@ -811,6 +780,46 @@ void qdio_check_outbound_after_thinint(struct qdio_q *q) tasklet_schedule(&out->tasklet); } +static void __tiqdio_inbound_processing(struct qdio_q *q) +{ + qdio_perf_stat_inc(&perf_stats.thinint_inbound); + qdio_sync_after_thinint(q); + + /* + * The interrupt could be caused by a PCI request. Check the + * PCI capable outbound queues. + */ + qdio_check_outbound_after_thinint(q); + + if (!qdio_inbound_q_moved(q)) + return; + + qdio_kick_handler(q); + + if (!qdio_inbound_q_done(q)) { + qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); + if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) + tasklet_schedule(&q->tasklet); + } + + qdio_stop_polling(q); + /* + * We need to check again to not lose initiative after + * resetting the ACK state. + */ + if (!qdio_inbound_q_done(q)) { + qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2); + if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) + tasklet_schedule(&q->tasklet); + } +} + +void tiqdio_inbound_processing(unsigned long data) +{ + struct qdio_q *q = (struct qdio_q *)data; + __tiqdio_inbound_processing(q); +} + static inline void qdio_set_state(struct qdio_irq *irq_ptr, enum qdio_irq_states state) { @@ -1488,18 +1497,13 @@ out: * @count: how many buffers to process */ int do_QDIO(struct ccw_device *cdev, unsigned int callflags, - int q_nr, int bufnr, int count) + int q_nr, unsigned int bufnr, unsigned int count) { struct qdio_irq *irq_ptr; - if ((bufnr > QDIO_MAX_BUFFERS_PER_Q) || - (count > QDIO_MAX_BUFFERS_PER_Q) || - (q_nr >= QDIO_MAX_QUEUES_PER_IRQ)) + if (bufnr >= QDIO_MAX_BUFFERS_PER_Q || count > QDIO_MAX_BUFFERS_PER_Q) return -EINVAL; - if (!count) - return 0; - irq_ptr = cdev->private->qdio_data; if (!irq_ptr) return -ENODEV; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index c655d011a78..981a77ea7ee 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -43,9 +43,6 @@ struct indicator_t { }; static struct indicator_t *q_indicators; -static void tiqdio_tasklet_fn(unsigned long data); -static DECLARE_TASKLET(tiqdio_tasklet, tiqdio_tasklet_fn, 0); - static int css_qdio_omit_svs; static inline unsigned long do_clear_global_summary(void) @@ -103,11 +100,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) xchg(irq_ptr->dsci, 1); } -/* - * we cannot stop the tiqdio tasklet here since it is for all - * thinint qdio devices and it must run as long as there is a - * thinint device left - */ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) { struct qdio_q *q; @@ -126,79 +118,39 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) } } -static inline int tiqdio_inbound_q_done(struct qdio_q *q) -{ - unsigned char state = 0; - - if (!atomic_read(&q->nr_buf_used)) - return 1; - - qdio_siga_sync_q(q); - get_buf_state(q, q->first_to_check, &state, 0); - - if (state == SLSB_P_INPUT_PRIMED) - /* more work coming */ - return 0; - return 1; -} - static inline int shared_ind(struct qdio_irq *irq_ptr) { return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; } -static void __tiqdio_inbound_processing(struct qdio_q *q) +/** + * tiqdio_thinint_handler - thin interrupt handler for qdio + * @ind: pointer to adapter local summary indicator + * @drv_data: NULL + */ +static void tiqdio_thinint_handler(void *ind, void *drv_data) { - qdio_perf_stat_inc(&perf_stats.thinint_inbound); - qdio_sync_after_thinint(q); + struct qdio_q *q; + + qdio_perf_stat_inc(&perf_stats.thin_int); /* - * Maybe we have work on our outbound queues... at least - * we have to check the PCI capable queues. + * SVS only when needed: issue SVS to benefit from iqdio interrupt + * avoidance (SVS clears adapter interrupt suppression overwrite) */ - qdio_check_outbound_after_thinint(q); - - if (!qdio_inbound_q_moved(q)) - return; - - qdio_kick_handler(q); - - if (!tiqdio_inbound_q_done(q)) { - qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop); - if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) - tasklet_schedule(&q->tasklet); - } + if (!css_qdio_omit_svs) + do_clear_global_summary(); - qdio_stop_polling(q); /* - * We need to check again to not lose initiative after - * resetting the ACK state. + * reset local summary indicator (tiqdio_alsi) to stop adapter + * interrupts for now */ - if (!tiqdio_inbound_q_done(q)) { - qdio_perf_stat_inc(&perf_stats.thinint_inbound_loop2); - if (likely(q->irq_ptr->state != QDIO_IRQ_STATE_STOPPED)) - tasklet_schedule(&q->tasklet); - } -} - -void tiqdio_inbound_processing(unsigned long data) -{ - struct qdio_q *q = (struct qdio_q *)data; - - __tiqdio_inbound_processing(q); -} - -/* check for work on all inbound thinint queues */ -static void tiqdio_tasklet_fn(unsigned long data) -{ - struct qdio_q *q; - - qdio_perf_stat_inc(&perf_stats.tasklet_thinint); -again: + xchg((u8 *)ind, 0); /* protect tiq_list entries, only changed in activate or shutdown */ rcu_read_lock(); + /* check for work on all inbound thinint queues */ list_for_each_entry_rcu(q, &tiq_list, entry) /* only process queues from changed sets */ if (*q->irq_ptr->dsci) { @@ -226,37 +178,6 @@ again: if (*tiqdio_alsi) xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1); } - - /* check for more work */ - if (*tiqdio_alsi) { - xchg(tiqdio_alsi, 0); - qdio_perf_stat_inc(&perf_stats.tasklet_thinint_loop); - goto again; - } -} - -/** - * tiqdio_thinint_handler - thin interrupt handler for qdio - * @ind: pointer to adapter local summary indicator - * @drv_data: NULL - */ -static void tiqdio_thinint_handler(void *ind, void *drv_data) -{ - qdio_perf_stat_inc(&perf_stats.thin_int); - - /* - * SVS only when needed: issue SVS to benefit from iqdio interrupt - * avoidance (SVS clears adapter interrupt suppression overwrite) - */ - if (!css_qdio_omit_svs) - do_clear_global_summary(); - - /* - * reset local summary indicator (tiqdio_alsi) to stop adapter - * interrupts for now, the tasklet will clean all dsci's - */ - xchg((u8 *)ind, 0); - tasklet_hi_schedule(&tiqdio_tasklet); } static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) @@ -376,5 +297,4 @@ void __exit tiqdio_unregister_thinints(void) s390_unregister_adapter_interrupt(tiqdio_alsi, QDIO_AIRQ_ISC); isc_unregister(QDIO_AIRQ_ISC); } - tasklet_kill(&tiqdio_tasklet); } diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 9c148406b98..727a809636d 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -54,6 +54,12 @@ static int ap_poll_thread_start(void); static void ap_poll_thread_stop(void); static void ap_request_timeout(unsigned long); static inline void ap_schedule_poll_timer(void); +static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags); +static int ap_device_remove(struct device *dev); +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); /* * Module description. @@ -101,6 +107,10 @@ static struct hrtimer ap_poll_timer; * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ static unsigned long long poll_timeout = 250000; +/* Suspend flag */ +static int ap_suspend_flag; +static struct bus_type ap_bus_type; + /** * ap_using_interrupts() - Returns non-zero if interrupt support is * available. @@ -617,10 +627,79 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env) return retval; } +static int ap_bus_suspend(struct device *dev, pm_message_t state) +{ + struct ap_device *ap_dev = to_ap_dev(dev); + unsigned long flags; + + if (!ap_suspend_flag) { + ap_suspend_flag = 1; + + /* Disable scanning for devices, thus we do not want to scan + * for them after removing. + */ + del_timer_sync(&ap_config_timer); + if (ap_work_queue != NULL) { + destroy_workqueue(ap_work_queue); + ap_work_queue = NULL; + } + tasklet_disable(&ap_tasklet); + } + /* Poll on the device until all requests are finished. */ + do { + flags = 0; + __ap_poll_device(ap_dev, &flags); + } while ((flags & 1) || (flags & 2)); + + ap_device_remove(dev); + return 0; +} + +static int ap_bus_resume(struct device *dev) +{ + int rc = 0; + struct ap_device *ap_dev = to_ap_dev(dev); + + if (ap_suspend_flag) { + 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); + init_timer(&ap_config_timer); + ap_config_timer.function = ap_config_timeout; + ap_config_timer.data = 0; + ap_config_timer.expires = jiffies + ap_config_time * HZ; + add_timer(&ap_config_timer); + ap_work_queue = create_singlethread_workqueue("kapwork"); + if (!ap_work_queue) + return -ENOMEM; + tasklet_enable(&ap_tasklet); + if (!ap_using_interrupts()) + ap_schedule_poll_timer(); + else + 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); + } + + return rc; +} + static struct bus_type ap_bus_type = { .name = "ap", .match = &ap_bus_match, .uevent = &ap_uevent, + .suspend = ap_bus_suspend, + .resume = ap_bus_resume }; static int ap_device_probe(struct device *dev) @@ -1066,7 +1145,7 @@ ap_config_timeout(unsigned long ptr) */ static inline void ap_schedule_poll_timer(void) { - if (ap_using_interrupts()) + if (ap_using_interrupts() || ap_suspend_flag) return; if (hrtimer_is_queued(&ap_poll_timer)) return; @@ -1384,6 +1463,8 @@ static int ap_poll_thread(void *data) set_user_nice(current, 19); while (1) { + if (ap_suspend_flag) + return 0; if (need_resched()) { schedule(); continue; @@ -1414,7 +1495,7 @@ static int ap_poll_thread_start(void) { int rc; - if (ap_using_interrupts()) + if (ap_using_interrupts() || ap_suspend_flag) return 0; mutex_lock(&ap_poll_thread_mutex); if (!ap_poll_kthread) { diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 52574ce797b..8c36eafcfbf 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -1307,7 +1307,7 @@ static void netiucv_pm_complete(struct device *dev) */ static int netiucv_pm_freeze(struct device *dev) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); struct net_device *ndev = NULL; int rc = 0; @@ -1331,7 +1331,7 @@ out: */ static int netiucv_pm_restore_thaw(struct device *dev) { - struct netiucv_priv *priv = dev->driver_data; + struct netiucv_priv *priv = dev_get_drvdata(dev); struct net_device *ndev = NULL; int rc = 0; diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index 6a19ed9a119..9c23122f755 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -258,10 +258,21 @@ config SCSI_SCAN_ASYNC or async on the kernel's command line. config SCSI_WAIT_SCAN - tristate + tristate # No prompt here, this is an invisible symbol. default m depends on SCSI depends on MODULES +# scsi_wait_scan is a loadable module which waits until all the async scans are +# complete. The idea is to use it in initrd/ initramfs scripts. You modprobe +# it after all the modprobes of the root SCSI drivers and it will wait until +# they have all finished scanning their buses before allowing the boot to +# proceed. (This method is not applicable if targets boot independently in +# parallel with the initiator, or with transports with non-deterministic target +# discovery schemes, or if a transport driver does not support scsi_wait_scan.) +# +# This symbol is not exposed as a prompt because little is to be gained by +# disabling it, whereas people who accidentally switch it off may wonder why +# their mkinitrd gets into trouble. menu "SCSI Transports" depends on SCSI diff --git a/drivers/scsi/bnx2i/Kconfig b/drivers/scsi/bnx2i/Kconfig index b62b482e55e..1e9f7141102 100644 --- a/drivers/scsi/bnx2i/Kconfig +++ b/drivers/scsi/bnx2i/Kconfig @@ -1,6 +1,8 @@ config SCSI_BNX2_ISCSI tristate "Broadcom NetXtreme II iSCSI support" select SCSI_ISCSI_ATTRS + select NETDEVICES + select NETDEV_1000 select CNIC depends on PCI ---help--- diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.c b/drivers/scsi/cxgb3i/cxgb3i_ddp.c index 99c91254790..344fd53b995 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_ddp.c +++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.c @@ -206,6 +206,31 @@ int cxgb3i_ddp_find_page_index(unsigned long pgsz) return DDP_PGIDX_MAX; } +/** + * cxgb3i_ddp_adjust_page_table - adjust page table with PAGE_SIZE + * return the ddp page index, if no match is found return DDP_PGIDX_MAX. + */ +int cxgb3i_ddp_adjust_page_table(void) +{ + int i; + unsigned int base_order, order; + + if (PAGE_SIZE < (1UL << ddp_page_shift[0])) { + ddp_log_info("PAGE_SIZE 0x%lx too small, min. 0x%lx.\n", + PAGE_SIZE, 1UL << ddp_page_shift[0]); + return -EINVAL; + } + + base_order = get_order(1UL << ddp_page_shift[0]); + order = get_order(1 << PAGE_SHIFT); + for (i = 0; i < DDP_PGIDX_MAX; i++) { + /* first is the kernel page size, then just doubling the size */ + ddp_page_order[i] = order - base_order + i; + ddp_page_shift[i] = PAGE_SHIFT + i; + } + return 0; +} + static inline void ddp_gl_unmap(struct pci_dev *pdev, struct cxgb3i_gather_list *gl) { @@ -598,30 +623,40 @@ int cxgb3i_adapter_ddp_info(struct t3cdev *tdev, * release all the resource held by the ddp pagepod manager for a given * adapter if needed */ -void cxgb3i_ddp_cleanup(struct t3cdev *tdev) + +static void ddp_cleanup(struct kref *kref) { + struct cxgb3i_ddp_info *ddp = container_of(kref, + struct cxgb3i_ddp_info, + refcnt); int i = 0; + + ddp_log_info("kref release ddp 0x%p, t3dev 0x%p.\n", ddp, ddp->tdev); + + ddp->tdev->ulp_iscsi = NULL; + while (i < ddp->nppods) { + struct cxgb3i_gather_list *gl = ddp->gl_map[i]; + if (gl) { + int npods = (gl->nelem + PPOD_PAGES_MAX - 1) + >> PPOD_PAGES_SHIFT; + ddp_log_info("t3dev 0x%p, ddp %d + %d.\n", + ddp->tdev, i, npods); + kfree(gl); + ddp_free_gl_skb(ddp, i, npods); + i += npods; + } else + i++; + } + cxgb3i_free_big_mem(ddp); +} + +void cxgb3i_ddp_cleanup(struct t3cdev *tdev) +{ struct cxgb3i_ddp_info *ddp = (struct cxgb3i_ddp_info *)tdev->ulp_iscsi; ddp_log_info("t3dev 0x%p, release ddp 0x%p.\n", tdev, ddp); - - if (ddp) { - tdev->ulp_iscsi = NULL; - while (i < ddp->nppods) { - struct cxgb3i_gather_list *gl = ddp->gl_map[i]; - if (gl) { - int npods = (gl->nelem + PPOD_PAGES_MAX - 1) - >> PPOD_PAGES_SHIFT; - ddp_log_info("t3dev 0x%p, ddp %d + %d.\n", - tdev, i, npods); - kfree(gl); - ddp_free_gl_skb(ddp, i, npods); - i += npods; - } else - i++; - } - cxgb3i_free_big_mem(ddp); - } + if (ddp) + kref_put(&ddp->refcnt, ddp_cleanup); } /** @@ -631,12 +666,13 @@ void cxgb3i_ddp_cleanup(struct t3cdev *tdev) */ static void ddp_init(struct t3cdev *tdev) { - struct cxgb3i_ddp_info *ddp; + struct cxgb3i_ddp_info *ddp = tdev->ulp_iscsi; struct ulp_iscsi_info uinfo; unsigned int ppmax, bits; int i, err; - if (tdev->ulp_iscsi) { + if (ddp) { + kref_get(&ddp->refcnt); ddp_log_warn("t3dev 0x%p, ddp 0x%p already set up.\n", tdev, tdev->ulp_iscsi); return; @@ -670,6 +706,7 @@ static void ddp_init(struct t3cdev *tdev) ppmax * sizeof(struct cxgb3i_gather_list *)); spin_lock_init(&ddp->map_lock); + kref_init(&ddp->refcnt); ddp->tdev = tdev; ddp->pdev = uinfo.pdev; @@ -715,6 +752,17 @@ void cxgb3i_ddp_init(struct t3cdev *tdev) { if (page_idx == DDP_PGIDX_MAX) { page_idx = cxgb3i_ddp_find_page_index(PAGE_SIZE); + + if (page_idx == DDP_PGIDX_MAX) { + ddp_log_info("system PAGE_SIZE %lu, update hw.\n", + PAGE_SIZE); + if (cxgb3i_ddp_adjust_page_table() < 0) { + ddp_log_info("PAGE_SIZE %lu, ddp disabled.\n", + PAGE_SIZE); + return; + } + page_idx = cxgb3i_ddp_find_page_index(PAGE_SIZE); + } ddp_log_info("system PAGE_SIZE %lu, ddp idx %u.\n", PAGE_SIZE, page_idx); } diff --git a/drivers/scsi/cxgb3i/cxgb3i_ddp.h b/drivers/scsi/cxgb3i/cxgb3i_ddp.h index 0d296de7cf3..87dd56b422b 100644 --- a/drivers/scsi/cxgb3i/cxgb3i_ddp.h +++ b/drivers/scsi/cxgb3i/cxgb3i_ddp.h @@ -54,6 +54,7 @@ struct cxgb3i_gather_list { * struct cxgb3i_ddp_info - cxgb3i direct data placement for pdu payload * * @list: list head to link elements + * @refcnt: ref. count * @tdev: pointer to t3cdev used by cxgb3 driver * @max_txsz: max tx packet size for ddp * @max_rxsz: max rx packet size for ddp @@ -70,6 +71,7 @@ struct cxgb3i_gather_list { */ struct cxgb3i_ddp_info { struct list_head list; + struct kref refcnt; struct t3cdev *tdev; struct pci_dev *pdev; unsigned int max_txsz; diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index c15878e8815..0a5609bb581 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -45,8 +45,6 @@ #include "fcoe.h" -static int debug_fcoe; - MODULE_AUTHOR("Open-FCoE.org"); MODULE_DESCRIPTION("FCoE"); MODULE_LICENSE("GPL v2"); @@ -305,23 +303,22 @@ static int fcoe_netdev_config(struct fc_lport *lp, struct net_device *netdev) #ifdef NETIF_F_FCOE_CRC if (netdev->features & NETIF_F_FCOE_CRC) { lp->crc_offload = 1; - printk(KERN_DEBUG "fcoe:%s supports FCCRC offload\n", - netdev->name); + FCOE_NETDEV_DBG(netdev, "Supports FCCRC offload\n"); } #endif #ifdef NETIF_F_FSO if (netdev->features & NETIF_F_FSO) { lp->seq_offload = 1; lp->lso_max = netdev->gso_max_size; - printk(KERN_DEBUG "fcoe:%s supports LSO for max len 0x%x\n", - netdev->name, lp->lso_max); + FCOE_NETDEV_DBG(netdev, "Supports LSO for max len 0x%x\n", + lp->lso_max); } #endif if (netdev->fcoe_ddp_xid) { lp->lro_enabled = 1; lp->lro_xid = netdev->fcoe_ddp_xid; - printk(KERN_DEBUG "fcoe:%s supports LRO for max xid 0x%x\n", - netdev->name, lp->lro_xid); + FCOE_NETDEV_DBG(netdev, "Supports LRO for max xid 0x%x\n", + lp->lro_xid); } skb_queue_head_init(&fc->fcoe_pending_queue); fc->fcoe_pending_queue_active = 0; @@ -407,7 +404,8 @@ static int fcoe_shost_config(struct fc_lport *lp, struct Scsi_Host *shost, /* add the new host to the SCSI-ml */ rc = scsi_add_host(lp->host, dev); if (rc) { - FC_DBG("fcoe_shost_config:error on scsi_add_host\n"); + FCOE_NETDEV_DBG(fcoe_netdev(lp), "fcoe_shost_config: " + "error on scsi_add_host\n"); return rc; } sprintf(fc_host_symbolic_name(lp->host), "%s v%s over %s", @@ -448,8 +446,7 @@ static int fcoe_if_destroy(struct net_device *netdev) BUG_ON(!netdev); - printk(KERN_DEBUG "fcoe_if_destroy:interface on %s\n", - netdev->name); + FCOE_NETDEV_DBG(netdev, "Destroying interface\n"); lp = fcoe_hostlist_lookup(netdev); if (!lp) @@ -560,8 +557,7 @@ static int fcoe_if_create(struct net_device *netdev) BUG_ON(!netdev); - printk(KERN_DEBUG "fcoe_if_create:interface on %s\n", - netdev->name); + FCOE_NETDEV_DBG(netdev, "Create Interface\n"); lp = fcoe_hostlist_lookup(netdev); if (lp) @@ -570,7 +566,7 @@ static int fcoe_if_create(struct net_device *netdev) shost = libfc_host_alloc(&fcoe_shost_template, sizeof(struct fcoe_softc)); if (!shost) { - FC_DBG("Could not allocate host structure\n"); + FCOE_NETDEV_DBG(netdev, "Could not allocate host structure\n"); return -ENOMEM; } lp = shost_priv(shost); @@ -579,7 +575,8 @@ static int fcoe_if_create(struct net_device *netdev) /* configure fc_lport, e.g., em */ rc = fcoe_lport_config(lp); if (rc) { - FC_DBG("Could not configure lport\n"); + FCOE_NETDEV_DBG(netdev, "Could not configure lport for the " + "interface\n"); goto out_host_put; } @@ -593,28 +590,32 @@ static int fcoe_if_create(struct net_device *netdev) /* configure lport network properties */ rc = fcoe_netdev_config(lp, netdev); if (rc) { - FC_DBG("Could not configure netdev for the interface\n"); + FCOE_NETDEV_DBG(netdev, "Could not configure netdev for the " + "interface\n"); goto out_netdev_cleanup; } /* configure lport scsi host properties */ rc = fcoe_shost_config(lp, shost, &netdev->dev); if (rc) { - FC_DBG("Could not configure shost for lport\n"); + FCOE_NETDEV_DBG(netdev, "Could not configure shost for the " + "interface\n"); goto out_netdev_cleanup; } /* lport exch manager allocation */ rc = fcoe_em_config(lp); if (rc) { - FC_DBG("Could not configure em for lport\n"); + FCOE_NETDEV_DBG(netdev, "Could not configure the EM for the " + "interface\n"); goto out_netdev_cleanup; } /* Initialize the library */ rc = fcoe_libfc_config(lp, &fcoe_libfc_fcn_templ); if (rc) { - FC_DBG("Could not configure libfc for lport!\n"); + FCOE_NETDEV_DBG(netdev, "Could not configure libfc for the " + "interface\n"); goto out_lp_destroy; } @@ -653,7 +654,7 @@ static int __init fcoe_if_init(void) fc_attach_transport(&fcoe_transport_function); if (!scsi_transport_fcoe_sw) { - printk(KERN_ERR "fcoe_init:fc_attach_transport() failed\n"); + printk(KERN_ERR "fcoe: Failed to attach to the FC transport\n"); return -ENODEV; } @@ -714,7 +715,7 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) unsigned targ_cpu = smp_processor_id(); #endif /* CONFIG_SMP */ - printk(KERN_DEBUG "fcoe: Destroying receive thread for CPU %d\n", cpu); + FCOE_DBG("Destroying receive thread for CPU %d\n", cpu); /* Prevent any new skbs from being queued for this CPU. */ p = &per_cpu(fcoe_percpu, cpu); @@ -736,8 +737,8 @@ static void fcoe_percpu_thread_destroy(unsigned int cpu) p0 = &per_cpu(fcoe_percpu, targ_cpu); spin_lock_bh(&p0->fcoe_rx_list.lock); if (p0->thread) { - FC_DBG("Moving frames from CPU %d to CPU %d\n", - cpu, targ_cpu); + FCOE_DBG("Moving frames from CPU %d to CPU %d\n", + cpu, targ_cpu); while ((skb = __skb_dequeue(&p->fcoe_rx_list)) != NULL) __skb_queue_tail(&p0->fcoe_rx_list, skb); @@ -803,12 +804,12 @@ static int fcoe_cpu_callback(struct notifier_block *nfb, switch (action) { case CPU_ONLINE: case CPU_ONLINE_FROZEN: - FC_DBG("CPU %x online: Create Rx thread\n", cpu); + FCOE_DBG("CPU %x online: Create Rx thread\n", cpu); fcoe_percpu_thread_create(cpu); break; case CPU_DEAD: case CPU_DEAD_FROZEN: - FC_DBG("CPU %x offline: Remove Rx thread\n", cpu); + FCOE_DBG("CPU %x offline: Remove Rx thread\n", cpu); fcoe_percpu_thread_destroy(cpu); break; default: @@ -846,24 +847,21 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, fc = container_of(ptype, struct fcoe_softc, fcoe_packet_type); lp = fc->ctlr.lp; if (unlikely(lp == NULL)) { - FC_DBG("cannot find hba structure"); + FCOE_NETDEV_DBG(dev, "Cannot find hba structure"); goto err2; } if (!lp->link_up) goto err2; - if (unlikely(debug_fcoe)) { - FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p tail:%p " - "end:%p sum:%d dev:%s", skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : "<NULL>"); - - } + FCOE_NETDEV_DBG(dev, "skb_info: len:%d data_len:%d head:%p " + "data:%p tail:%p end:%p sum:%d dev:%s", + skb->len, skb->data_len, skb->head, skb->data, + skb_tail_pointer(skb), skb_end_pointer(skb), + skb->csum, skb->dev ? skb->dev->name : "<NULL>"); /* check for FCOE packet type */ if (unlikely(eth_hdr(skb)->h_proto != htons(ETH_P_FCOE))) { - FC_DBG("wrong FC type frame"); + FCOE_NETDEV_DBG(dev, "Wrong FC type frame"); goto err; } @@ -901,8 +899,9 @@ int fcoe_rcv(struct sk_buff *skb, struct net_device *dev, * the first CPU now. For non-SMP systems this * will check the same CPU twice. */ - FC_DBG("CPU is online, but no receive thread ready " - "for incoming skb- using first online CPU.\n"); + FCOE_NETDEV_DBG(dev, "CPU is online, but no receive thread " + "ready for incoming skb- using first online " + "CPU.\n"); spin_unlock_bh(&fps->fcoe_rx_list.lock); cpu = first_cpu(cpu_online_map); @@ -1201,19 +1200,17 @@ int fcoe_percpu_receive_thread(void *arg) fr = fcoe_dev_from_skb(skb); lp = fr->fr_dev; if (unlikely(lp == NULL)) { - FC_DBG("invalid HBA Structure"); + FCOE_NETDEV_DBG(skb->dev, "Invalid HBA Structure"); kfree_skb(skb); continue; } - if (unlikely(debug_fcoe)) { - FC_DBG("skb_info: len:%d data_len:%d head:%p data:%p " - "tail:%p end:%p sum:%d dev:%s", - skb->len, skb->data_len, - skb->head, skb->data, skb_tail_pointer(skb), - skb_end_pointer(skb), skb->csum, - skb->dev ? skb->dev->name : "<NULL>"); - } + FCOE_NETDEV_DBG(skb->dev, "skb_info: len:%d data_len:%d " + "head:%p data:%p tail:%p end:%p sum:%d dev:%s", + skb->len, skb->data_len, + skb->head, skb->data, skb_tail_pointer(skb), + skb_end_pointer(skb), skb->csum, + skb->dev ? skb->dev->name : "<NULL>"); /* * Save source MAC address before discarding header. @@ -1233,7 +1230,7 @@ int fcoe_percpu_receive_thread(void *arg) stats = fc_lport_get_stats(lp); if (unlikely(FC_FCOE_DECAPS_VER(hp) != FC_FCOE_VER)) { if (stats->ErrorFrames < 5) - printk(KERN_WARNING "FCoE version " + printk(KERN_WARNING "fcoe: FCoE version " "mismatch: The frame has " "version %x, but the " "initiator supports version " @@ -1286,7 +1283,7 @@ int fcoe_percpu_receive_thread(void *arg) if (fr_flags(fp) & FCPHF_CRC_UNCHECKED) { if (le32_to_cpu(fr_crc(fp)) != ~crc32(~0, skb->data, fr_len)) { - if (debug_fcoe || stats->InvalidCRCCount < 5) + if (stats->InvalidCRCCount < 5) printk(KERN_WARNING "fcoe: dropping " "frame with CRC error\n"); stats->InvalidCRCCount++; @@ -1432,7 +1429,8 @@ static int fcoe_device_notification(struct notifier_block *notifier, case NETDEV_REGISTER: break; default: - FC_DBG("Unknown event %ld from netdev netlink\n", event); + FCOE_NETDEV_DBG(real_dev, "Unknown event %ld " + "from netdev netlink\n", event); } if (link_possible && !fcoe_link_ok(lp)) fcoe_ctlr_link_up(&fc->ctlr); @@ -1505,8 +1503,8 @@ static int fcoe_ethdrv_get(const struct net_device *netdev) owner = fcoe_netdev_to_module_owner(netdev); if (owner) { - printk(KERN_DEBUG "fcoe:hold driver module %s for %s\n", - module_name(owner), netdev->name); + FCOE_NETDEV_DBG(netdev, "Hold driver module %s\n", + module_name(owner)); return try_module_get(owner); } return -ENODEV; @@ -1527,8 +1525,8 @@ static int fcoe_ethdrv_put(const struct net_device *netdev) owner = fcoe_netdev_to_module_owner(netdev); if (owner) { - printk(KERN_DEBUG "fcoe:release driver module %s for %s\n", - module_name(owner), netdev->name); + FCOE_NETDEV_DBG(netdev, "Release driver module %s\n", + module_name(owner)); module_put(owner); return 0; } @@ -1559,7 +1557,7 @@ static int fcoe_destroy(const char *buffer, struct kernel_param *kp) } rc = fcoe_if_destroy(netdev); if (rc) { - printk(KERN_ERR "fcoe: fcoe_if_destroy(%s) failed\n", + printk(KERN_ERR "fcoe: Failed to destroy interface (%s)\n", netdev->name); rc = -EIO; goto out_putdev; @@ -1598,7 +1596,7 @@ static int fcoe_create(const char *buffer, struct kernel_param *kp) rc = fcoe_if_create(netdev); if (rc) { - printk(KERN_ERR "fcoe: fcoe_if_create(%s) failed\n", + printk(KERN_ERR "fcoe: Failed to create interface (%s)\n", netdev->name); fcoe_ethdrv_put(netdev); rc = -EIO; diff --git a/drivers/scsi/fcoe/fcoe.h b/drivers/scsi/fcoe/fcoe.h index a1eb8c1988b..0d724fa0898 100644 --- a/drivers/scsi/fcoe/fcoe.h +++ b/drivers/scsi/fcoe/fcoe.h @@ -40,6 +40,30 @@ #define FCOE_MIN_XID 0x0001 /* the min xid supported by fcoe_sw */ #define FCOE_MAX_XID 0x07ef /* the max xid supported by fcoe_sw */ +unsigned int fcoe_debug_logging; +module_param_named(debug_logging, fcoe_debug_logging, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); + +#define FCOE_LOGGING 0x01 /* General logging, not categorized */ +#define FCOE_NETDEV_LOGGING 0x02 /* Netdevice logging */ + +#define FCOE_CHECK_LOGGING(LEVEL, CMD) \ +do { \ + if (unlikely(fcoe_debug_logging & LEVEL)) \ + do { \ + CMD; \ + } while (0); \ +} while (0); + +#define FCOE_DBG(fmt, args...) \ + FCOE_CHECK_LOGGING(FCOE_LOGGING, \ + printk(KERN_INFO "fcoe: " fmt, ##args);) + +#define FCOE_NETDEV_DBG(netdev, fmt, args...) \ + FCOE_CHECK_LOGGING(FCOE_NETDEV_LOGGING, \ + printk(KERN_INFO "fcoe: %s" fmt, \ + netdev->name, ##args);) + /* * this percpu struct for fcoe */ diff --git a/drivers/scsi/fcoe/libfcoe.c b/drivers/scsi/fcoe/libfcoe.c index 2f5bc7fd3fa..f544340d318 100644 --- a/drivers/scsi/fcoe/libfcoe.c +++ b/drivers/scsi/fcoe/libfcoe.c @@ -56,15 +56,28 @@ static void fcoe_ctlr_recv_work(struct work_struct *); static u8 fcoe_all_fcfs[ETH_ALEN] = FIP_ALL_FCF_MACS; -static u32 fcoe_ctlr_debug; /* 1 for basic, 2 for noisy debug */ +unsigned int libfcoe_debug_logging; +module_param_named(debug_logging, libfcoe_debug_logging, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); -#define FIP_DBG_LVL(level, fmt, args...) \ +#define LIBFCOE_LOGGING 0x01 /* General logging, not categorized */ +#define LIBFCOE_FIP_LOGGING 0x02 /* FIP logging */ + +#define LIBFCOE_CHECK_LOGGING(LEVEL, CMD) \ +do { \ + if (unlikely(libfcoe_debug_logging & LEVEL)) \ do { \ - if (fcoe_ctlr_debug >= (level)) \ - FC_DBG(fmt, ##args); \ - } while (0) + CMD; \ + } while (0); \ +} while (0); + +#define LIBFCOE_DBG(fmt, args...) \ + LIBFCOE_CHECK_LOGGING(LIBFCOE_LOGGING, \ + printk(KERN_INFO "libfcoe: " fmt, ##args);) -#define FIP_DBG(fmt, args...) FIP_DBG_LVL(1, fmt, ##args) +#define LIBFCOE_FIP_DBG(fmt, args...) \ + LIBFCOE_CHECK_LOGGING(LIBFCOE_FIP_LOGGING, \ + printk(KERN_INFO "fip: " fmt, ##args);) /* * Return non-zero if FCF fcoe_size has been validated. @@ -243,7 +256,7 @@ void fcoe_ctlr_link_up(struct fcoe_ctlr *fip) fip->last_link = 1; fip->link = 1; spin_unlock_bh(&fip->lock); - FIP_DBG("%s", "setting AUTO mode.\n"); + LIBFCOE_FIP_DBG("%s", "setting AUTO mode.\n"); fc_linkup(fip->lp); fcoe_ctlr_solicit(fip, NULL); } else @@ -614,7 +627,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); if (!is_valid_ether_addr(fcf->fcf_mac)) { - FIP_DBG("invalid MAC addr in FIP adv\n"); + LIBFCOE_FIP_DBG("Invalid MAC address " + "in FIP adv\n"); return -EINVAL; } break; @@ -647,8 +661,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) case FIP_DT_LOGO: case FIP_DT_ELP: default: - FIP_DBG("unexpected descriptor type %x in FIP adv\n", - desc->fip_dtype); + LIBFCOE_FIP_DBG("unexpected descriptor type %x " + "in FIP adv\n", desc->fip_dtype); /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) return -EINVAL; @@ -664,8 +678,8 @@ static int fcoe_ctlr_parse_adv(struct sk_buff *skb, struct fcoe_fcf *fcf) return 0; len_err: - FIP_DBG("FIP length error in descriptor type %x len %zu\n", - desc->fip_dtype, dlen); + LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n", + desc->fip_dtype, dlen); return -EINVAL; } @@ -728,9 +742,10 @@ static void fcoe_ctlr_recv_adv(struct fcoe_ctlr *fip, struct sk_buff *skb) } mtu_valid = fcoe_ctlr_mtu_valid(fcf); fcf->time = jiffies; - FIP_DBG_LVL(found ? 2 : 1, "%s FCF for fab %llx map %x val %d\n", - found ? "old" : "new", - fcf->fabric_name, fcf->fc_map, mtu_valid); + if (!found) { + LIBFCOE_FIP_DBG("New FCF for fab %llx map %x val %d\n", + fcf->fabric_name, fcf->fc_map, mtu_valid); + } /* * If this advertisement is not solicited and our max receive size @@ -807,7 +822,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) ((struct fip_mac_desc *)desc)->fd_mac, ETH_ALEN); if (!is_valid_ether_addr(granted_mac)) { - FIP_DBG("invalid MAC addrs in FIP ELS\n"); + LIBFCOE_FIP_DBG("Invalid MAC address " + "in FIP ELS\n"); goto drop; } break; @@ -825,8 +841,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) els_dtype = desc->fip_dtype; break; default: - FIP_DBG("unexpected descriptor type %x " - "in FIP adv\n", desc->fip_dtype); + LIBFCOE_FIP_DBG("unexpected descriptor type %x " + "in FIP adv\n", desc->fip_dtype); /* standard says ignore unknown descriptors >= 128 */ if (desc->fip_dtype < FIP_DT_VENDOR_BASE) goto drop; @@ -867,8 +883,8 @@ static void fcoe_ctlr_recv_els(struct fcoe_ctlr *fip, struct sk_buff *skb) return; len_err: - FIP_DBG("FIP length error in descriptor type %x len %zu\n", - desc->fip_dtype, dlen); + LIBFCOE_FIP_DBG("FIP length error in descriptor type %x len %zu\n", + desc->fip_dtype, dlen); drop: kfree_skb(skb); } @@ -894,7 +910,7 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, struct fc_lport *lp = fip->lp; u32 desc_mask; - FIP_DBG("Clear Virtual Link received\n"); + LIBFCOE_FIP_DBG("Clear Virtual Link received\n"); if (!fcf) return; if (!fcf || !fc_host_port_id(lp->host)) @@ -952,9 +968,9 @@ static void fcoe_ctlr_recv_clr_vlink(struct fcoe_ctlr *fip, * reset only if all required descriptors were present and valid. */ if (desc_mask) { - FIP_DBG("missing descriptors mask %x\n", desc_mask); + LIBFCOE_FIP_DBG("missing descriptors mask %x\n", desc_mask); } else { - FIP_DBG("performing Clear Virtual Link\n"); + LIBFCOE_FIP_DBG("performing Clear Virtual Link\n"); fcoe_ctlr_reset(fip, FIP_ST_ENABLED); } } @@ -1002,10 +1018,6 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) op = ntohs(fiph->fip_op); sub = fiph->fip_subcode; - FIP_DBG_LVL(2, "ver %x op %x/%x dl %x fl %x\n", - FIP_VER_DECAPS(fiph->fip_ver), op, sub, - ntohs(fiph->fip_dl_len), ntohs(fiph->fip_flags)); - if (FIP_VER_DECAPS(fiph->fip_ver) != FIP_VER) goto drop; if (ntohs(fiph->fip_dl_len) * FIP_BPW + sizeof(*fiph) > skb->len) @@ -1017,7 +1029,7 @@ static int fcoe_ctlr_recv_handler(struct fcoe_ctlr *fip, struct sk_buff *skb) fip->map_dest = 0; fip->state = FIP_ST_ENABLED; state = FIP_ST_ENABLED; - FIP_DBG("using FIP mode\n"); + LIBFCOE_FIP_DBG("Using FIP mode\n"); } spin_unlock_bh(&fip->lock); if (state != FIP_ST_ENABLED) @@ -1052,14 +1064,15 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) struct fcoe_fcf *best = NULL; list_for_each_entry(fcf, &fip->fcfs, list) { - FIP_DBG("consider FCF for fab %llx VFID %d map %x val %d\n", - fcf->fabric_name, fcf->vfid, - fcf->fc_map, fcoe_ctlr_mtu_valid(fcf)); + LIBFCOE_FIP_DBG("consider FCF for fab %llx VFID %d map %x " + "val %d\n", fcf->fabric_name, fcf->vfid, + fcf->fc_map, fcoe_ctlr_mtu_valid(fcf)); if (!fcoe_ctlr_fcf_usable(fcf)) { - FIP_DBG("FCF for fab %llx map %x %svalid %savailable\n", - fcf->fabric_name, fcf->fc_map, - (fcf->flags & FIP_FL_SOL) ? "" : "in", - (fcf->flags & FIP_FL_AVAIL) ? "" : "un"); + LIBFCOE_FIP_DBG("FCF for fab %llx map %x %svalid " + "%savailable\n", fcf->fabric_name, + fcf->fc_map, (fcf->flags & FIP_FL_SOL) + ? "" : "in", (fcf->flags & FIP_FL_AVAIL) + ? "" : "un"); continue; } if (!best) { @@ -1069,7 +1082,8 @@ static void fcoe_ctlr_select(struct fcoe_ctlr *fip) if (fcf->fabric_name != best->fabric_name || fcf->vfid != best->vfid || fcf->fc_map != best->fc_map) { - FIP_DBG("conflicting fabric, VFID, or FC-MAP\n"); + LIBFCOE_FIP_DBG("Conflicting fabric, VFID, " + "or FC-MAP\n"); return; } if (fcf->pri < best->pri) @@ -1113,7 +1127,7 @@ static void fcoe_ctlr_timeout(unsigned long arg) if (sel != fcf) { fcf = sel; /* the old FCF may have been freed */ if (sel) { - printk(KERN_INFO "host%d: FIP selected " + printk(KERN_INFO "libfcoe: host%d: FIP selected " "Fibre-Channel Forwarder MAC %s\n", fip->lp->host->host_no, print_mac(buf, sel->fcf_mac)); @@ -1123,7 +1137,7 @@ static void fcoe_ctlr_timeout(unsigned long arg) fip->ctlr_ka_time = jiffies + sel->fka_period; fip->link = 1; } else { - printk(KERN_NOTICE "host%d: " + printk(KERN_NOTICE "libfcoe: host%d: " "FIP Fibre-Channel Forwarder timed out. " "Starting FCF discovery.\n", fip->lp->host->host_no); @@ -1247,7 +1261,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) return -EINVAL; } fip->state = FIP_ST_NON_FIP; - FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n"); + LIBFCOE_FIP_DBG("received FLOGI LS_ACC using non-FIP mode\n"); /* * FLOGI accepted. @@ -1276,7 +1290,7 @@ int fcoe_ctlr_recv_flogi(struct fcoe_ctlr *fip, struct fc_frame *fp, u8 *sa) memcpy(fip->dest_addr, sa, ETH_ALEN); fip->map_dest = 0; if (fip->state == FIP_ST_NON_FIP) - FIP_DBG("received FLOGI REQ, " + LIBFCOE_FIP_DBG("received FLOGI REQ, " "using non-FIP mode\n"); fip->state = FIP_ST_NON_FIP; } diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 89d41a424b3..5fd2da494d0 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -40,7 +40,7 @@ #include "scsi_logging.h" -static int scsi_host_next_hn; /* host_no for next new host */ +static atomic_t scsi_host_next_hn; /* host_no for next new host */ static void scsi_host_cls_release(struct device *dev) @@ -333,7 +333,11 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) mutex_init(&shost->scan_mutex); - shost->host_no = scsi_host_next_hn++; /* XXX(hch): still racy */ + /* + * subtract one because we increment first then return, but we need to + * know what the next host number was before increment + */ + shost->host_no = atomic_inc_return(&scsi_host_next_hn) - 1; shost->dma_channel = 0xff; /* These three are default values which can be overridden */ diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c index b4b805e8d7d..166d96450a0 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.c +++ b/drivers/scsi/ibmvscsi/ibmvfc.c @@ -2254,10 +2254,13 @@ static void ibmvfc_handle_async(struct ibmvfc_async_crq *crq, continue; if (crq->node_name && tgt->ids.node_name != crq->node_name) continue; - ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); + if (tgt->need_login && crq->event == IBMVFC_AE_ELS_LOGO) + tgt->logo_rcvd = 1; + if (!tgt->need_login || crq->event == IBMVFC_AE_ELS_PLOGI) { + ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); + ibmvfc_reinit_host(vhost); + } } - - ibmvfc_reinit_host(vhost); break; case IBMVFC_AE_LINK_DOWN: case IBMVFC_AE_ADAPTER_FAILED: @@ -2783,27 +2786,27 @@ static void ibmvfc_tasklet(void *data) spin_lock_irqsave(vhost->host->host_lock, flags); while (!done) { - /* Pull all the valid messages off the CRQ */ - while ((crq = ibmvfc_next_crq(vhost)) != NULL) { - ibmvfc_handle_crq(crq, vhost); - crq->valid = 0; - } - /* Pull all the valid messages off the async CRQ */ while ((async = ibmvfc_next_async_crq(vhost)) != NULL) { ibmvfc_handle_async(async, vhost); async->valid = 0; } - vio_enable_interrupts(vdev); - if ((crq = ibmvfc_next_crq(vhost)) != NULL) { - vio_disable_interrupts(vdev); + /* Pull all the valid messages off the CRQ */ + while ((crq = ibmvfc_next_crq(vhost)) != NULL) { ibmvfc_handle_crq(crq, vhost); crq->valid = 0; - } else if ((async = ibmvfc_next_async_crq(vhost)) != NULL) { + } + + vio_enable_interrupts(vdev); + if ((async = ibmvfc_next_async_crq(vhost)) != NULL) { vio_disable_interrupts(vdev); ibmvfc_handle_async(async, vhost); async->valid = 0; + } else if ((crq = ibmvfc_next_crq(vhost)) != NULL) { + vio_disable_interrupts(vdev); + ibmvfc_handle_crq(crq, vhost); + crq->valid = 0; } else done = 1; } @@ -2927,7 +2930,11 @@ static void ibmvfc_tgt_prli_done(struct ibmvfc_event *evt) break; case IBMVFC_MAD_FAILED: default: - if (ibmvfc_retry_cmd(rsp->status, rsp->error)) + if ((rsp->status & IBMVFC_VIOS_FAILURE) && rsp->error == IBMVFC_PLOGI_REQUIRED) + level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); + else if (tgt->logo_rcvd) + level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_plogi); + else if (ibmvfc_retry_cmd(rsp->status, rsp->error)) level += ibmvfc_retry_tgt_init(tgt, ibmvfc_tgt_send_prli); else ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_DEL_RPORT); @@ -3054,6 +3061,7 @@ static void ibmvfc_tgt_send_plogi(struct ibmvfc_target *tgt) return; kref_get(&tgt->kref); + tgt->logo_rcvd = 0; evt = ibmvfc_get_event(vhost); vhost->discovery_threads++; ibmvfc_set_tgt_action(tgt, IBMVFC_TGT_ACTION_INIT_WAIT); diff --git a/drivers/scsi/ibmvscsi/ibmvfc.h b/drivers/scsi/ibmvscsi/ibmvfc.h index c2668d7d67f..007fa1c9ef1 100644 --- a/drivers/scsi/ibmvscsi/ibmvfc.h +++ b/drivers/scsi/ibmvscsi/ibmvfc.h @@ -605,6 +605,7 @@ struct ibmvfc_target { int need_login; int add_rport; int init_retries; + int logo_rcvd; u32 cancel_key; struct ibmvfc_service_parms service_parms; struct ibmvfc_service_parms service_parms_change; diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index 0f8bc772b11..5f045505a1f 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -131,13 +131,13 @@ static const struct ipr_chip_cfg_t ipr_chip_cfg[] = { }; static const struct ipr_chip_t ipr_chip[] = { - { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, &ipr_chip_cfg[0] }, - { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, &ipr_chip_cfg[0] }, - { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, &ipr_chip_cfg[0] }, - { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, &ipr_chip_cfg[0] }, - { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, &ipr_chip_cfg[0] }, - { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, &ipr_chip_cfg[1] }, - { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, &ipr_chip_cfg[1] } + { PCI_VENDOR_ID_MYLEX, PCI_DEVICE_ID_IBM_GEMSTONE, IPR_USE_LSI, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_CITRINE, IPR_USE_LSI, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_OBSIDIAN, IPR_USE_LSI, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, IPR_USE_LSI, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN_E, IPR_USE_MSI, &ipr_chip_cfg[0] }, + { PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_SNIPE, IPR_USE_LSI, &ipr_chip_cfg[1] }, + { PCI_VENDOR_ID_ADAPTEC2, PCI_DEVICE_ID_ADAPTEC2_SCAMP, IPR_USE_LSI, &ipr_chip_cfg[1] } }; static int ipr_max_bus_speeds [] = { @@ -7367,6 +7367,7 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg, INIT_LIST_HEAD(&ioa_cfg->used_res_q); INIT_WORK(&ioa_cfg->work_q, ipr_worker_thread); init_waitqueue_head(&ioa_cfg->reset_wait_q); + init_waitqueue_head(&ioa_cfg->msi_wait_q); ioa_cfg->sdt_state = INACTIVE; if (ipr_enable_cache) ioa_cfg->cache_state = CACHE_ENABLED; @@ -7398,25 +7399,108 @@ static void __devinit ipr_init_ioa_cfg(struct ipr_ioa_cfg *ioa_cfg, } /** - * ipr_get_chip_cfg - Find adapter chip configuration + * ipr_get_chip_info - Find adapter chip information * @dev_id: PCI device id struct * * Return value: - * ptr to chip config on success / NULL on failure + * ptr to chip information on success / NULL on failure **/ -static const struct ipr_chip_cfg_t * __devinit -ipr_get_chip_cfg(const struct pci_device_id *dev_id) +static const struct ipr_chip_t * __devinit +ipr_get_chip_info(const struct pci_device_id *dev_id) { int i; for (i = 0; i < ARRAY_SIZE(ipr_chip); i++) if (ipr_chip[i].vendor == dev_id->vendor && ipr_chip[i].device == dev_id->device) - return ipr_chip[i].cfg; + return &ipr_chip[i]; return NULL; } /** + * ipr_test_intr - Handle the interrupt generated in ipr_test_msi(). + * @pdev: PCI device struct + * + * Description: Simply set the msi_received flag to 1 indicating that + * Message Signaled Interrupts are supported. + * + * Return value: + * 0 on success / non-zero on failure + **/ +static irqreturn_t __devinit ipr_test_intr(int irq, void *devp) +{ + struct ipr_ioa_cfg *ioa_cfg = (struct ipr_ioa_cfg *)devp; + unsigned long lock_flags = 0; + irqreturn_t rc = IRQ_HANDLED; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + + ioa_cfg->msi_received = 1; + wake_up(&ioa_cfg->msi_wait_q); + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + return rc; +} + +/** + * ipr_test_msi - Test for Message Signaled Interrupt (MSI) support. + * @pdev: PCI device struct + * + * Description: The return value from pci_enable_msi() can not always be + * trusted. This routine sets up and initiates a test interrupt to determine + * if the interrupt is received via the ipr_test_intr() service routine. + * If the tests fails, the driver will fall back to LSI. + * + * Return value: + * 0 on success / non-zero on failure + **/ +static int __devinit ipr_test_msi(struct ipr_ioa_cfg *ioa_cfg, + struct pci_dev *pdev) +{ + int rc; + volatile u32 int_reg; + unsigned long lock_flags = 0; + + ENTER; + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + init_waitqueue_head(&ioa_cfg->msi_wait_q); + ioa_cfg->msi_received = 0; + ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); + writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.clr_interrupt_mask_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_mask_reg); + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + rc = request_irq(pdev->irq, ipr_test_intr, 0, IPR_NAME, ioa_cfg); + if (rc) { + dev_err(&pdev->dev, "Can not assign irq %d\n", pdev->irq); + return rc; + } else if (ipr_debug) + dev_info(&pdev->dev, "IRQ assigned: %d\n", pdev->irq); + + writel(IPR_PCII_IO_DEBUG_ACKNOWLEDGE, ioa_cfg->regs.sense_interrupt_reg); + int_reg = readl(ioa_cfg->regs.sense_interrupt_reg); + wait_event_timeout(ioa_cfg->msi_wait_q, ioa_cfg->msi_received, HZ); + ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); + + spin_lock_irqsave(ioa_cfg->host->host_lock, lock_flags); + if (!ioa_cfg->msi_received) { + /* MSI test failed */ + dev_info(&pdev->dev, "MSI test failed. Falling back to LSI.\n"); + rc = -EOPNOTSUPP; + } else if (ipr_debug) + dev_info(&pdev->dev, "MSI test succeeded.\n"); + + spin_unlock_irqrestore(ioa_cfg->host->host_lock, lock_flags); + + free_irq(pdev->irq, ioa_cfg); + + LEAVE; + + return rc; +} + +/** * ipr_probe_ioa - Allocates memory and does first stage of initialization * @pdev: PCI device struct * @dev_id: PCI device id struct @@ -7441,11 +7525,6 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, goto out; } - if (!(rc = pci_enable_msi(pdev))) - dev_info(&pdev->dev, "MSI enabled\n"); - else if (ipr_debug) - dev_info(&pdev->dev, "Cannot enable MSI\n"); - dev_info(&pdev->dev, "Found IOA with IRQ: %d\n", pdev->irq); host = scsi_host_alloc(&driver_template, sizeof(*ioa_cfg)); @@ -7461,14 +7540,16 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, ata_host_init(&ioa_cfg->ata_host, &pdev->dev, sata_port_info.flags, &ipr_sata_ops); - ioa_cfg->chip_cfg = ipr_get_chip_cfg(dev_id); + ioa_cfg->ipr_chip = ipr_get_chip_info(dev_id); - if (!ioa_cfg->chip_cfg) { + if (!ioa_cfg->ipr_chip) { dev_err(&pdev->dev, "Unknown adapter chipset 0x%04X 0x%04X\n", dev_id->vendor, dev_id->device); goto out_scsi_host_put; } + ioa_cfg->chip_cfg = ioa_cfg->ipr_chip->cfg; + if (ipr_transop_timeout) ioa_cfg->transop_timeout = ipr_transop_timeout; else if (dev_id->driver_data & IPR_USE_LONG_TRANSOP_TIMEOUT) @@ -7519,6 +7600,18 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, goto cleanup_nomem; } + /* Enable MSI style interrupts if they are supported. */ + if (ioa_cfg->ipr_chip->intr_type == IPR_USE_MSI && !pci_enable_msi(pdev)) { + rc = ipr_test_msi(ioa_cfg, pdev); + if (rc == -EOPNOTSUPP) + pci_disable_msi(pdev); + else if (rc) + goto out_msi_disable; + else + dev_info(&pdev->dev, "MSI enabled with IRQ: %d\n", pdev->irq); + } else if (ipr_debug) + dev_info(&pdev->dev, "Cannot enable MSI.\n"); + /* Save away PCI config space for use following IOA reset */ rc = pci_save_state(pdev); @@ -7556,7 +7649,9 @@ static int __devinit ipr_probe_ioa(struct pci_dev *pdev, ioa_cfg->ioa_unit_checked = 1; ipr_mask_and_clear_interrupts(ioa_cfg, ~IPR_PCII_IOA_TRANS_TO_OPER); - rc = request_irq(pdev->irq, ipr_isr, IRQF_SHARED, IPR_NAME, ioa_cfg); + rc = request_irq(pdev->irq, ipr_isr, + ioa_cfg->msi_received ? 0 : IRQF_SHARED, + IPR_NAME, ioa_cfg); if (rc) { dev_err(&pdev->dev, "Couldn't register IRQ %d! rc=%d\n", @@ -7583,12 +7678,13 @@ cleanup_nolog: ipr_free_mem(ioa_cfg); cleanup_nomem: iounmap(ipr_regs); +out_msi_disable: + pci_disable_msi(pdev); out_release_regions: pci_release_regions(pdev); out_scsi_host_put: scsi_host_put(host); out_disable: - pci_disable_msi(pdev); pci_disable_device(pdev); goto out; } diff --git a/drivers/scsi/ipr.h b/drivers/scsi/ipr.h index 79a3ae4fb2c..4b63dd6b1c8 100644 --- a/drivers/scsi/ipr.h +++ b/drivers/scsi/ipr.h @@ -37,8 +37,8 @@ /* * Literals */ -#define IPR_DRIVER_VERSION "2.4.2" -#define IPR_DRIVER_DATE "(January 21, 2009)" +#define IPR_DRIVER_VERSION "2.4.3" +#define IPR_DRIVER_DATE "(June 10, 2009)" /* * IPR_MAX_CMD_PER_LUN: This defines the maximum number of outstanding @@ -1025,6 +1025,9 @@ struct ipr_chip_cfg_t { struct ipr_chip_t { u16 vendor; u16 device; + u16 intr_type; +#define IPR_USE_LSI 0x00 +#define IPR_USE_MSI 0x01 const struct ipr_chip_cfg_t *cfg; }; @@ -1094,6 +1097,7 @@ struct ipr_ioa_cfg { u8 needs_hard_reset:1; u8 dual_raid:1; u8 needs_warm_reset:1; + u8 msi_received:1; u8 revid; @@ -1159,6 +1163,7 @@ struct ipr_ioa_cfg { unsigned int transop_timeout; const struct ipr_chip_cfg_t *chip_cfg; + const struct ipr_chip_t *ipr_chip; void __iomem *hdw_dma_regs; /* iomapped PCI memory space */ unsigned long hdw_dma_regs_pci; /* raw PCI memory space */ @@ -1179,6 +1184,7 @@ struct ipr_ioa_cfg { struct work_struct work_q; wait_queue_head_t reset_wait_q; + wait_queue_head_t msi_wait_q; struct ipr_dump *dump; enum ipr_sdt_state sdt_state; diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index b7c092d63bb..518dbd91df8 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -253,8 +253,6 @@ static int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn, if (r < 0) { iscsi_tcp_segment_unmap(segment); - if (copied || r == -EAGAIN) - break; return r; } copied += r; @@ -275,11 +273,17 @@ static int iscsi_sw_tcp_xmit(struct iscsi_conn *conn) while (1) { rc = iscsi_sw_tcp_xmit_segment(tcp_conn, segment); - if (rc < 0) { + /* + * We may not have been able to send data because the conn + * is getting stopped. libiscsi will know so propogate err + * for it to do the right thing. + */ + if (rc == -EAGAIN) + return rc; + else if (rc < 0) { rc = ISCSI_ERR_XMIT_FAILED; goto error; - } - if (rc == 0) + } else if (rc == 0) break; consumed += rc; diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 4c880656990..6fabf66972b 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -45,14 +45,6 @@ #define FC_DISC_DELAY 3 -static int fc_disc_debug; - -#define FC_DEBUG_DISC(fmt...) \ - do { \ - if (fc_disc_debug) \ - FC_DBG(fmt); \ - } while (0) - static void fc_disc_gpn_ft_req(struct fc_disc *); static void fc_disc_gpn_ft_resp(struct fc_seq *, struct fc_frame *, void *); static int fc_disc_new_target(struct fc_disc *, struct fc_rport *, @@ -137,8 +129,8 @@ static void fc_disc_rport_callback(struct fc_lport *lport, struct fc_rport_libfc_priv *rdata = rport->dd_data; struct fc_disc *disc = &lport->disc; - FC_DEBUG_DISC("Received a %d event for port (%6x)\n", event, - rport->port_id); + FC_DISC_DBG(disc, "Received a %d event for port (%6x)\n", event, + rport->port_id); switch (event) { case RPORT_EV_CREATED: @@ -191,8 +183,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, lport = disc->lport; - FC_DEBUG_DISC("Received an RSCN event on port (%6x)\n", - fc_host_port_id(lport->host)); + FC_DISC_DBG(disc, "Received an RSCN event\n"); /* make sure the frame contains an RSCN message */ rp = fc_frame_payload_get(fp, sizeof(*rp)); @@ -225,8 +216,8 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, */ switch (fmt) { case ELS_ADDR_FMT_PORT: - FC_DEBUG_DISC("Port address format for port (%6x)\n", - ntoh24(pp->rscn_fid)); + FC_DISC_DBG(disc, "Port address format for port " + "(%6x)\n", ntoh24(pp->rscn_fid)); dp = kzalloc(sizeof(*dp), GFP_KERNEL); if (!dp) { redisc = 1; @@ -243,19 +234,19 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, case ELS_ADDR_FMT_DOM: case ELS_ADDR_FMT_FAB: default: - FC_DEBUG_DISC("Address format is (%d)\n", fmt); + FC_DISC_DBG(disc, "Address format is (%d)\n", fmt); redisc = 1; break; } } lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); if (redisc) { - FC_DEBUG_DISC("RSCN received: rediscovering\n"); + FC_DISC_DBG(disc, "RSCN received: rediscovering\n"); fc_disc_restart(disc); } else { - FC_DEBUG_DISC("RSCN received: not rediscovering. " - "redisc %d state %d in_prog %d\n", - redisc, lport->state, disc->pending); + FC_DISC_DBG(disc, "RSCN received: not rediscovering. " + "redisc %d state %d in_prog %d\n", + redisc, lport->state, disc->pending); list_for_each_entry_safe(dp, next, &disc_ports, peers) { list_del(&dp->peers); rport = lport->tt.rport_lookup(lport, dp->ids.port_id); @@ -270,7 +261,7 @@ static void fc_disc_recv_rscn_req(struct fc_seq *sp, struct fc_frame *fp, fc_frame_free(fp); return; reject: - FC_DEBUG_DISC("Received a bad RSCN frame\n"); + FC_DISC_DBG(disc, "Received a bad RSCN frame\n"); rjt_data.fp = NULL; rjt_data.reason = ELS_RJT_LOGIC; rjt_data.explan = ELS_EXPL_NONE; @@ -302,7 +293,8 @@ static void fc_disc_recv_req(struct fc_seq *sp, struct fc_frame *fp, mutex_unlock(&disc->disc_mutex); break; default: - FC_DBG("Received an unsupported request. opcode (%x)\n", op); + FC_DISC_DBG(disc, "Received an unsupported request, " + "the opcode is (%x)\n", op); break; } } @@ -320,12 +312,10 @@ static void fc_disc_restart(struct fc_disc *disc) struct fc_rport_libfc_priv *rdata, *next; struct fc_lport *lport = disc->lport; - FC_DEBUG_DISC("Restarting discovery for port (%6x)\n", - fc_host_port_id(lport->host)); + FC_DISC_DBG(disc, "Restarting discovery\n"); list_for_each_entry_safe(rdata, next, &disc->rports, peers) { rport = PRIV_TO_RPORT(rdata); - FC_DEBUG_DISC("list_del(%6x)\n", rport->port_id); list_del(&rdata->peers); lport->tt.rport_logoff(rport); } @@ -485,8 +475,7 @@ static void fc_disc_done(struct fc_disc *disc) struct fc_lport *lport = disc->lport; enum fc_disc_event event; - FC_DEBUG_DISC("Discovery complete for port (%6x)\n", - fc_host_port_id(lport->host)); + FC_DISC_DBG(disc, "Discovery complete\n"); event = disc->event; disc->event = DISC_EV_NONE; @@ -510,10 +499,10 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp) { struct fc_lport *lport = disc->lport; unsigned long delay = 0; - if (fc_disc_debug) - FC_DBG("Error %ld, retries %d/%d\n", - PTR_ERR(fp), disc->retry_count, - FC_DISC_RETRY_LIMIT); + + FC_DISC_DBG(disc, "Error %ld, retries %d/%d\n", + PTR_ERR(fp), disc->retry_count, + FC_DISC_RETRY_LIMIT); if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { /* @@ -649,9 +638,9 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) &disc->rogue_rports); lport->tt.rport_login(rport); } else - FC_DBG("Failed to allocate memory for " - "the newly discovered port (%6x)\n", - dp.ids.port_id); + printk(KERN_WARNING "libfc: Failed to allocate " + "memory for the newly discovered port " + "(%6x)\n", dp.ids.port_id); } if (np->fp_flags & FC_NS_FID_LAST) { @@ -671,9 +660,8 @@ static int fc_disc_gpn_ft_parse(struct fc_disc *disc, void *buf, size_t len) */ if (error == 0 && len > 0 && len < sizeof(*np)) { if (np != &disc->partial_buf) { - FC_DEBUG_DISC("Partial buffer remains " - "for discovery by (%6x)\n", - fc_host_port_id(lport->host)); + FC_DISC_DBG(disc, "Partial buffer remains " + "for discovery\n"); memcpy(&disc->partial_buf, np, len); } disc->buf_len = (unsigned char) len; @@ -721,8 +709,7 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, int error; mutex_lock(&disc->disc_mutex); - FC_DEBUG_DISC("Received a GPN_FT response on port (%6x)\n", - fc_host_port_id(disc->lport->host)); + FC_DISC_DBG(disc, "Received a GPN_FT response\n"); if (IS_ERR(fp)) { fc_disc_error(disc, fp); @@ -738,30 +725,30 @@ static void fc_disc_gpn_ft_resp(struct fc_seq *sp, struct fc_frame *fp, disc->seq_count == 0) { cp = fc_frame_payload_get(fp, sizeof(*cp)); if (!cp) { - FC_DBG("GPN_FT response too short, len %d\n", - fr_len(fp)); + FC_DISC_DBG(disc, "GPN_FT response too short, len %d\n", + fr_len(fp)); } else if (ntohs(cp->ct_cmd) == FC_FS_ACC) { /* Accepted, parse the response. */ buf = cp + 1; len -= sizeof(*cp); } else if (ntohs(cp->ct_cmd) == FC_FS_RJT) { - FC_DBG("GPN_FT rejected reason %x exp %x " - "(check zoning)\n", cp->ct_reason, - cp->ct_explan); + FC_DISC_DBG(disc, "GPN_FT rejected reason %x exp %x " + "(check zoning)\n", cp->ct_reason, + cp->ct_explan); disc->event = DISC_EV_FAILED; fc_disc_done(disc); } else { - FC_DBG("GPN_FT unexpected response code %x\n", - ntohs(cp->ct_cmd)); + FC_DISC_DBG(disc, "GPN_FT unexpected response code " + "%x\n", ntohs(cp->ct_cmd)); } } else if (fr_sof(fp) == FC_SOF_N3 && seq_cnt == disc->seq_count) { buf = fh + 1; } else { - FC_DBG("GPN_FT unexpected frame - out of sequence? " - "seq_cnt %x expected %x sof %x eof %x\n", - seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp)); + FC_DISC_DBG(disc, "GPN_FT unexpected frame - out of sequence? " + "seq_cnt %x expected %x sof %x eof %x\n", + seq_cnt, disc->seq_count, fr_sof(fp), fr_eof(fp)); } if (buf) { error = fc_disc_gpn_ft_parse(disc, buf, len); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 7af9bceb8aa..2bc22be5f84 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -32,18 +32,7 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> -/* - * fc_exch_debug can be set in debugger or at compile time to get more logs. - */ -static int fc_exch_debug; - -#define FC_DEBUG_EXCH(fmt...) \ - do { \ - if (fc_exch_debug) \ - FC_DBG(fmt); \ - } while (0) - -static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ +static struct kmem_cache *fc_em_cachep; /* cache for exchanges */ /* * Structure and function definitions for managing Fibre Channel Exchanges @@ -333,8 +322,8 @@ static inline void fc_exch_timer_set_locked(struct fc_exch *ep, if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE)) return; - FC_DEBUG_EXCH("Exchange (%4x) timed out, notifying the upper layer\n", - ep->xid); + FC_EXCH_DBG(ep, "Exchange timed out, notifying the upper layer\n"); + if (schedule_delayed_work(&ep->timeout_work, msecs_to_jiffies(timer_msec))) fc_exch_hold(ep); /* hold for timer */ @@ -545,7 +534,7 @@ struct fc_exch *fc_exch_alloc(struct fc_exch_mgr *mp, /* alloc a new xid */ xid = fc_em_alloc_xid(mp, fp); if (!xid) { - printk(KERN_ERR "fc_em_alloc_xid() failed\n"); + printk(KERN_WARNING "libfc: Failed to allocate an exhange\n"); goto err; } } @@ -820,8 +809,8 @@ static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp) struct fc_exch *ep = fc_seq_exch(sp); sp = fc_seq_alloc(ep, ep->seq_id++); - FC_DEBUG_EXCH("exch %4x f_ctl %6x seq %2x\n", - ep->xid, ep->f_ctl, sp->id); + FC_EXCH_DBG(ep, "f_ctl %6x seq %2x\n", + ep->f_ctl, sp->id); return sp; } /* @@ -901,7 +890,7 @@ void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, fc_exch_els_rec(sp, els_data->fp); break; default: - FC_DBG("Invalid ELS CMD:%x\n", els_cmd); + FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd); } } EXPORT_SYMBOL(fc_seq_els_rsp_send); @@ -1134,7 +1123,7 @@ static void fc_exch_recv_req(struct fc_lport *lp, struct fc_exch_mgr *mp, lp->tt.lport_recv(lp, sp, fp); fc_exch_release(ep); /* release from lookup */ } else { - FC_DEBUG_EXCH("exch/seq lookup failed: reject %x\n", reject); + FC_EM_DBG(mp, "exch/seq lookup failed: reject %x\n", reject); fc_frame_free(fp); } } @@ -1242,10 +1231,10 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) sp = fc_seq_lookup_orig(mp, fp); /* doesn't hold sequence */ if (!sp) { atomic_inc(&mp->stats.xid_not_found); - FC_DEBUG_EXCH("seq lookup failed\n"); + FC_EM_DBG(mp, "seq lookup failed\n"); } else { atomic_inc(&mp->stats.non_bls_resp); - FC_DEBUG_EXCH("non-BLS response to sequence"); + FC_EM_DBG(mp, "non-BLS response to sequence"); } fc_frame_free(fp); } @@ -1266,8 +1255,8 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) int rc = 1, has_rec = 0; fh = fc_frame_header_get(fp); - FC_DEBUG_EXCH("exch: BLS rctl %x - %s\n", - fh->fh_r_ctl, fc_exch_rctl_name(fh->fh_r_ctl)); + FC_EXCH_DBG(ep, "exch: BLS rctl %x - %s\n", fh->fh_r_ctl, + fc_exch_rctl_name(fh->fh_r_ctl)); if (cancel_delayed_work_sync(&ep->timeout_work)) fc_exch_release(ep); /* release from pending timer hold */ @@ -1359,9 +1348,9 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp) case FC_RCTL_ACK_0: break; default: - FC_DEBUG_EXCH("BLS rctl %x - %s received", - fh->fh_r_ctl, - fc_exch_rctl_name(fh->fh_r_ctl)); + FC_EXCH_DBG(ep, "BLS rctl %x - %s received", + fh->fh_r_ctl, + fc_exch_rctl_name(fh->fh_r_ctl)); break; } fc_frame_free(fp); @@ -1599,7 +1588,8 @@ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg) if (err == -FC_EX_CLOSED || err == -FC_EX_TIMEOUT) goto cleanup; - FC_DBG("Cannot process RRQ, because of frame error %d\n", err); + FC_EXCH_DBG(aborted_ep, "Cannot process RRQ, " + "frame error %d\n", err); return; } @@ -1608,12 +1598,13 @@ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg) switch (op) { case ELS_LS_RJT: - FC_DBG("LS_RJT for RRQ"); + FC_EXCH_DBG(aborted_ep, "LS_RJT for RRQ"); /* fall through */ case ELS_LS_ACC: goto cleanup; default: - FC_DBG("unexpected response op %x for RRQ", op); + FC_EXCH_DBG(aborted_ep, "unexpected response op %x " + "for RRQ", op); return; } @@ -1740,8 +1731,8 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lp, size_t len; if (max_xid <= min_xid || min_xid == 0 || max_xid == FC_XID_UNKNOWN) { - FC_DBG("Invalid min_xid 0x:%x and max_xid 0x:%x\n", - min_xid, max_xid); + FC_LPORT_DBG(lp, "Invalid min_xid 0x:%x and max_xid 0x:%x\n", + min_xid, max_xid); return NULL; } @@ -1878,7 +1869,8 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, /* lport lock ? */ if (!lp || !mp || (lp->state == LPORT_ST_NONE)) { - FC_DBG("fc_lport or EM is not allocated and configured"); + FC_LPORT_DBG(lp, "Receiving frames for an lport that " + "has not been initialized correctly\n"); fc_frame_free(fp); return; } @@ -1904,7 +1896,7 @@ void fc_exch_recv(struct fc_lport *lp, struct fc_exch_mgr *mp, fc_exch_recv_req(lp, mp, fp); break; default: - FC_DBG("dropping invalid frame (eof %x)", fr_eof(fp)); + FC_EM_DBG(mp, "dropping invalid frame (eof %x)", fr_eof(fp)); fc_frame_free(fp); break; } diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index ad8b747837b..e303e0d12c4 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -43,13 +43,9 @@ MODULE_AUTHOR("Open-FCoE.org"); MODULE_DESCRIPTION("libfc"); MODULE_LICENSE("GPL v2"); -static int fc_fcp_debug; - -#define FC_DEBUG_FCP(fmt...) \ - do { \ - if (fc_fcp_debug) \ - FC_DBG(fmt); \ - } while (0) +unsigned int fc_debug_logging; +module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); static struct kmem_cache *scsi_pkt_cachep; @@ -347,8 +343,8 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) && fc_frame_crc_check(fp)) goto crc_err; - FC_DEBUG_FCP("data received past end. len %zx offset %zx " - "data_len %x\n", len, offset, fsp->data_len); + FC_FCP_DBG(fsp, "data received past end. len %zx offset %zx " + "data_len %x\n", len, offset, fsp->data_len); fc_fcp_retry_cmd(fsp); return; } @@ -411,7 +407,8 @@ crc_err: stats->ErrorFrames++; /* FIXME - per cpu count, not total count! */ if (stats->InvalidCRCCount++ < 5) - printk(KERN_WARNING "CRC error on data frame for port (%6x)\n", + printk(KERN_WARNING "libfc: CRC error on data " + "frame for port (%6x)\n", fc_host_port_id(lp->host)); /* * Assume the frame is total garbage. @@ -475,14 +472,14 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, WARN_ON(seq_blen <= 0); if (unlikely(offset + seq_blen > fsp->data_len)) { /* this should never happen */ - FC_DEBUG_FCP("xfer-ready past end. seq_blen %zx offset %zx\n", - seq_blen, offset); + FC_FCP_DBG(fsp, "xfer-ready past end. seq_blen %zx " + "offset %zx\n", seq_blen, offset); fc_fcp_send_abort(fsp); return 0; } else if (offset != fsp->xfer_len) { /* Out of Order Data Request - no problem, but unexpected. */ - FC_DEBUG_FCP("xfer-ready non-contiguous. " - "seq_blen %zx offset %zx\n", seq_blen, offset); + FC_FCP_DBG(fsp, "xfer-ready non-contiguous. " + "seq_blen %zx offset %zx\n", seq_blen, offset); } /* @@ -493,7 +490,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, t_blen = fsp->max_payload; if (lp->seq_offload) { t_blen = min(seq_blen, (size_t)lp->lso_max); - FC_DEBUG_FCP("fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n", + FC_FCP_DBG(fsp, "fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n", fsp, seq_blen, lp->lso_max, t_blen); } @@ -694,7 +691,7 @@ static void fc_fcp_reduce_can_queue(struct fc_lport *lp) if (!can_queue) can_queue = 1; lp->host->can_queue = can_queue; - shost_printk(KERN_ERR, lp->host, "Could not allocate frame.\n" + shost_printk(KERN_ERR, lp->host, "libfc: Could not allocate frame.\n" "Reducing can_queue to %d.\n", can_queue); done: spin_unlock_irqrestore(lp->host->host_lock, flags); @@ -768,7 +765,7 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg) fc_fcp_resp(fsp, fp); } else { - FC_DBG("unexpected frame. r_ctl %x\n", r_ctl); + FC_FCP_DBG(fsp, "unexpected frame. r_ctl %x\n", r_ctl); } unlock: fc_fcp_unlock_pkt(fsp); @@ -877,17 +874,17 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) return; } fsp->status_code = FC_DATA_OVRRUN; - FC_DBG("tgt %6x xfer len %zx greater than expected len %x. " - "data len %x\n", - fsp->rport->port_id, - fsp->xfer_len, expected_len, fsp->data_len); + FC_FCP_DBG(fsp, "tgt %6x xfer len %zx greater than expected, " + "len %x, data len %x\n", + fsp->rport->port_id, + fsp->xfer_len, expected_len, fsp->data_len); } fc_fcp_complete_locked(fsp); return; len_err: - FC_DBG("short FCP response. flags 0x%x len %u respl %u snsl %u\n", - flags, fr_len(fp), respl, snsl); + FC_FCP_DBG(fsp, "short FCP response. flags 0x%x len %u respl %u " + "snsl %u\n", flags, fr_len(fp), respl, snsl); err: fsp->status_code = FC_ERROR; fc_fcp_complete_locked(fsp); @@ -1107,13 +1104,11 @@ static void fc_fcp_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) if (fc_fcp_lock_pkt(fsp)) return; - switch (error) { - case -FC_EX_CLOSED: + if (error == -FC_EX_CLOSED) { fc_fcp_retry_cmd(fsp); goto unlock; - default: - FC_DBG("unknown error %ld\n", PTR_ERR(fp)); } + /* * clear abort pending, because the lower layer * decided to force completion. @@ -1145,10 +1140,10 @@ static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp) fsp->wait_for_comp = 0; if (!rc) { - FC_DBG("target abort cmd failed\n"); + FC_FCP_DBG(fsp, "target abort cmd failed\n"); rc = FAILED; } else if (fsp->state & FC_SRB_ABORTED) { - FC_DBG("target abort cmd passed\n"); + FC_FCP_DBG(fsp, "target abort cmd passed\n"); rc = SUCCESS; fc_fcp_complete_locked(fsp); } @@ -1213,7 +1208,7 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp, spin_unlock_bh(&fsp->scsi_pkt_lock); if (!rc) { - FC_DBG("lun reset failed\n"); + FC_SCSI_DBG(lp, "lun reset failed\n"); return FAILED; } @@ -1221,7 +1216,7 @@ static int fc_lun_reset(struct fc_lport *lp, struct fc_fcp_pkt *fsp, if (fsp->cdb_status != FCP_TMF_CMPL) return FAILED; - FC_DBG("lun reset to lun %u completed\n", lun); + FC_SCSI_DBG(lp, "lun reset to lun %u completed\n", lun); fc_fcp_cleanup_each_cmd(lp, id, lun, FC_CMD_ABORTED); return SUCCESS; } @@ -1388,13 +1383,13 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg) rjt = fc_frame_payload_get(fp, sizeof(*rjt)); switch (rjt->er_reason) { default: - FC_DEBUG_FCP("device %x unexpected REC reject " - "reason %d expl %d\n", - fsp->rport->port_id, rjt->er_reason, - rjt->er_explan); + FC_FCP_DBG(fsp, "device %x unexpected REC reject " + "reason %d expl %d\n", + fsp->rport->port_id, rjt->er_reason, + rjt->er_explan); /* fall through */ case ELS_RJT_UNSUP: - FC_DEBUG_FCP("device does not support REC\n"); + FC_FCP_DBG(fsp, "device does not support REC\n"); rp = fsp->rport->dd_data; /* * if we do not spport RECs or got some bogus @@ -1514,8 +1509,8 @@ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) break; default: - FC_DBG("REC %p fid %x error unexpected error %d\n", - fsp, fsp->rport->port_id, error); + FC_FCP_DBG(fsp, "REC %p fid %x error unexpected error %d\n", + fsp, fsp->rport->port_id, error); fsp->status_code = FC_CMD_PLOGO; /* fall through */ @@ -1524,9 +1519,9 @@ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp) * Assume REC or LS_ACC was lost. * The exchange manager will have aborted REC, so retry. */ - FC_DBG("REC fid %x error error %d retry %d/%d\n", - fsp->rport->port_id, error, fsp->recov_retry, - FC_MAX_RECOV_RETRY); + FC_FCP_DBG(fsp, "REC fid %x error error %d retry %d/%d\n", + fsp->rport->port_id, error, fsp->recov_retry, + FC_MAX_RECOV_RETRY); if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) fc_fcp_rec(fsp); else @@ -2011,9 +2006,11 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd) if (lp->state != LPORT_ST_READY) return rc; + FC_SCSI_DBG(lp, "Resetting rport (%6x)\n", rport->port_id); + fsp = fc_fcp_pkt_alloc(lp, GFP_NOIO); if (fsp == NULL) { - FC_DBG("could not allocate scsi_pkt\n"); + printk(KERN_WARNING "libfc: could not allocate scsi_pkt\n"); sc_cmd->result = DID_NO_CONNECT << 16; goto out; } @@ -2048,17 +2045,21 @@ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd) struct fc_lport *lp = shost_priv(shost); unsigned long wait_tmo; + FC_SCSI_DBG(lp, "Resetting host\n"); + lp->tt.lport_reset(lp); wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT; while (!fc_fcp_lport_queue_ready(lp) && time_before(jiffies, wait_tmo)) msleep(1000); if (fc_fcp_lport_queue_ready(lp)) { - shost_printk(KERN_INFO, shost, "Host reset succeeded.\n"); + shost_printk(KERN_INFO, shost, "libfc: Host reset succeeded " + "on port (%6x)\n", fc_host_port_id(lp->host)); return SUCCESS; } else { - shost_printk(KERN_INFO, shost, "Host reset failed. " - "lport not ready.\n"); + shost_printk(KERN_INFO, shost, "libfc: Host reset failed, " + "port (%6x) is not ready.\n", + fc_host_port_id(lp->host)); return FAILED; } } @@ -2117,7 +2118,8 @@ void fc_fcp_destroy(struct fc_lport *lp) struct fc_fcp_internal *si = fc_get_scsi_internal(lp); if (!list_empty(&si->scsi_pkt_queue)) - printk(KERN_ERR "Leaked scsi packets.\n"); + printk(KERN_ERR "libfc: Leaked SCSI packets when destroying " + "port (%6x)\n", fc_host_port_id(lp->host)); mempool_destroy(si->scsi_pkt_pool); kfree(si); @@ -2166,7 +2168,8 @@ static int __init libfc_init(void) sizeof(struct fc_fcp_pkt), 0, SLAB_HWCACHE_ALIGN, NULL); if (scsi_pkt_cachep == NULL) { - FC_DBG("Unable to allocate SRB cache...module load failed!"); + printk(KERN_ERR "libfc: Unable to allocate SRB cache, " + "module load failed!"); return -ENOMEM; } diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index e0c247724d2..745fa5555d6 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -101,14 +101,6 @@ #define DNS_DELAY 3 /* Discovery delay after RSCN (in seconds)*/ -static int fc_lport_debug; - -#define FC_DEBUG_LPORT(fmt...) \ - do { \ - if (fc_lport_debug) \ - FC_DBG(fmt); \ - } while (0) - static void fc_lport_error(struct fc_lport *, struct fc_frame *); static void fc_lport_enter_reset(struct fc_lport *); @@ -151,8 +143,8 @@ static void fc_lport_rport_callback(struct fc_lport *lport, struct fc_rport *rport, enum fc_rport_event event) { - FC_DEBUG_LPORT("Received a %d event for port (%6x)\n", event, - rport->port_id); + FC_LPORT_DBG(lport, "Received a %d event for port (%6x)\n", event, + rport->port_id); switch (event) { case RPORT_EV_CREATED: @@ -162,19 +154,19 @@ static void fc_lport_rport_callback(struct fc_lport *lport, lport->dns_rp = rport; fc_lport_enter_rpn_id(lport); } else { - FC_DEBUG_LPORT("Received an CREATED event on " - "port (%6x) for the directory " - "server, but the lport is not " - "in the DNS state, it's in the " - "%d state", rport->port_id, - lport->state); + FC_LPORT_DBG(lport, "Received an CREATED event " + "on port (%6x) for the directory " + "server, but the lport is not " + "in the DNS state, it's in the " + "%d state", rport->port_id, + lport->state); lport->tt.rport_logoff(rport); } mutex_unlock(&lport->lp_mutex); } else - FC_DEBUG_LPORT("Received an event for port (%6x) " - "which is not the directory server\n", - rport->port_id); + FC_LPORT_DBG(lport, "Received an event for port (%6x) " + "which is not the directory server\n", + rport->port_id); break; case RPORT_EV_LOGO: case RPORT_EV_FAILED: @@ -185,9 +177,9 @@ static void fc_lport_rport_callback(struct fc_lport *lport, mutex_unlock(&lport->lp_mutex); } else - FC_DEBUG_LPORT("Received an event for port (%6x) " - "which is not the directory server\n", - rport->port_id); + FC_LPORT_DBG(lport, "Received an event for port (%6x) " + "which is not the directory server\n", + rport->port_id); break; case RPORT_EV_NONE: break; @@ -363,8 +355,8 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type) static void fc_lport_recv_rlir_req(struct fc_seq *sp, struct fc_frame *fp, struct fc_lport *lport) { - FC_DEBUG_LPORT("Received RLIR request while in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n", + fc_lport_state(lport)); lport->tt.seq_els_rsp_send(sp, ELS_LS_ACC, NULL); fc_frame_free(fp); @@ -389,8 +381,8 @@ static void fc_lport_recv_echo_req(struct fc_seq *sp, struct fc_frame *in_fp, void *dp; u32 f_ctl; - FC_DEBUG_LPORT("Received RLIR request while in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received RLIR request while in state %s\n", + fc_lport_state(lport)); len = fr_len(in_fp) - sizeof(struct fc_frame_header); pp = fc_frame_payload_get(in_fp, len); @@ -437,8 +429,8 @@ static void fc_lport_recv_rnid_req(struct fc_seq *sp, struct fc_frame *in_fp, size_t len; u32 f_ctl; - FC_DEBUG_LPORT("Received RNID request while in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received RNID request while in state %s\n", + fc_lport_state(lport)); req = fc_frame_payload_get(in_fp, sizeof(*req)); if (!req) { @@ -498,8 +490,8 @@ static void fc_lport_recv_adisc_req(struct fc_seq *sp, struct fc_frame *in_fp, size_t len; u32 f_ctl; - FC_DEBUG_LPORT("Received ADISC request while in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received ADISC request while in state %s\n", + fc_lport_state(lport)); req = fc_frame_payload_get(in_fp, sizeof(*req)); if (!req) { @@ -574,8 +566,8 @@ EXPORT_SYMBOL(fc_fabric_login); */ void fc_linkup(struct fc_lport *lport) { - FC_DEBUG_LPORT("Link is up for port (%6x)\n", - fc_host_port_id(lport->host)); + printk(KERN_INFO "libfc: Link up on port (%6x)\n", + fc_host_port_id(lport->host)); mutex_lock(&lport->lp_mutex); if (!lport->link_up) { @@ -595,8 +587,8 @@ EXPORT_SYMBOL(fc_linkup); void fc_linkdown(struct fc_lport *lport) { mutex_lock(&lport->lp_mutex); - FC_DEBUG_LPORT("Link is down for port (%6x)\n", - fc_host_port_id(lport->host)); + printk(KERN_INFO "libfc: Link down on port (%6x)\n", + fc_host_port_id(lport->host)); if (lport->link_up) { lport->link_up = 0; @@ -701,12 +693,11 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) { switch (event) { case DISC_EV_SUCCESS: - FC_DEBUG_LPORT("Got a SUCCESS event for port (%6x)\n", - fc_host_port_id(lport->host)); + FC_LPORT_DBG(lport, "Discovery succeeded\n"); break; case DISC_EV_FAILED: - FC_DEBUG_LPORT("Got a FAILED event for port (%6x)\n", - fc_host_port_id(lport->host)); + printk(KERN_ERR "libfc: Discovery failed for port (%6x)\n", + fc_host_port_id(lport->host)); mutex_lock(&lport->lp_mutex); fc_lport_enter_reset(lport); mutex_unlock(&lport->lp_mutex); @@ -726,8 +717,8 @@ void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) */ static void fc_lport_enter_ready(struct fc_lport *lport) { - FC_DEBUG_LPORT("Port (%6x) entered Ready from state %s\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Entered READY from state %s\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_READY); @@ -762,8 +753,8 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, u32 local_fid; u32 f_ctl; - FC_DEBUG_LPORT("Received FLOGI request while in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received FLOGI request while in state %s\n", + fc_lport_state(lport)); fh = fc_frame_header_get(rx_fp); remote_fid = ntoh24(fh->fh_s_id); @@ -772,12 +763,11 @@ static void fc_lport_recv_flogi_req(struct fc_seq *sp_in, goto out; remote_wwpn = get_unaligned_be64(&flp->fl_wwpn); if (remote_wwpn == lport->wwpn) { - FC_DBG("FLOGI from port with same WWPN %llx " - "possible configuration error\n", - (unsigned long long)remote_wwpn); + printk(KERN_WARNING "libfc: Received FLOGI from port " + "with same WWPN %llx\n", remote_wwpn); goto out; } - FC_DBG("FLOGI from port WWPN %llx\n", (unsigned long long)remote_wwpn); + FC_LPORT_DBG(lport, "FLOGI from port WWPN %llx\n", remote_wwpn); /* * XXX what is the right thing to do for FIDs? @@ -909,7 +899,8 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_seq *sp, } } } else { - FC_DBG("dropping invalid frame (eof %x)\n", fr_eof(fp)); + FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n", + fr_eof(fp)); fc_frame_free(fp); } mutex_unlock(&lport->lp_mutex); @@ -947,8 +938,8 @@ EXPORT_SYMBOL(fc_lport_reset); */ static void fc_lport_enter_reset(struct fc_lport *lport) { - FC_DEBUG_LPORT("Port (%6x) entered RESET state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Entered RESET state from %s state\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_RESET); @@ -982,9 +973,9 @@ static void fc_lport_enter_reset(struct fc_lport *lport) static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp) { unsigned long delay = 0; - FC_DEBUG_LPORT("Error %ld in state %s, retries %d\n", - PTR_ERR(fp), fc_lport_state(lport), - lport->retry_count); + FC_LPORT_DBG(lport, "Error %ld in state %s, retries %d\n", + PTR_ERR(fp), fc_lport_state(lport), + lport->retry_count); if (!fp || PTR_ERR(fp) == -FC_EX_TIMEOUT) { /* @@ -1040,11 +1031,11 @@ static void fc_lport_rft_id_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&lport->lp_mutex); - FC_DEBUG_LPORT("Received a RFT_ID response\n"); + FC_LPORT_DBG(lport, "Received a RFT_ID response\n"); if (lport->state != LPORT_ST_RFT_ID) { - FC_DBG("Received a RFT_ID response, but in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received a RFT_ID response, but in state " + "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out; @@ -1094,11 +1085,11 @@ static void fc_lport_rpn_id_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&lport->lp_mutex); - FC_DEBUG_LPORT("Received a RPN_ID response\n"); + FC_LPORT_DBG(lport, "Received a RPN_ID response\n"); if (lport->state != LPORT_ST_RPN_ID) { - FC_DBG("Received a RPN_ID response, but in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received a RPN_ID response, but in state " + "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out; @@ -1146,11 +1137,11 @@ static void fc_lport_scr_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&lport->lp_mutex); - FC_DEBUG_LPORT("Received a SCR response\n"); + FC_LPORT_DBG(lport, "Received a SCR response\n"); if (lport->state != LPORT_ST_SCR) { - FC_DBG("Received a SCR response, but in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received a SCR response, but in state " + "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out; @@ -1184,8 +1175,8 @@ static void fc_lport_enter_scr(struct fc_lport *lport) { struct fc_frame *fp; - FC_DEBUG_LPORT("Port (%6x) entered SCR state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Entered SCR state from %s state\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_SCR); @@ -1213,8 +1204,8 @@ static void fc_lport_enter_rft_id(struct fc_lport *lport) struct fc_ns_fts *lps; int i; - FC_DEBUG_LPORT("Port (%6x) entered RFT_ID state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Entered RFT_ID state from %s state\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_RFT_ID); @@ -1253,8 +1244,8 @@ static void fc_lport_enter_rpn_id(struct fc_lport *lport) { struct fc_frame *fp; - FC_DEBUG_LPORT("Port (%6x) entered RPN_ID state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Entered RPN_ID state from %s state\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_RPN_ID); @@ -1294,8 +1285,8 @@ static void fc_lport_enter_dns(struct fc_lport *lport) dp.ids.roles = FC_RPORT_ROLE_UNKNOWN; dp.lp = lport; - FC_DEBUG_LPORT("Port (%6x) entered DNS state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Entered DNS state from %s state\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_DNS); @@ -1374,11 +1365,11 @@ static void fc_lport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&lport->lp_mutex); - FC_DEBUG_LPORT("Received a LOGO response\n"); + FC_LPORT_DBG(lport, "Received a LOGO response\n"); if (lport->state != LPORT_ST_LOGO) { - FC_DBG("Received a LOGO response, but in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received a LOGO response, but in state " + "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out; @@ -1413,8 +1404,8 @@ static void fc_lport_enter_logo(struct fc_lport *lport) struct fc_frame *fp; struct fc_els_logo *logo; - FC_DEBUG_LPORT("Port (%6x) entered LOGO state from %s state\n", - fc_host_port_id(lport->host), fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Entered LOGO state from %s state\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_LOGO); @@ -1456,11 +1447,11 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&lport->lp_mutex); - FC_DEBUG_LPORT("Received a FLOGI response\n"); + FC_LPORT_DBG(lport, "Received a FLOGI response\n"); if (lport->state != LPORT_ST_FLOGI) { - FC_DBG("Received a FLOGI response, but in state %s\n", - fc_lport_state(lport)); + FC_LPORT_DBG(lport, "Received a FLOGI response, but in state " + "%s\n", fc_lport_state(lport)); if (IS_ERR(fp)) goto err; goto out; @@ -1475,7 +1466,8 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, did = ntoh24(fh->fh_d_id); if (fc_frame_payload_op(fp) == ELS_LS_ACC && did != 0) { - FC_DEBUG_LPORT("Assigned fid %x\n", did); + printk(KERN_INFO "libfc: Assigned FID (%6x) in FLOGI response\n", + did); fc_host_port_id(lport->host) = did; flp = fc_frame_payload_get(fp, sizeof(*flp)); @@ -1494,7 +1486,8 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, if (e_d_tov > lport->e_d_tov) lport->e_d_tov = e_d_tov; lport->r_a_tov = 2 * e_d_tov; - FC_DBG("Point-to-Point mode\n"); + printk(KERN_INFO "libfc: Port (%6x) entered " + "point to point mode\n", did); fc_lport_ptp_setup(lport, ntoh24(fh->fh_s_id), get_unaligned_be64( &flp->fl_wwpn), @@ -1517,7 +1510,7 @@ static void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, } } } else { - FC_DBG("bad FLOGI response\n"); + FC_LPORT_DBG(lport, "Bad FLOGI response\n"); } out: @@ -1537,7 +1530,8 @@ void fc_lport_enter_flogi(struct fc_lport *lport) { struct fc_frame *fp; - FC_DEBUG_LPORT("Processing FLOGI state\n"); + FC_LPORT_DBG(lport, "Entered FLOGI state from %s state\n", + fc_lport_state(lport)); fc_lport_state_enter(lport, LPORT_ST_FLOGI); diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index 7bfbff7e0ef..7162385f52e 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -55,14 +55,6 @@ #include <scsi/libfc.h> #include <scsi/fc_encode.h> -static int fc_rport_debug; - -#define FC_DEBUG_RPORT(fmt...) \ - do { \ - if (fc_rport_debug) \ - FC_DBG(fmt); \ - } while (0) - struct workqueue_struct *rport_event_queue; static void fc_rport_enter_plogi(struct fc_rport *); @@ -97,7 +89,7 @@ static const char *fc_rport_state_names[] = { static void fc_rport_rogue_destroy(struct device *dev) { struct fc_rport *rport = dev_to_rport(dev); - FC_DEBUG_RPORT("Destroying rogue rport (%6x)\n", rport->port_id); + FC_RPORT_DBG(rport, "Destroying rogue rport\n"); kfree(rport); } @@ -263,8 +255,8 @@ static void fc_rport_work(struct work_struct *work) fc_rport_state_enter(new_rport, RPORT_ST_READY); } else { - FC_DBG("Failed to create the rport for port " - "(%6x).\n", ids.port_id); + printk(KERN_WARNING "libfc: Failed to allocate " + " memory for rport (%6x)\n", ids.port_id); event = RPORT_EV_FAILED; } if (rport->port_id != FC_FID_DIR_SERV) @@ -309,7 +301,7 @@ int fc_rport_login(struct fc_rport *rport) mutex_lock(&rdata->rp_mutex); - FC_DEBUG_RPORT("Login to port (%6x)\n", rport->port_id); + FC_RPORT_DBG(rport, "Login to port\n"); fc_rport_enter_plogi(rport); @@ -329,16 +321,13 @@ int fc_rport_login(struct fc_rport *rport) int fc_rport_logoff(struct fc_rport *rport) { struct fc_rport_libfc_priv *rdata = rport->dd_data; - struct fc_lport *lport = rdata->local_port; mutex_lock(&rdata->rp_mutex); - FC_DEBUG_RPORT("Remove port (%6x)\n", rport->port_id); + FC_RPORT_DBG(rport, "Remove port\n"); if (rdata->rp_state == RPORT_ST_NONE) { - FC_DEBUG_RPORT("(%6x): Port (%6x) in NONE state," - " not removing", fc_host_port_id(lport->host), - rport->port_id); + FC_RPORT_DBG(rport, "Port in NONE state, not removing\n"); mutex_unlock(&rdata->rp_mutex); goto out; } @@ -379,7 +368,7 @@ static void fc_rport_enter_ready(struct fc_rport *rport) fc_rport_state_enter(rport, RPORT_ST_READY); - FC_DEBUG_RPORT("Port (%6x) is Ready\n", rport->port_id); + FC_RPORT_DBG(rport, "Port is Ready\n"); rdata->event = RPORT_EV_CREATED; queue_work(rport_event_queue, &rdata->event_work); @@ -436,8 +425,8 @@ static void fc_rport_error(struct fc_rport *rport, struct fc_frame *fp) { struct fc_rport_libfc_priv *rdata = rport->dd_data; - FC_DEBUG_RPORT("Error %ld in state %s, retries %d\n", - PTR_ERR(fp), fc_rport_state(rport), rdata->retries); + FC_RPORT_DBG(rport, "Error %ld in state %s, retries %d\n", + PTR_ERR(fp), fc_rport_state(rport), rdata->retries); switch (rdata->rp_state) { case RPORT_ST_PLOGI: @@ -479,8 +468,8 @@ static void fc_rport_error_retry(struct fc_rport *rport, struct fc_frame *fp) return fc_rport_error(rport, fp); if (rdata->retries < rdata->local_port->max_rport_retry_count) { - FC_DEBUG_RPORT("Error %ld in state %s, retrying\n", - PTR_ERR(fp), fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Error %ld in state %s, retrying\n", + PTR_ERR(fp), fc_rport_state(rport)); rdata->retries++; /* no additional delay on exchange timeouts */ if (PTR_ERR(fp) == -FC_EX_TIMEOUT) @@ -517,12 +506,11 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&rdata->rp_mutex); - FC_DEBUG_RPORT("Received a PLOGI response from port (%6x)\n", - rport->port_id); + FC_RPORT_DBG(rport, "Received a PLOGI response\n"); if (rdata->rp_state != RPORT_ST_PLOGI) { - FC_DBG("Received a PLOGI response, but in state %s\n", - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received a PLOGI response, but in state " + "%s\n", fc_rport_state(rport)); if (IS_ERR(fp)) goto err; goto out; @@ -583,8 +571,8 @@ static void fc_rport_enter_plogi(struct fc_rport *rport) struct fc_lport *lport = rdata->local_port; struct fc_frame *fp; - FC_DEBUG_RPORT("Port (%6x) entered PLOGI state from %s state\n", - rport->port_id, fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Port entered PLOGI state from %s state\n", + fc_rport_state(rport)); fc_rport_state_enter(rport, RPORT_ST_PLOGI); @@ -628,12 +616,11 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&rdata->rp_mutex); - FC_DEBUG_RPORT("Received a PRLI response from port (%6x)\n", - rport->port_id); + FC_RPORT_DBG(rport, "Received a PRLI response\n"); if (rdata->rp_state != RPORT_ST_PRLI) { - FC_DBG("Received a PRLI response, but in state %s\n", - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received a PRLI response, but in state " + "%s\n", fc_rport_state(rport)); if (IS_ERR(fp)) goto err; goto out; @@ -663,7 +650,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp, fc_rport_enter_rtv(rport); } else { - FC_DBG("Bad ELS response\n"); + FC_RPORT_DBG(rport, "Bad ELS response for PRLI command\n"); rdata->event = RPORT_EV_FAILED; fc_rport_state_enter(rport, RPORT_ST_NONE); queue_work(rport_event_queue, &rdata->event_work); @@ -695,12 +682,11 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&rdata->rp_mutex); - FC_DEBUG_RPORT("Received a LOGO response from port (%6x)\n", - rport->port_id); + FC_RPORT_DBG(rport, "Received a LOGO response\n"); if (rdata->rp_state != RPORT_ST_LOGO) { - FC_DEBUG_RPORT("Received a LOGO response, but in state %s\n", - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received a LOGO response, but in state " + "%s\n", fc_rport_state(rport)); if (IS_ERR(fp)) goto err; goto out; @@ -715,7 +701,7 @@ static void fc_rport_logo_resp(struct fc_seq *sp, struct fc_frame *fp, if (op == ELS_LS_ACC) { fc_rport_enter_rtv(rport); } else { - FC_DBG("Bad ELS response\n"); + FC_RPORT_DBG(rport, "Bad ELS response for LOGO command\n"); rdata->event = RPORT_EV_LOGO; fc_rport_state_enter(rport, RPORT_ST_NONE); queue_work(rport_event_queue, &rdata->event_work); @@ -745,8 +731,8 @@ static void fc_rport_enter_prli(struct fc_rport *rport) } *pp; struct fc_frame *fp; - FC_DEBUG_RPORT("Port (%6x) entered PRLI state from %s state\n", - rport->port_id, fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Port entered PRLI state from %s state\n", + fc_rport_state(rport)); fc_rport_state_enter(rport, RPORT_ST_PRLI); @@ -784,12 +770,11 @@ static void fc_rport_rtv_resp(struct fc_seq *sp, struct fc_frame *fp, mutex_lock(&rdata->rp_mutex); - FC_DEBUG_RPORT("Received a RTV response from port (%6x)\n", - rport->port_id); + FC_RPORT_DBG(rport, "Received a RTV response\n"); if (rdata->rp_state != RPORT_ST_RTV) { - FC_DBG("Received a RTV response, but in state %s\n", - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received a RTV response, but in state " + "%s\n", fc_rport_state(rport)); if (IS_ERR(fp)) goto err; goto out; @@ -844,8 +829,8 @@ static void fc_rport_enter_rtv(struct fc_rport *rport) struct fc_rport_libfc_priv *rdata = rport->dd_data; struct fc_lport *lport = rdata->local_port; - FC_DEBUG_RPORT("Port (%6x) entered RTV state from %s state\n", - rport->port_id, fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Port entered RTV state from %s state\n", + fc_rport_state(rport)); fc_rport_state_enter(rport, RPORT_ST_RTV); @@ -875,8 +860,8 @@ static void fc_rport_enter_logo(struct fc_rport *rport) struct fc_lport *lport = rdata->local_port; struct fc_frame *fp; - FC_DEBUG_RPORT("Port (%6x) entered LOGO state from %s state\n", - rport->port_id, fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Port entered LOGO state from %s state\n", + fc_rport_state(rport)); fc_rport_state_enter(rport, RPORT_ST_LOGO); @@ -983,14 +968,13 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, fh = fc_frame_header_get(fp); - FC_DEBUG_RPORT("Received PLOGI request from port (%6x) " - "while in state %s\n", ntoh24(fh->fh_s_id), - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received PLOGI request while in state %s\n", + fc_rport_state(rport)); sid = ntoh24(fh->fh_s_id); pl = fc_frame_payload_get(fp, sizeof(*pl)); if (!pl) { - FC_DBG("incoming PLOGI from %x too short\n", sid); + FC_RPORT_DBG(rport, "Received PLOGI too short\n"); WARN_ON(1); /* XXX TBD: send reject? */ fc_frame_free(fp); @@ -1012,26 +996,26 @@ static void fc_rport_recv_plogi_req(struct fc_rport *rport, */ switch (rdata->rp_state) { case RPORT_ST_INIT: - FC_DEBUG_RPORT("incoming PLOGI from %6x wwpn %llx state INIT " - "- reject\n", sid, (unsigned long long)wwpn); + FC_RPORT_DBG(rport, "Received PLOGI, wwpn %llx state INIT " + "- reject\n", (unsigned long long)wwpn); reject = ELS_RJT_UNSUP; break; case RPORT_ST_PLOGI: - FC_DEBUG_RPORT("incoming PLOGI from %x in PLOGI state %d\n", - sid, rdata->rp_state); + FC_RPORT_DBG(rport, "Received PLOGI in PLOGI state %d\n", + rdata->rp_state); if (wwpn < lport->wwpn) reject = ELS_RJT_INPROG; break; case RPORT_ST_PRLI: case RPORT_ST_READY: - FC_DEBUG_RPORT("incoming PLOGI from %x in logged-in state %d " - "- ignored for now\n", sid, rdata->rp_state); + FC_RPORT_DBG(rport, "Received PLOGI in logged-in state %d " + "- ignored for now\n", rdata->rp_state); /* XXX TBD - should reset */ break; case RPORT_ST_NONE: default: - FC_DEBUG_RPORT("incoming PLOGI from %x in unexpected " - "state %d\n", sid, rdata->rp_state); + FC_RPORT_DBG(rport, "Received PLOGI in unexpected " + "state %d\n", rdata->rp_state); fc_frame_free(fp); return; break; @@ -1115,9 +1099,8 @@ static void fc_rport_recv_prli_req(struct fc_rport *rport, fh = fc_frame_header_get(rx_fp); - FC_DEBUG_RPORT("Received PRLI request from port (%6x) " - "while in state %s\n", ntoh24(fh->fh_s_id), - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received PRLI request while in state %s\n", + fc_rport_state(rport)); switch (rdata->rp_state) { case RPORT_ST_PRLI: @@ -1252,9 +1235,8 @@ static void fc_rport_recv_prlo_req(struct fc_rport *rport, struct fc_seq *sp, fh = fc_frame_header_get(fp); - FC_DEBUG_RPORT("Received PRLO request from port (%6x) " - "while in state %s\n", ntoh24(fh->fh_s_id), - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received PRLO request while in state %s\n", + fc_rport_state(rport)); if (rdata->rp_state == RPORT_ST_NONE) { fc_frame_free(fp); @@ -1286,9 +1268,8 @@ static void fc_rport_recv_logo_req(struct fc_rport *rport, struct fc_seq *sp, fh = fc_frame_header_get(fp); - FC_DEBUG_RPORT("Received LOGO request from port (%6x) " - "while in state %s\n", ntoh24(fh->fh_s_id), - fc_rport_state(rport)); + FC_RPORT_DBG(rport, "Received LOGO request while in state %s\n", + fc_rport_state(rport)); if (rdata->rp_state == RPORT_ST_NONE) { fc_frame_free(fp); @@ -1308,7 +1289,6 @@ static void fc_rport_flush_queue(void) flush_workqueue(rport_event_queue); } - int fc_rport_init(struct fc_lport *lport) { if (!lport->tt.rport_create) diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 59908aead53..716cc344c5d 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -38,15 +38,30 @@ #include <scsi/scsi_transport_iscsi.h> #include <scsi/libiscsi.h> -static int iscsi_dbg_lib; -module_param_named(debug_libiscsi, iscsi_dbg_lib, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug_libiscsi, "Turn on debugging for libiscsi module. " - "Set to 1 to turn on, and zero to turn off. Default " - "is off."); +static int iscsi_dbg_lib_conn; +module_param_named(debug_libiscsi_conn, iscsi_dbg_lib_conn, int, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug_libiscsi_conn, + "Turn on debugging for connections in libiscsi module. " + "Set to 1 to turn on, and zero to turn off. Default is off."); + +static int iscsi_dbg_lib_session; +module_param_named(debug_libiscsi_session, iscsi_dbg_lib_session, int, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug_libiscsi_session, + "Turn on debugging for sessions in libiscsi module. " + "Set to 1 to turn on, and zero to turn off. Default is off."); + +static int iscsi_dbg_lib_eh; +module_param_named(debug_libiscsi_eh, iscsi_dbg_lib_eh, int, + S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug_libiscsi_eh, + "Turn on debugging for error handling in libiscsi module. " + "Set to 1 to turn on, and zero to turn off. Default is off."); #define ISCSI_DBG_CONN(_conn, dbg_fmt, arg...) \ do { \ - if (iscsi_dbg_lib) \ + if (iscsi_dbg_lib_conn) \ iscsi_conn_printk(KERN_INFO, _conn, \ "%s " dbg_fmt, \ __func__, ##arg); \ @@ -54,7 +69,15 @@ MODULE_PARM_DESC(debug_libiscsi, "Turn on debugging for libiscsi module. " #define ISCSI_DBG_SESSION(_session, dbg_fmt, arg...) \ do { \ - if (iscsi_dbg_lib) \ + if (iscsi_dbg_lib_session) \ + iscsi_session_printk(KERN_INFO, _session, \ + "%s " dbg_fmt, \ + __func__, ##arg); \ + } while (0); + +#define ISCSI_DBG_EH(_session, dbg_fmt, arg...) \ + do { \ + if (iscsi_dbg_lib_eh) \ iscsi_session_printk(KERN_INFO, _session, \ "%s " dbg_fmt, \ __func__, ##arg); \ @@ -954,6 +977,7 @@ int __iscsi_complete_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, task = iscsi_itt_to_ctask(conn, hdr->itt); if (!task) return ISCSI_ERR_BAD_ITT; + task->last_xfer = jiffies; break; case ISCSI_OP_R2T: /* @@ -1192,10 +1216,12 @@ static int iscsi_xmit_task(struct iscsi_conn *conn) spin_unlock_bh(&conn->session->lock); rc = conn->session->tt->xmit_task(task); spin_lock_bh(&conn->session->lock); - __iscsi_put_task(task); - if (!rc) + if (!rc) { /* done with this task */ + task->last_xfer = jiffies; conn->task = NULL; + } + __iscsi_put_task(task); return rc; } @@ -1361,6 +1387,9 @@ static inline struct iscsi_task *iscsi_alloc_task(struct iscsi_conn *conn, task->state = ISCSI_TASK_PENDING; task->conn = conn; task->sc = sc; + task->have_checked_conn = false; + task->last_timeout = jiffies; + task->last_xfer = jiffies; INIT_LIST_HEAD(&task->running); return task; } @@ -1555,10 +1584,10 @@ int iscsi_eh_target_reset(struct scsi_cmnd *sc) spin_lock_bh(&session->lock); if (session->state == ISCSI_STATE_TERMINATE) { failed: - iscsi_session_printk(KERN_INFO, session, - "failing target reset: Could not log " - "back into target [age %d]\n", - session->age); + ISCSI_DBG_EH(session, + "failing target reset: Could not log back into " + "target [age %d]\n", + session->age); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); return FAILED; @@ -1572,7 +1601,7 @@ failed: */ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); - ISCSI_DBG_SESSION(session, "wait for relogin\n"); + ISCSI_DBG_EH(session, "wait for relogin\n"); wait_event_interruptible(conn->ehwait, session->state == ISCSI_STATE_TERMINATE || session->state == ISCSI_STATE_LOGGED_IN || @@ -1582,10 +1611,10 @@ failed: mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); - if (session->state == ISCSI_STATE_LOGGED_IN) - iscsi_session_printk(KERN_INFO, session, - "target reset succeeded\n"); - else + if (session->state == ISCSI_STATE_LOGGED_IN) { + ISCSI_DBG_EH(session, + "target reset succeeded\n"); + } else goto failed; spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); @@ -1601,7 +1630,7 @@ static void iscsi_tmf_timedout(unsigned long data) spin_lock(&session->lock); if (conn->tmf_state == TMF_QUEUED) { conn->tmf_state = TMF_TIMEDOUT; - ISCSI_DBG_SESSION(session, "tmf timedout\n"); + ISCSI_DBG_EH(session, "tmf timedout\n"); /* unblock eh_abort() */ wake_up(&conn->ehwait); } @@ -1621,7 +1650,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, spin_unlock_bh(&session->lock); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); spin_lock_bh(&session->lock); - ISCSI_DBG_SESSION(session, "tmf exec failure\n"); + ISCSI_DBG_EH(session, "tmf exec failure\n"); return -EPERM; } conn->tmfcmd_pdus_cnt++; @@ -1629,7 +1658,7 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, conn->tmf_timer.function = iscsi_tmf_timedout; conn->tmf_timer.data = (unsigned long)conn; add_timer(&conn->tmf_timer); - ISCSI_DBG_SESSION(session, "tmf set timeout\n"); + ISCSI_DBG_EH(session, "tmf set timeout\n"); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); @@ -1716,17 +1745,18 @@ static int iscsi_has_ping_timed_out(struct iscsi_conn *conn) return 0; } -static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) +static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) { + enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; + struct iscsi_task *task = NULL; struct iscsi_cls_session *cls_session; struct iscsi_session *session; struct iscsi_conn *conn; - enum blk_eh_timer_return rc = BLK_EH_NOT_HANDLED; - cls_session = starget_to_session(scsi_target(scmd->device)); + cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; - ISCSI_DBG_SESSION(session, "scsi cmd %p timedout\n", scmd); + ISCSI_DBG_EH(session, "scsi cmd %p timedout\n", sc); spin_lock(&session->lock); if (session->state != ISCSI_STATE_LOGGED_IN) { @@ -1745,6 +1775,26 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) goto done; } + task = (struct iscsi_task *)sc->SCp.ptr; + if (!task) + goto done; + /* + * If we have sent (at least queued to the network layer) a pdu or + * recvd one for the task since the last timeout ask for + * more time. If on the next timeout we have not made progress + * we can check if it is the task or connection when we send the + * nop as a ping. + */ + if (time_after_eq(task->last_xfer, task->last_timeout)) { + ISCSI_DBG_EH(session, "Command making progress. Asking " + "scsi-ml for more time to complete. " + "Last data recv at %lu. Last timeout was at " + "%lu\n.", task->last_xfer, task->last_timeout); + task->have_checked_conn = false; + rc = BLK_EH_RESET_TIMER; + goto done; + } + if (!conn->recv_timeout && !conn->ping_timeout) goto done; /* @@ -1755,23 +1805,32 @@ static enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *scmd) rc = BLK_EH_RESET_TIMER; goto done; } + + /* Assumes nop timeout is shorter than scsi cmd timeout */ + if (task->have_checked_conn) + goto done; + /* - * if we are about to check the transport then give the command - * more time + * Checking the transport already or nop from a cmd timeout still + * running */ - if (time_before_eq(conn->last_recv + (conn->recv_timeout * HZ), - jiffies)) { + if (conn->ping_task) { + task->have_checked_conn = true; rc = BLK_EH_RESET_TIMER; goto done; } - /* if in the middle of checking the transport then give us more time */ - if (conn->ping_task) - rc = BLK_EH_RESET_TIMER; + /* Make sure there is a transport check done */ + iscsi_send_nopout(conn, NULL); + task->have_checked_conn = true; + rc = BLK_EH_RESET_TIMER; + done: + if (task) + task->last_timeout = jiffies; spin_unlock(&session->lock); - ISCSI_DBG_SESSION(session, "return %s\n", rc == BLK_EH_RESET_TIMER ? - "timer reset" : "nh"); + ISCSI_DBG_EH(session, "return %s\n", rc == BLK_EH_RESET_TIMER ? + "timer reset" : "nh"); return rc; } @@ -1841,7 +1900,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; - ISCSI_DBG_SESSION(session, "aborting sc %p\n", sc); + ISCSI_DBG_EH(session, "aborting sc %p\n", sc); mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); @@ -1850,8 +1909,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) * got the command. */ if (!sc->SCp.ptr) { - ISCSI_DBG_SESSION(session, "sc never reached iscsi layer or " - "it completed.\n"); + ISCSI_DBG_EH(session, "sc never reached iscsi layer or " + "it completed.\n"); spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); return SUCCESS; @@ -1865,7 +1924,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) sc->SCp.phase != session->age) { spin_unlock_bh(&session->lock); mutex_unlock(&session->eh_mutex); - ISCSI_DBG_SESSION(session, "failing abort due to dropped " + ISCSI_DBG_EH(session, "failing abort due to dropped " "session.\n"); return FAILED; } @@ -1875,13 +1934,12 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) age = session->age; task = (struct iscsi_task *)sc->SCp.ptr; - ISCSI_DBG_SESSION(session, "aborting [sc %p itt 0x%x]\n", - sc, task->itt); + ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", + sc, task->itt); /* task completed before time out */ if (!task->sc) { - ISCSI_DBG_SESSION(session, "sc completed while abort in " - "progress\n"); + ISCSI_DBG_EH(session, "sc completed while abort in progress\n"); goto success; } @@ -1930,8 +1988,8 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) if (!sc->SCp.ptr) { conn->tmf_state = TMF_INITIAL; /* task completed before tmf abort response */ - ISCSI_DBG_SESSION(session, "sc completed while abort " - "in progress\n"); + ISCSI_DBG_EH(session, "sc completed while abort in " + "progress\n"); goto success; } /* fall through */ @@ -1943,16 +2001,16 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) success: spin_unlock_bh(&session->lock); success_unlocked: - ISCSI_DBG_SESSION(session, "abort success [sc %p itt 0x%x]\n", - sc, task->itt); + ISCSI_DBG_EH(session, "abort success [sc %p itt 0x%x]\n", + sc, task->itt); mutex_unlock(&session->eh_mutex); return SUCCESS; failed: spin_unlock_bh(&session->lock); failed_unlocked: - ISCSI_DBG_SESSION(session, "abort failed [sc %p itt 0x%x]\n", sc, - task ? task->itt : 0); + ISCSI_DBG_EH(session, "abort failed [sc %p itt 0x%x]\n", sc, + task ? task->itt : 0); mutex_unlock(&session->eh_mutex); return FAILED; } @@ -1979,8 +2037,7 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; - ISCSI_DBG_SESSION(session, "LU Reset [sc %p lun %u]\n", - sc, sc->device->lun); + ISCSI_DBG_EH(session, "LU Reset [sc %p lun %u]\n", sc, sc->device->lun); mutex_lock(&session->eh_mutex); spin_lock_bh(&session->lock); @@ -2034,8 +2091,8 @@ int iscsi_eh_device_reset(struct scsi_cmnd *sc) unlock: spin_unlock_bh(&session->lock); done: - ISCSI_DBG_SESSION(session, "dev reset result = %s\n", - rc == SUCCESS ? "SUCCESS" : "FAILED"); + ISCSI_DBG_EH(session, "dev reset result = %s\n", + rc == SUCCESS ? "SUCCESS" : "FAILED"); mutex_unlock(&session->eh_mutex); return rc; } diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 2bc07090321..2e0746d7030 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -686,6 +686,7 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) "offset=%d, datalen=%d)\n", tcp_task->data_offset, tcp_conn->in.datalen); + task->last_xfer = jiffies; rc = iscsi_segment_seek_sg(&tcp_conn->in.segment, sdb->table.sgl, sdb->table.nents, @@ -713,9 +714,10 @@ iscsi_tcp_hdr_dissect(struct iscsi_conn *conn, struct iscsi_hdr *hdr) rc = ISCSI_ERR_BAD_ITT; else if (ahslen) rc = ISCSI_ERR_AHSLEN; - else if (task->sc->sc_data_direction == DMA_TO_DEVICE) + else if (task->sc->sc_data_direction == DMA_TO_DEVICE) { + task->last_xfer = jiffies; rc = iscsi_tcp_r2t_rsp(conn, task); - else + } else rc = ISCSI_ERR_PROTO; spin_unlock(&conn->session->lock); break; diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index 4a990f4da4e..cca8e4ab037 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -216,7 +216,7 @@ qla24xx_soft_reset(struct qla_hw_data *ha) static int qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram, - uint16_t ram_words, void **nxt) + uint32_t ram_words, void **nxt) { int rval; uint32_t cnt, stat, timer, words, idx; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 26202612932..f2ce8e3cc91 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -2301,7 +2301,7 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" }; char *link_speed; int rval; - uint16_t mb[6]; + uint16_t mb[4]; struct qla_hw_data *ha = vha->hw; if (!IS_IIDMA_CAPABLE(ha)) diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 451ece0760b..fe69f305767 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1267,17 +1267,22 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) mcp->mb[0] = MBC_GET_FIRMWARE_STATE; mcp->out_mb = MBX_0; - mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + if (IS_FWI2_CAPABLE(vha->hw)) + mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; + else + mcp->in_mb = MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); /* Return firmware states. */ states[0] = mcp->mb[1]; - states[1] = mcp->mb[2]; - states[2] = mcp->mb[3]; - states[3] = mcp->mb[4]; - states[4] = mcp->mb[5]; + if (IS_FWI2_CAPABLE(vha->hw)) { + states[1] = mcp->mb[2]; + states[2] = mcp->mb[3]; + states[3] = mcp->mb[4]; + states[4] = mcp->mb[5]; + } if (rval != QLA_SUCCESS) { /*EMPTY*/ @@ -2697,10 +2702,13 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, mcp->mb[0] = MBC_PORT_PARAMS; mcp->mb[1] = loop_id; mcp->mb[2] = BIT_0; - mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0); - mcp->mb[4] = mcp->mb[5] = 0; - mcp->out_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; - mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_1|MBX_0; + if (IS_QLA81XX(vha->hw)) + mcp->mb[3] = port_speed & (BIT_5|BIT_4|BIT_3|BIT_2|BIT_1|BIT_0); + else + mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0); + mcp->mb[9] = vha->vp_idx; + mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_3|MBX_1|MBX_0; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); @@ -2710,8 +2718,6 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, mb[0] = mcp->mb[0]; mb[1] = mcp->mb[1]; mb[3] = mcp->mb[3]; - mb[4] = mcp->mb[4]; - mb[5] = mcp->mb[5]; } if (rval != QLA_SUCCESS) { diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index dcf011679c8..f0396e79b6f 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1663,7 +1663,7 @@ skip_pio: /* queue 0 uses two msix vectors */ if (ql2xmultique_tag) { cpus = num_online_cpus(); - ha->max_rsp_queues = (ha->msix_count - 1 - cpus) ? + ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ? (cpus + 1) : (ha->msix_count - 1); ha->max_req_queues = 2; } else if (ql2xmaxqueues > 1) { diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index b63feaf4312..84369705a9a 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.01-k3" +#define QLA2XXX_VERSION "8.03.01-k4" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 3 diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c index 41a21772df1..fb9af207d61 100644 --- a/drivers/scsi/scsi_debug.c +++ b/drivers/scsi/scsi_debug.c @@ -101,6 +101,8 @@ static const char * scsi_debug_version_date = "20070104"; #define DEF_DIF 0 #define DEF_GUARD 0 #define DEF_ATO 1 +#define DEF_PHYSBLK_EXP 0 +#define DEF_LOWEST_ALIGNED 0 /* bit mask values for scsi_debug_opts */ #define SCSI_DEBUG_OPT_NOISE 1 @@ -156,6 +158,8 @@ static int scsi_debug_dix = DEF_DIX; static int scsi_debug_dif = DEF_DIF; static int scsi_debug_guard = DEF_GUARD; static int scsi_debug_ato = DEF_ATO; +static int scsi_debug_physblk_exp = DEF_PHYSBLK_EXP; +static int scsi_debug_lowest_aligned = DEF_LOWEST_ALIGNED; static int scsi_debug_cmnd_count = 0; @@ -657,7 +661,12 @@ static unsigned char vpdb0_data[] = { static int inquiry_evpd_b0(unsigned char * arr) { + unsigned int gran; + memcpy(arr, vpdb0_data, sizeof(vpdb0_data)); + gran = 1 << scsi_debug_physblk_exp; + arr[2] = (gran >> 8) & 0xff; + arr[3] = gran & 0xff; if (sdebug_store_sectors > 0x400) { arr[4] = (sdebug_store_sectors >> 24) & 0xff; arr[5] = (sdebug_store_sectors >> 16) & 0xff; @@ -945,6 +954,9 @@ static int resp_readcap16(struct scsi_cmnd * scp, arr[9] = (scsi_debug_sector_size >> 16) & 0xff; arr[10] = (scsi_debug_sector_size >> 8) & 0xff; arr[11] = scsi_debug_sector_size & 0xff; + arr[13] = scsi_debug_physblk_exp & 0xf; + arr[14] = (scsi_debug_lowest_aligned >> 8) & 0x3f; + arr[15] = scsi_debug_lowest_aligned & 0xff; if (scsi_debug_dif) { arr[12] = (scsi_debug_dif - 1) << 1; /* P_TYPE */ @@ -2380,6 +2392,8 @@ module_param_named(dix, scsi_debug_dix, int, S_IRUGO); module_param_named(dif, scsi_debug_dif, int, S_IRUGO); module_param_named(guard, scsi_debug_guard, int, S_IRUGO); module_param_named(ato, scsi_debug_ato, int, S_IRUGO); +module_param_named(physblk_exp, scsi_debug_physblk_exp, int, S_IRUGO); +module_param_named(lowest_aligned, scsi_debug_lowest_aligned, int, S_IRUGO); MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); MODULE_DESCRIPTION("SCSI debug adapter driver"); @@ -2401,7 +2415,9 @@ MODULE_PARM_DESC(ptype, "SCSI peripheral type(def=0[disk])"); MODULE_PARM_DESC(scsi_level, "SCSI level to simulate(def=5[SPC-3])"); MODULE_PARM_DESC(virtual_gb, "virtual gigabyte size (def=0 -> use dev_size_mb)"); MODULE_PARM_DESC(vpd_use_hostno, "0 -> dev ids ignore hostno (def=1 -> unique dev ids)"); -MODULE_PARM_DESC(sector_size, "hardware sector size in bytes (def=512)"); +MODULE_PARM_DESC(sector_size, "logical block size in bytes (def=512)"); +MODULE_PARM_DESC(physblk_exp, "physical block exponent (def=0)"); +MODULE_PARM_DESC(lowest_aligned, "lowest aligned lba (def=0)"); MODULE_PARM_DESC(dix, "data integrity extensions mask (def=0)"); MODULE_PARM_DESC(dif, "data integrity field type: 0-3 (def=0)"); MODULE_PARM_DESC(guard, "protection checksum: 0=crc, 1=ip (def=0)"); @@ -2874,6 +2890,18 @@ static int __init scsi_debug_init(void) return -EINVAL; } + if (scsi_debug_physblk_exp > 15) { + printk(KERN_ERR "scsi_debug_init: invalid physblk_exp %u\n", + scsi_debug_physblk_exp); + return -EINVAL; + } + + if (scsi_debug_lowest_aligned > 0x3fff) { + printk(KERN_ERR "scsi_debug_init: lowest_aligned too big: %u\n", + scsi_debug_lowest_aligned); + return -EINVAL; + } + if (scsi_debug_dev_size_mb < 1) scsi_debug_dev_size_mb = 1; /* force minimum 1 MB ramdisk */ sz = (unsigned long)scsi_debug_dev_size_mb * 1048576; diff --git a/drivers/scsi/scsi_devinfo.c b/drivers/scsi/scsi_devinfo.c index 8821df9a277..93c2622cb96 100644 --- a/drivers/scsi/scsi_devinfo.c +++ b/drivers/scsi/scsi_devinfo.c @@ -24,6 +24,13 @@ struct scsi_dev_info_list { unsigned compatible; /* for use with scsi_static_device_list entries */ }; +struct scsi_dev_info_list_table { + struct list_head node; /* our node for being on the master list */ + struct list_head scsi_dev_info_list; /* head of dev info list */ + const char *name; /* name of list for /proc (NULL for global) */ + int key; /* unique numeric identifier */ +}; + static const char spaces[] = " "; /* 16 of them */ static unsigned scsi_default_dev_flags; @@ -247,6 +254,22 @@ static struct { { NULL, NULL, NULL, 0 }, }; +static struct scsi_dev_info_list_table *scsi_devinfo_lookup_by_key(int key) +{ + struct scsi_dev_info_list_table *devinfo_table; + int found = 0; + + list_for_each_entry(devinfo_table, &scsi_dev_info_list, node) + if (devinfo_table->key == key) { + found = 1; + break; + } + if (!found) + return ERR_PTR(-EINVAL); + + return devinfo_table; +} + /* * scsi_strcpy_devinfo: called from scsi_dev_info_list_add to copy into * devinfo vendor and model strings. @@ -296,7 +319,38 @@ static void scsi_strcpy_devinfo(char *name, char *to, size_t to_length, static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, char *strflags, int flags) { + return scsi_dev_info_list_add_keyed(compatible, vendor, model, + strflags, flags, + SCSI_DEVINFO_GLOBAL); +} + +/** + * scsi_dev_info_list_add_keyed - add one dev_info list entry. + * @compatible: if true, null terminate short strings. Otherwise space pad. + * @vendor: vendor string + * @model: model (product) string + * @strflags: integer string + * @flags: if strflags NULL, use this flag value + * @key: specify list to use + * + * Description: + * Create and add one dev_info entry for @vendor, @model, + * @strflags or @flag in list specified by @key. If @compatible, + * add to the tail of the list, do not space pad, and set + * devinfo->compatible. The scsi_static_device_list entries are + * added with @compatible 1 and @clfags NULL. + * + * Returns: 0 OK, -error on failure. + **/ +int scsi_dev_info_list_add_keyed(int compatible, char *vendor, char *model, + char *strflags, int flags, int key) +{ struct scsi_dev_info_list *devinfo; + struct scsi_dev_info_list_table *devinfo_table = + scsi_devinfo_lookup_by_key(key); + + if (IS_ERR(devinfo_table)) + return PTR_ERR(devinfo_table); devinfo = kmalloc(sizeof(*devinfo), GFP_KERNEL); if (!devinfo) { @@ -317,12 +371,15 @@ static int scsi_dev_info_list_add(int compatible, char *vendor, char *model, devinfo->compatible = compatible; if (compatible) - list_add_tail(&devinfo->dev_info_list, &scsi_dev_info_list); + list_add_tail(&devinfo->dev_info_list, + &devinfo_table->scsi_dev_info_list); else - list_add(&devinfo->dev_info_list, &scsi_dev_info_list); + list_add(&devinfo->dev_info_list, + &devinfo_table->scsi_dev_info_list); return 0; } +EXPORT_SYMBOL(scsi_dev_info_list_add_keyed); /** * scsi_dev_info_list_add_str - parse dev_list and add to the scsi_dev_info_list. @@ -382,22 +439,48 @@ static int scsi_dev_info_list_add_str(char *dev_list) * @model: model name * * Description: - * Search the scsi_dev_info_list for an entry matching @vendor and - * @model, if found, return the matching flags value, else return - * the host or global default settings. Called during scan time. + * Search the global scsi_dev_info_list (specified by list zero) + * for an entry matching @vendor and @model, if found, return the + * matching flags value, else return the host or global default + * settings. Called during scan time. **/ int scsi_get_device_flags(struct scsi_device *sdev, const unsigned char *vendor, const unsigned char *model) { + return scsi_get_device_flags_keyed(sdev, vendor, model, + SCSI_DEVINFO_GLOBAL); +} + + +/** + * get_device_flags_keyed - get device specific flags from the dynamic device list. + * @sdev: &scsi_device to get flags for + * @vendor: vendor name + * @model: model name + * @key: list to look up + * + * Description: + * Search the scsi_dev_info_list specified by @key for an entry + * matching @vendor and @model, if found, return the matching + * flags value, else return the host or global default settings. + * Called during scan time. + **/ +int scsi_get_device_flags_keyed(struct scsi_device *sdev, + const unsigned char *vendor, + const unsigned char *model, + int key) +{ struct scsi_dev_info_list *devinfo; - unsigned int bflags; + struct scsi_dev_info_list_table *devinfo_table; + + devinfo_table = scsi_devinfo_lookup_by_key(key); - bflags = sdev->sdev_bflags; - if (!bflags) - bflags = scsi_default_dev_flags; + if (IS_ERR(devinfo_table)) + return PTR_ERR(devinfo_table); - list_for_each_entry(devinfo, &scsi_dev_info_list, dev_info_list) { + list_for_each_entry(devinfo, &devinfo_table->scsi_dev_info_list, + dev_info_list) { if (devinfo->compatible) { /* * Behave like the older version of get_device_flags. @@ -447,32 +530,89 @@ int scsi_get_device_flags(struct scsi_device *sdev, return devinfo->flags; } } - return bflags; + /* nothing found, return nothing */ + if (key != SCSI_DEVINFO_GLOBAL) + return 0; + + /* except for the global list, where we have an exception */ + if (sdev->sdev_bflags) + return sdev->sdev_bflags; + + return scsi_default_dev_flags; } +EXPORT_SYMBOL(scsi_get_device_flags_keyed); #ifdef CONFIG_SCSI_PROC_FS +struct double_list { + struct list_head *top; + struct list_head *bottom; +}; + static int devinfo_seq_show(struct seq_file *m, void *v) { + struct double_list *dl = v; + struct scsi_dev_info_list_table *devinfo_table = + list_entry(dl->top, struct scsi_dev_info_list_table, node); struct scsi_dev_info_list *devinfo = - list_entry(v, struct scsi_dev_info_list, dev_info_list); + list_entry(dl->bottom, struct scsi_dev_info_list, + dev_info_list); + + if (devinfo_table->scsi_dev_info_list.next == dl->bottom && + devinfo_table->name) + seq_printf(m, "[%s]:\n", devinfo_table->name); seq_printf(m, "'%.8s' '%.16s' 0x%x\n", - devinfo->vendor, devinfo->model, devinfo->flags); + devinfo->vendor, devinfo->model, devinfo->flags); return 0; } -static void * devinfo_seq_start(struct seq_file *m, loff_t *pos) +static void *devinfo_seq_start(struct seq_file *m, loff_t *ppos) { - return seq_list_start(&scsi_dev_info_list, *pos); + struct double_list *dl = kmalloc(sizeof(*dl), GFP_KERNEL); + loff_t pos = *ppos; + + if (!dl) + return NULL; + + list_for_each(dl->top, &scsi_dev_info_list) { + struct scsi_dev_info_list_table *devinfo_table = + list_entry(dl->top, struct scsi_dev_info_list_table, + node); + list_for_each(dl->bottom, &devinfo_table->scsi_dev_info_list) + if (pos-- == 0) + return dl; + } + + kfree(dl); + return NULL; } -static void * devinfo_seq_next(struct seq_file *m, void *v, loff_t *pos) +static void *devinfo_seq_next(struct seq_file *m, void *v, loff_t *ppos) { - return seq_list_next(v, &scsi_dev_info_list, pos); + struct double_list *dl = v; + struct scsi_dev_info_list_table *devinfo_table = + list_entry(dl->top, struct scsi_dev_info_list_table, node); + + ++*ppos; + dl->bottom = dl->bottom->next; + while (&devinfo_table->scsi_dev_info_list == dl->bottom) { + dl->top = dl->top->next; + if (dl->top == &scsi_dev_info_list) { + kfree(dl); + return NULL; + } + devinfo_table = list_entry(dl->top, + struct scsi_dev_info_list_table, + node); + dl->bottom = devinfo_table->scsi_dev_info_list.next; + } + + return dl; } static void devinfo_seq_stop(struct seq_file *m, void *v) { + kfree(v); } static const struct seq_operations scsi_devinfo_seq_ops = { @@ -549,19 +689,78 @@ MODULE_PARM_DESC(default_dev_flags, **/ void scsi_exit_devinfo(void) { - struct list_head *lh, *lh_next; - struct scsi_dev_info_list *devinfo; - #ifdef CONFIG_SCSI_PROC_FS remove_proc_entry("scsi/device_info", NULL); #endif - list_for_each_safe(lh, lh_next, &scsi_dev_info_list) { + scsi_dev_info_remove_list(SCSI_DEVINFO_GLOBAL); +} + +/** + * scsi_dev_info_add_list - add a new devinfo list + * @key: key of the list to add + * @name: Name of the list to add (for /proc/scsi/device_info) + * + * Adds the requested list, returns zero on success, -EEXIST if the + * key is already registered to a list, or other error on failure. + */ +int scsi_dev_info_add_list(int key, const char *name) +{ + struct scsi_dev_info_list_table *devinfo_table = + scsi_devinfo_lookup_by_key(key); + + if (!IS_ERR(devinfo_table)) + /* list already exists */ + return -EEXIST; + + devinfo_table = kmalloc(sizeof(*devinfo_table), GFP_KERNEL); + + if (!devinfo_table) + return -ENOMEM; + + INIT_LIST_HEAD(&devinfo_table->node); + INIT_LIST_HEAD(&devinfo_table->scsi_dev_info_list); + devinfo_table->name = name; + devinfo_table->key = key; + list_add_tail(&devinfo_table->node, &scsi_dev_info_list); + + return 0; +} +EXPORT_SYMBOL(scsi_dev_info_add_list); + +/** + * scsi_dev_info_remove_list - destroy an added devinfo list + * @key: key of the list to destroy + * + * Iterates over the entire list first, freeing all the values, then + * frees the list itself. Returns 0 on success or -EINVAL if the key + * can't be found. + */ +int scsi_dev_info_remove_list(int key) +{ + struct list_head *lh, *lh_next; + struct scsi_dev_info_list_table *devinfo_table = + scsi_devinfo_lookup_by_key(key); + + if (IS_ERR(devinfo_table)) + /* no such list */ + return -EINVAL; + + /* remove from the master list */ + list_del(&devinfo_table->node); + + list_for_each_safe(lh, lh_next, &devinfo_table->scsi_dev_info_list) { + struct scsi_dev_info_list *devinfo; + devinfo = list_entry(lh, struct scsi_dev_info_list, dev_info_list); kfree(devinfo); } + kfree(devinfo_table); + + return 0; } +EXPORT_SYMBOL(scsi_dev_info_remove_list); /** * scsi_init_devinfo - set up the dynamic device list. @@ -577,10 +776,14 @@ int __init scsi_init_devinfo(void) #endif int error, i; - error = scsi_dev_info_list_add_str(scsi_dev_flags); + error = scsi_dev_info_add_list(SCSI_DEVINFO_GLOBAL, NULL); if (error) return error; + error = scsi_dev_info_list_add_str(scsi_dev_flags); + if (error) + goto out; + for (i = 0; scsi_static_device_list[i].vendor; i++) { error = scsi_dev_info_list_add(1 /* compatibile */, scsi_static_device_list[i].vendor, diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 30f3275e119..f3c40898fc7 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -1207,6 +1207,7 @@ int scsi_prep_fn(struct request_queue *q, struct request *req) ret = scsi_setup_blk_pc_cmnd(sdev, req); return scsi_prep_return(q, req, ret); } +EXPORT_SYMBOL(scsi_prep_fn); /* * scsi_dev_queue_ready: if we can send requests to sdev, return 1 else diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index fbc83bebdd8..021e503c8c4 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -39,9 +39,25 @@ static inline void scsi_log_completion(struct scsi_cmnd *cmd, int disposition) #endif /* scsi_devinfo.c */ + +/* list of keys for the lists */ +enum { + SCSI_DEVINFO_GLOBAL = 0, + SCSI_DEVINFO_SPI, +}; + extern int scsi_get_device_flags(struct scsi_device *sdev, const unsigned char *vendor, const unsigned char *model); +extern int scsi_get_device_flags_keyed(struct scsi_device *sdev, + const unsigned char *vendor, + const unsigned char *model, int key); +extern int scsi_dev_info_list_add_keyed(int compatible, char *vendor, + char *model, char *strflags, + int flags, int key); +extern int scsi_dev_info_add_list(int key, const char *name); +extern int scsi_dev_info_remove_list(int key); + extern int __init scsi_init_devinfo(void); extern void scsi_exit_devinfo(void); @@ -71,7 +87,6 @@ extern int scsi_init_queue(void); extern void scsi_exit_queue(void); struct request_queue; struct request; -extern int scsi_prep_fn(struct request_queue *, struct request *); extern struct kmem_cache *scsi_sdb_cache; /* scsi_proc.c */ diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index fa4711d1274..91482f2dcc5 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -420,29 +420,12 @@ static int scsi_bus_resume(struct device * dev) return err; } -static int scsi_bus_remove(struct device *dev) -{ - struct device_driver *drv = dev->driver; - struct scsi_device *sdev = to_scsi_device(dev); - int err = 0; - - /* reset the prep_fn back to the default since the - * driver may have altered it and it's being removed */ - blk_queue_prep_rq(sdev->request_queue, scsi_prep_fn); - - if (drv && drv->remove) - err = drv->remove(dev); - - return 0; -} - struct bus_type scsi_bus_type = { .name = "scsi", .match = scsi_bus_match, .uevent = scsi_bus_uevent, .suspend = scsi_bus_suspend, .resume = scsi_bus_resume, - .remove = scsi_bus_remove, }; EXPORT_SYMBOL_GPL(scsi_bus_type); diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 3f64d93b6c8..2eee9e6e4fe 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -3397,7 +3397,6 @@ fc_destroy_bsgjob(struct fc_bsg_job *job) kfree(job); } - /** * fc_bsg_jobdone - completion routine for bsg requests that the LLD has * completed @@ -3408,15 +3407,10 @@ fc_bsg_jobdone(struct fc_bsg_job *job) { struct request *req = job->req; struct request *rsp = req->next_rq; - unsigned long flags; int err; - spin_lock_irqsave(&job->job_lock, flags); - job->state_flags |= FC_RQST_STATE_DONE; - job->ref_cnt--; - spin_unlock_irqrestore(&job->job_lock, flags); - err = job->req->errors = job->reply->result; + if (err < 0) /* we're only returning the result field in the reply */ job->req->sense_len = sizeof(uint32_t); @@ -3433,13 +3427,27 @@ fc_bsg_jobdone(struct fc_bsg_job *job) rsp->resid_len -= min(job->reply->reply_payload_rcv_len, rsp->resid_len); } + blk_complete_request(req); +} - blk_end_request_all(req, err); +/** + * fc_bsg_softirq_done - softirq done routine for destroying the bsg requests + * @req: BSG request that holds the job to be destroyed + */ +static void fc_bsg_softirq_done(struct request *rq) +{ + struct fc_bsg_job *job = rq->special; + unsigned long flags; + spin_lock_irqsave(&job->job_lock, flags); + job->state_flags |= FC_RQST_STATE_DONE; + job->ref_cnt--; + spin_unlock_irqrestore(&job->job_lock, flags); + + blk_end_request_all(rq, rq->errors); fc_destroy_bsgjob(job); } - /** * fc_bsg_job_timeout - handler for when a bsg request timesout * @req: request that timed out @@ -3471,19 +3479,13 @@ fc_bsg_job_timeout(struct request *req) "abort failed with status %d\n", err); } - if (!done) { - spin_lock_irqsave(&job->job_lock, flags); - job->ref_cnt--; - spin_unlock_irqrestore(&job->job_lock, flags); - fc_destroy_bsgjob(job); - } - /* the blk_end_sync_io() doesn't check the error */ - return BLK_EH_HANDLED; + if (done) + return BLK_EH_NOT_HANDLED; + else + return BLK_EH_HANDLED; } - - static int fc_bsg_map_buffer(struct fc_bsg_buffer *buf, struct request *req) { @@ -3859,7 +3861,7 @@ fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host) struct fc_internal *i = to_fc_internal(shost->transportt); struct request_queue *q; int err; - char bsg_name[BUS_ID_SIZE]; /*20*/ + char bsg_name[20]; fc_host->rqst_q = NULL; @@ -3879,6 +3881,7 @@ fc_bsg_hostadd(struct Scsi_Host *shost, struct fc_host_attrs *fc_host) q->queuedata = shost; queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); + blk_queue_softirq_done(q, fc_bsg_softirq_done); blk_queue_rq_timed_out(q, fc_bsg_job_timeout); blk_queue_rq_timeout(q, FC_DEFAULT_BSG_TIMEOUT); @@ -3924,6 +3927,7 @@ fc_bsg_rportadd(struct Scsi_Host *shost, struct fc_rport *rport) q->queuedata = rport; queue_flag_set_unlocked(QUEUE_FLAG_BIDI, q); + blk_queue_softirq_done(q, fc_bsg_softirq_done); blk_queue_rq_timed_out(q, fc_bsg_job_timeout); blk_queue_rq_timeout(q, BLK_DEFAULT_SG_TIMEOUT); diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index f3e664628d7..783e33c65eb 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -692,6 +692,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) "Too many iscsi targets. Max " "number of targets is %d.\n", ISCSI_MAX_TARGET - 1); + err = -EOVERFLOW; goto release_host; } } diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index d606452297c..0895d3c71b0 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -173,9 +173,9 @@ static void sas_smp_request(struct request_queue *q, struct Scsi_Host *shost, ret = handler(shost, rphy, req); req->errors = ret; - spin_lock_irq(q->queue_lock); + blk_end_request_all(req, ret); - req->end_io(req, ret); + spin_lock_irq(q->queue_lock); } } diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index 654a34fb04c..c25bd9a34e0 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -46,6 +46,22 @@ #define DV_RETRIES 3 /* should only need at most * two cc/ua clears */ +/* Our blacklist flags */ +enum { + SPI_BLIST_NOIUS = 0x1, +}; + +/* blacklist table, modelled on scsi_devinfo.c */ +static struct { + char *vendor; + char *model; + unsigned flags; +} spi_static_device_list[] __initdata = { + {"HP", "Ultrium 3-SCSI", SPI_BLIST_NOIUS }, + {"IBM", "ULTRIUM-TD3", SPI_BLIST_NOIUS }, + {NULL, NULL, 0} +}; + /* Private data accessors (keep these out of the header file) */ #define spi_dv_in_progress(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_in_progress) #define spi_dv_mutex(x) (((struct spi_transport_attrs *)&(x)->starget_data)->dv_mutex) @@ -207,6 +223,9 @@ static int spi_device_configure(struct transport_container *tc, { struct scsi_device *sdev = to_scsi_device(dev); struct scsi_target *starget = sdev->sdev_target; + unsigned bflags = scsi_get_device_flags_keyed(sdev, &sdev->inquiry[8], + &sdev->inquiry[16], + SCSI_DEVINFO_SPI); /* Populate the target capability fields with the values * gleaned from the device inquiry */ @@ -216,6 +235,10 @@ static int spi_device_configure(struct transport_container *tc, spi_support_dt(starget) = scsi_device_dt(sdev); spi_support_dt_only(starget) = scsi_device_dt_only(sdev); spi_support_ius(starget) = scsi_device_ius(sdev); + if (bflags & SPI_BLIST_NOIUS) { + dev_info(dev, "Information Units disabled by blacklist\n"); + spi_support_ius(starget) = 0; + } spi_support_qas(starget) = scsi_device_qas(sdev); return 0; @@ -833,7 +856,7 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer) return; } - if (!scsi_device_wide(sdev)) { + if (!spi_support_wide(starget)) { spi_max_width(starget) = 0; max_width = 0; } @@ -860,7 +883,7 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer) return; /* device can't handle synchronous */ - if (!scsi_device_sync(sdev) && !scsi_device_dt(sdev)) + if (!spi_support_sync(starget) && !spi_support_dt(starget)) return; /* len == -1 is the signal that we need to ascertain the @@ -876,13 +899,14 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer) /* try QAS requests; this should be harmless to set if the * target supports it */ - if (scsi_device_qas(sdev) && spi_max_qas(starget)) { + if (spi_support_qas(starget) && spi_max_qas(starget)) { DV_SET(qas, 1); } else { DV_SET(qas, 0); } - if (scsi_device_ius(sdev) && spi_max_iu(starget) && min_period < 9) { + if (spi_support_ius(starget) && spi_max_iu(starget) && + min_period < 9) { /* This u320 (or u640). Set IU transfers */ DV_SET(iu, 1); /* Then set the optional parameters */ @@ -902,7 +926,7 @@ spi_dv_device_internal(struct scsi_device *sdev, u8 *buffer) i->f->get_signalling(shost); if (spi_signalling(shost) == SPI_SIGNAL_SE || spi_signalling(shost) == SPI_SIGNAL_HVD || - !scsi_device_dt(sdev)) { + !spi_support_dt(starget)) { DV_SET(dt, 0); } else { DV_SET(dt, 1); @@ -1523,7 +1547,21 @@ EXPORT_SYMBOL(spi_release_transport); static __init int spi_transport_init(void) { - int error = transport_class_register(&spi_transport_class); + int error = scsi_dev_info_add_list(SCSI_DEVINFO_SPI, + "SCSI Parallel Transport Class"); + if (!error) { + int i; + + for (i = 0; spi_static_device_list[i].vendor; i++) + scsi_dev_info_list_add_keyed(1, /* compatible */ + spi_static_device_list[i].vendor, + spi_static_device_list[i].model, + NULL, + spi_static_device_list[i].flags, + SCSI_DEVINFO_SPI); + } + + error = transport_class_register(&spi_transport_class); if (error) return error; error = anon_transport_class_register(&spi_device_class); @@ -1535,6 +1573,7 @@ static void __exit spi_transport_exit(void) transport_class_unregister(&spi_transport_class); anon_transport_class_unregister(&spi_device_class); transport_class_unregister(&spi_host_class); + scsi_dev_info_remove_list(SCSI_DEVINFO_SPI); } MODULE_AUTHOR("Martin Hicks"); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 878b17a9af3..5616cd780ff 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -1307,6 +1307,7 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, int sense_valid = 0; int the_result; int retries = 3; + unsigned int alignment; unsigned long long lba; unsigned sector_size; @@ -1358,6 +1359,16 @@ static int read_capacity_16(struct scsi_disk *sdkp, struct scsi_device *sdp, return -EOVERFLOW; } + /* Logical blocks per physical block exponent */ + sdkp->hw_sector_size = (1 << (buffer[13] & 0xf)) * sector_size; + + /* Lowest aligned logical block */ + alignment = ((buffer[14] & 0x3f) << 8 | buffer[15]) * sector_size; + blk_queue_alignment_offset(sdp->request_queue, alignment); + if (alignment && sdkp->first_scan) + sd_printk(KERN_NOTICE, sdkp, + "physical block alignment offset: %u\n", alignment); + sdkp->capacity = lba + 1; return sector_size; } @@ -1409,6 +1420,7 @@ static int read_capacity_10(struct scsi_disk *sdkp, struct scsi_device *sdp, } sdkp->capacity = lba + 1; + sdkp->hw_sector_size = sector_size; return sector_size; } @@ -1521,11 +1533,17 @@ got_data: string_get_size(sz, STRING_UNITS_10, cap_str_10, sizeof(cap_str_10)); - if (sdkp->first_scan || old_capacity != sdkp->capacity) + if (sdkp->first_scan || old_capacity != sdkp->capacity) { sd_printk(KERN_NOTICE, sdkp, - "%llu %d-byte hardware sectors: (%s/%s)\n", + "%llu %d-byte logical blocks: (%s/%s)\n", (unsigned long long)sdkp->capacity, sector_size, cap_str_10, cap_str_2); + + if (sdkp->hw_sector_size != sector_size) + sd_printk(KERN_NOTICE, sdkp, + "%u-byte physical blocks\n", + sdkp->hw_sector_size); + } } /* Rescale capacity to 512-byte units */ @@ -1538,6 +1556,7 @@ got_data: else if (sector_size == 256) sdkp->capacity >>= 1; + blk_queue_physical_block_size(sdp->request_queue, sdkp->hw_sector_size); sdkp->device->sector_size = sector_size; } @@ -1776,6 +1795,52 @@ void sd_read_app_tag_own(struct scsi_disk *sdkp, unsigned char *buffer) } /** + * sd_read_block_limits - Query disk device for preferred I/O sizes. + * @disk: disk to query + */ +static void sd_read_block_limits(struct scsi_disk *sdkp) +{ + unsigned int sector_sz = sdkp->device->sector_size; + char *buffer; + + /* Block Limits VPD */ + buffer = scsi_get_vpd_page(sdkp->device, 0xb0); + + if (buffer == NULL) + return; + + blk_queue_io_min(sdkp->disk->queue, + get_unaligned_be16(&buffer[6]) * sector_sz); + blk_queue_io_opt(sdkp->disk->queue, + get_unaligned_be32(&buffer[12]) * sector_sz); + + kfree(buffer); +} + +/** + * sd_read_block_characteristics - Query block dev. characteristics + * @disk: disk to query + */ +static void sd_read_block_characteristics(struct scsi_disk *sdkp) +{ + char *buffer; + u16 rot; + + /* Block Device Characteristics VPD */ + buffer = scsi_get_vpd_page(sdkp->device, 0xb1); + + if (buffer == NULL) + return; + + rot = get_unaligned_be16(&buffer[4]); + + if (rot == 1) + queue_flag_set_unlocked(QUEUE_FLAG_NONROT, sdkp->disk->queue); + + kfree(buffer); +} + +/** * sd_revalidate_disk - called the first time a new disk is seen, * performs disk spin up, read_capacity, etc. * @disk: struct gendisk we care about @@ -1812,6 +1877,8 @@ static int sd_revalidate_disk(struct gendisk *disk) */ if (sdkp->media_present) { sd_read_capacity(sdkp, buffer); + sd_read_block_limits(sdkp); + sd_read_block_characteristics(sdkp); sd_read_write_protect_flag(sdkp, buffer); sd_read_cache_type(sdkp, buffer); sd_read_app_tag_own(sdkp, buffer); @@ -1934,6 +2001,8 @@ static void sd_probe_async(void *data, async_cookie_t cookie) add_disk(gd); sd_dif_config_host(sdkp); + sd_revalidate_disk(gd); + sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n", sdp->removable ? "removable " : ""); } @@ -2054,6 +2123,7 @@ static int sd_remove(struct device *dev) async_synchronize_full(); sdkp = dev_get_drvdata(dev); + blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn); device_del(&sdkp->dev); del_gendisk(sdkp->disk); sd_shutdown(dev); diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 708778cf5f0..8474b5bad3f 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -45,6 +45,7 @@ struct scsi_disk { unsigned int openers; /* protected by BKL for now, yuck */ sector_t capacity; /* size in 512-byte sectors */ u32 index; + unsigned short hw_sector_size; u8 media_present; u8 write_prot; u8 protection_type;/* Data Integrity Field */ diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index cd350dfc121..cce0fe4c8a3 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -881,6 +881,7 @@ static int sr_remove(struct device *dev) { struct scsi_cd *cd = dev_get_drvdata(dev); + blk_queue_prep_rq(cd->device->request_queue, scsi_prep_fn); del_gendisk(cd->disk); mutex_lock(&sr_ref_mutex); diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 69ad4945c93..297deb817a5 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -2321,8 +2321,9 @@ static void sym_int_par (struct sym_hcb *np, u_short sist) int phase = cmd & 7; struct sym_ccb *cp = sym_ccb_from_dsa(np, dsa); - printf("%s: SCSI parity error detected: SCR1=%d DBC=%x SBCL=%x\n", - sym_name(np), hsts, dbc, sbcl); + if (printk_ratelimit()) + printf("%s: SCSI parity error detected: SCR1=%d DBC=%x SBCL=%x\n", + sym_name(np), hsts, dbc, sbcl); /* * Check that the chip is connected to the SCSI BUS. diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c index e371a9c1534..a07015d646d 100644 --- a/drivers/serial/8250_pci.c +++ b/drivers/serial/8250_pci.c @@ -398,8 +398,7 @@ static int sbs_init(struct pci_dev *dev) { u8 __iomem *p; - p = ioremap_nocache(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + p = pci_ioremap_bar(dev, 0); if (p == NULL) return -ENOMEM; @@ -423,8 +422,7 @@ static void __devexit sbs_exit(struct pci_dev *dev) { u8 __iomem *p; - p = ioremap_nocache(pci_resource_start(dev, 0), - pci_resource_len(dev, 0)); + p = pci_ioremap_bar(dev, 0); /* FIXME: What if resource_len < OCT_REG_CR_OFF */ if (p != NULL) writeb(0, p + OCT_REG_CR_OFF); diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 1132c5cae7a..037c1e0b7c4 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1320,6 +1320,16 @@ config SERIAL_SGI_IOC3 If you have an SGI Altix with an IOC3 serial card, say Y or M. Otherwise, say N. +config SERIAL_MSM + bool "MSM on-chip serial port support" + depends on ARM && ARCH_MSM + select SERIAL_CORE + +config SERIAL_MSM_CONSOLE + bool "MSM serial console support" + depends on SERIAL_MSM=y + select SERIAL_CORE_CONSOLE + config SERIAL_NETX tristate "NetX serial port support" depends on ARM && ARCH_NETX diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index 45a8658f54d..d5a29981c6c 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -71,6 +71,7 @@ obj-$(CONFIG_SERIAL_SGI_IOC4) += ioc4_serial.o obj-$(CONFIG_SERIAL_SGI_IOC3) += ioc3_serial.o obj-$(CONFIG_SERIAL_ATMEL) += atmel_serial.o obj-$(CONFIG_SERIAL_UARTLITE) += uartlite.o +obj-$(CONFIG_SERIAL_MSM) += msm_serial.o obj-$(CONFIG_SERIAL_NETX) += netx-serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM) += of_serial.o obj-$(CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL) += nwpserial.o diff --git a/drivers/serial/bfin_5xx.c b/drivers/serial/bfin_5xx.c index e2f6b1bfac9..b4a7650af69 100644 --- a/drivers/serial/bfin_5xx.c +++ b/drivers/serial/bfin_5xx.c @@ -38,6 +38,10 @@ #include <asm/cacheflush.h> #endif +#ifdef CONFIG_SERIAL_BFIN_MODULE +# undef CONFIG_EARLY_PRINTK +#endif + /* UART name and device definitions */ #define BFIN_SERIAL_NAME "ttyBF" #define BFIN_SERIAL_MAJOR 204 @@ -1110,6 +1114,7 @@ static void __init bfin_serial_init_ports(void) bfin_serial_hw_init(); for (i = 0; i < nr_active_ports; i++) { + spin_lock_init(&bfin_serial_ports[i].port.lock); bfin_serial_ports[i].port.uartclk = get_sclk(); bfin_serial_ports[i].port.fifosize = BFIN_UART_TX_FIFO_SIZE; bfin_serial_ports[i].port.ops = &bfin_serial_pops; diff --git a/drivers/serial/icom.c b/drivers/serial/icom.c index 9f2891c2c4a..cd1b6a45bb8 100644 --- a/drivers/serial/icom.c +++ b/drivers/serial/icom.c @@ -1548,8 +1548,7 @@ static int __devinit icom_probe(struct pci_dev *dev, goto probe_exit1; } - icom_adapter->base_addr = ioremap(icom_adapter->base_addr_pci, - pci_resource_len(dev, 0)); + icom_adapter->base_addr = pci_ioremap_bar(dev, 0); if (!icom_adapter->base_addr) goto probe_exit1; diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c index 107ce2e187b..00f4577d2f7 100644 --- a/drivers/serial/jsm/jsm_tty.c +++ b/drivers/serial/jsm/jsm_tty.c @@ -467,7 +467,7 @@ int __devinit jsm_uart_port_init(struct jsm_board *brd) printk(KERN_INFO "jsm: linemap is full, added device failed\n"); continue; } else - set_bit((int)line, linemap); + set_bit(line, linemap); brd->channels[i]->uart_port.line = line; if (uart_add_one_port (&jsm_uart_driver, &brd->channels[i]->uart_port)) printk(KERN_INFO "jsm: add device failed\n"); @@ -503,7 +503,7 @@ int jsm_remove_uart_port(struct jsm_board *brd) ch = brd->channels[i]; - clear_bit((int)(ch->uart_port.line), linemap); + clear_bit(ch->uart_port.line, linemap); uart_remove_one_port(&jsm_uart_driver, &brd->channels[i]->uart_port); } diff --git a/drivers/serial/msm_serial.c b/drivers/serial/msm_serial.c new file mode 100644 index 00000000000..698048f64f5 --- /dev/null +++ b/drivers/serial/msm_serial.c @@ -0,0 +1,772 @@ +/* + * drivers/serial/msm_serial.c - driver for msm7k serial device and console + * + * Copyright (C) 2007 Google, Inc. + * Author: Robert Love <rlove@google.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. + */ + +#if defined(CONFIG_SERIAL_MSM_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +# define SUPPORT_SYSRQ +#endif + +#include <linux/hrtimer.h> +#include <linux/module.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/console.h> +#include <linux/tty.h> +#include <linux/tty_flip.h> +#include <linux/serial_core.h> +#include <linux/serial.h> +#include <linux/clk.h> +#include <linux/platform_device.h> + +#include "msm_serial.h" + +struct msm_port { + struct uart_port uart; + char name[16]; + struct clk *clk; + unsigned int imr; +}; + +#define UART_TO_MSM(uart_port) ((struct msm_port *) uart_port) + +static inline void msm_write(struct uart_port *port, unsigned int val, + unsigned int off) +{ + __raw_writel(val, port->membase + off); +} + +static inline unsigned int msm_read(struct uart_port *port, unsigned int off) +{ + return __raw_readl(port->membase + off); +} + +static void msm_stop_tx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr &= ~UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); +} + +static void msm_start_tx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr |= UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); +} + +static void msm_stop_rx(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr &= ~(UART_IMR_RXLEV | UART_IMR_RXSTALE); + msm_write(port, msm_port->imr, UART_IMR); +} + +static void msm_enable_ms(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr |= UART_IMR_DELTA_CTS; + msm_write(port, msm_port->imr, UART_IMR); +} + +static void handle_rx(struct uart_port *port) +{ + struct tty_struct *tty = port->info->port.tty; + unsigned int sr; + + /* + * Handle overrun. My understanding of the hardware is that overrun + * is not tied to the RX buffer, so we handle the case out of band. + */ + if ((msm_read(port, UART_SR) & UART_SR_OVERRUN)) { + port->icount.overrun++; + tty_insert_flip_char(tty, 0, TTY_OVERRUN); + msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); + } + + /* and now the main RX loop */ + while ((sr = msm_read(port, UART_SR)) & UART_SR_RX_READY) { + unsigned int c; + char flag = TTY_NORMAL; + + c = msm_read(port, UART_RF); + + if (sr & UART_SR_RX_BREAK) { + port->icount.brk++; + if (uart_handle_break(port)) + continue; + } else if (sr & UART_SR_PAR_FRAME_ERR) { + port->icount.frame++; + } else { + port->icount.rx++; + } + + /* Mask conditions we're ignorning. */ + sr &= port->read_status_mask; + + if (sr & UART_SR_RX_BREAK) { + flag = TTY_BREAK; + } else if (sr & UART_SR_PAR_FRAME_ERR) { + flag = TTY_FRAME; + } + + if (!uart_handle_sysrq_char(port, c)) + tty_insert_flip_char(tty, c, flag); + } + + tty_flip_buffer_push(tty); +} + +static void handle_tx(struct uart_port *port) +{ + struct circ_buf *xmit = &port->info->xmit; + struct msm_port *msm_port = UART_TO_MSM(port); + int sent_tx; + + if (port->x_char) { + msm_write(port, port->x_char, UART_TF); + port->icount.tx++; + port->x_char = 0; + } + + while (msm_read(port, UART_SR) & UART_SR_TX_READY) { + if (uart_circ_empty(xmit)) { + /* disable tx interrupts */ + msm_port->imr &= ~UART_IMR_TXLEV; + msm_write(port, msm_port->imr, UART_IMR); + break; + } + + msm_write(port, xmit->buf[xmit->tail], UART_TF); + + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + port->icount.tx++; + sent_tx = 1; + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +static void handle_delta_cts(struct uart_port *port) +{ + msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); + port->icount.cts++; + wake_up_interruptible(&port->info->delta_msr_wait); +} + +static irqreturn_t msm_irq(int irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int misr; + + spin_lock(&port->lock); + misr = msm_read(port, UART_MISR); + msm_write(port, 0, UART_IMR); /* disable interrupt */ + + if (misr & (UART_IMR_RXLEV | UART_IMR_RXSTALE)) + handle_rx(port); + if (misr & UART_IMR_TXLEV) + handle_tx(port); + if (misr & UART_IMR_DELTA_CTS) + handle_delta_cts(port); + + msm_write(port, msm_port->imr, UART_IMR); /* restore interrupt */ + spin_unlock(&port->lock); + + return IRQ_HANDLED; +} + +static unsigned int msm_tx_empty(struct uart_port *port) +{ + return (msm_read(port, UART_SR) & UART_SR_TX_EMPTY) ? TIOCSER_TEMT : 0; +} + +static unsigned int msm_get_mctrl(struct uart_port *port) +{ + return TIOCM_CAR | TIOCM_CTS | TIOCM_DSR | TIOCM_RTS; +} + +static void msm_set_mctrl(struct uart_port *port, unsigned int mctrl) +{ + unsigned int mr; + + mr = msm_read(port, UART_MR1); + + if (!(mctrl & TIOCM_RTS)) { + mr &= ~UART_MR1_RX_RDY_CTL; + msm_write(port, mr, UART_MR1); + msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR); + } else { + mr |= UART_MR1_RX_RDY_CTL; + msm_write(port, mr, UART_MR1); + } +} + +static void msm_break_ctl(struct uart_port *port, int break_ctl) +{ + if (break_ctl) + msm_write(port, UART_CR_CMD_START_BREAK, UART_CR); + else + msm_write(port, UART_CR_CMD_STOP_BREAK, UART_CR); +} + +static int msm_set_baud_rate(struct uart_port *port, unsigned int baud) +{ + unsigned int baud_code, rxstale, watermark; + + switch (baud) { + case 300: + baud_code = UART_CSR_300; + rxstale = 1; + break; + case 600: + baud_code = UART_CSR_600; + rxstale = 1; + break; + case 1200: + baud_code = UART_CSR_1200; + rxstale = 1; + break; + case 2400: + baud_code = UART_CSR_2400; + rxstale = 1; + break; + case 4800: + baud_code = UART_CSR_4800; + rxstale = 1; + break; + case 9600: + baud_code = UART_CSR_9600; + rxstale = 2; + break; + case 14400: + baud_code = UART_CSR_14400; + rxstale = 3; + break; + case 19200: + baud_code = UART_CSR_19200; + rxstale = 4; + break; + case 28800: + baud_code = UART_CSR_28800; + rxstale = 6; + break; + case 38400: + baud_code = UART_CSR_38400; + rxstale = 8; + break; + case 57600: + baud_code = UART_CSR_57600; + rxstale = 16; + break; + case 115200: + default: + baud_code = UART_CSR_115200; + baud = 115200; + rxstale = 31; + break; + } + + msm_write(port, baud_code, UART_CSR); + + /* RX stale watermark */ + watermark = UART_IPR_STALE_LSB & rxstale; + watermark |= UART_IPR_RXSTALE_LAST; + watermark |= UART_IPR_STALE_TIMEOUT_MSB & (rxstale << 2); + msm_write(port, watermark, UART_IPR); + + /* set RX watermark */ + watermark = (port->fifosize * 3) / 4; + msm_write(port, watermark, UART_RFWR); + + /* set TX watermark */ + msm_write(port, 10, UART_TFWR); + + return baud; +} + +static void msm_reset(struct uart_port *port) +{ + /* reset everything */ + msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); + msm_write(port, UART_CR_CMD_RESET_TX, UART_CR); + msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); + msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); + msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); + msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); +} + +static void msm_init_clock(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + clk_enable(msm_port->clk); + + msm_write(port, 0xC0, UART_MREG); + msm_write(port, 0xB2, UART_NREG); + msm_write(port, 0x7D, UART_DREG); + msm_write(port, 0x1C, UART_MNDREG); +} + +static int msm_startup(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int data, rfr_level; + int ret; + + snprintf(msm_port->name, sizeof(msm_port->name), + "msm_serial%d", port->line); + + ret = request_irq(port->irq, msm_irq, IRQF_TRIGGER_HIGH, + msm_port->name, port); + if (unlikely(ret)) + return ret; + + msm_init_clock(port); + + if (likely(port->fifosize > 12)) + rfr_level = port->fifosize - 12; + else + rfr_level = port->fifosize; + + /* set automatic RFR level */ + data = msm_read(port, UART_MR1); + data &= ~UART_MR1_AUTO_RFR_LEVEL1; + data &= ~UART_MR1_AUTO_RFR_LEVEL0; + data |= UART_MR1_AUTO_RFR_LEVEL1 & (rfr_level << 2); + data |= UART_MR1_AUTO_RFR_LEVEL0 & rfr_level; + msm_write(port, data, UART_MR1); + + /* make sure that RXSTALE count is non-zero */ + data = msm_read(port, UART_IPR); + if (unlikely(!data)) { + data |= UART_IPR_RXSTALE_LAST; + data |= UART_IPR_STALE_LSB; + msm_write(port, data, UART_IPR); + } + + msm_reset(port); + + msm_write(port, 0x05, UART_CR); /* enable TX & RX */ + + /* turn on RX and CTS interrupts */ + msm_port->imr = UART_IMR_RXLEV | UART_IMR_RXSTALE | + UART_IMR_CURRENT_CTS; + msm_write(port, msm_port->imr, UART_IMR); + + return 0; +} + +static void msm_shutdown(struct uart_port *port) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + msm_port->imr = 0; + msm_write(port, 0, UART_IMR); /* disable interrupts */ + + clk_disable(msm_port->clk); + + free_irq(port->irq, port); +} + +static void msm_set_termios(struct uart_port *port, struct ktermios *termios, + struct ktermios *old) +{ + unsigned long flags; + unsigned int baud, mr; + + spin_lock_irqsave(&port->lock, flags); + + /* calculate and set baud rate */ + baud = uart_get_baud_rate(port, termios, old, 300, 115200); + baud = msm_set_baud_rate(port, baud); + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); + + /* calculate parity */ + mr = msm_read(port, UART_MR2); + mr &= ~UART_MR2_PARITY_MODE; + if (termios->c_cflag & PARENB) { + if (termios->c_cflag & PARODD) + mr |= UART_MR2_PARITY_MODE_ODD; + else if (termios->c_cflag & CMSPAR) + mr |= UART_MR2_PARITY_MODE_SPACE; + else + mr |= UART_MR2_PARITY_MODE_EVEN; + } + + /* calculate bits per char */ + mr &= ~UART_MR2_BITS_PER_CHAR; + switch (termios->c_cflag & CSIZE) { + case CS5: + mr |= UART_MR2_BITS_PER_CHAR_5; + break; + case CS6: + mr |= UART_MR2_BITS_PER_CHAR_6; + break; + case CS7: + mr |= UART_MR2_BITS_PER_CHAR_7; + break; + case CS8: + default: + mr |= UART_MR2_BITS_PER_CHAR_8; + break; + } + + /* calculate stop bits */ + mr &= ~(UART_MR2_STOP_BIT_LEN_ONE | UART_MR2_STOP_BIT_LEN_TWO); + if (termios->c_cflag & CSTOPB) + mr |= UART_MR2_STOP_BIT_LEN_TWO; + else + mr |= UART_MR2_STOP_BIT_LEN_ONE; + + /* set parity, bits per char, and stop bit */ + msm_write(port, mr, UART_MR2); + + /* calculate and set hardware flow control */ + mr = msm_read(port, UART_MR1); + mr &= ~(UART_MR1_CTS_CTL | UART_MR1_RX_RDY_CTL); + if (termios->c_cflag & CRTSCTS) { + mr |= UART_MR1_CTS_CTL; + mr |= UART_MR1_RX_RDY_CTL; + } + msm_write(port, mr, UART_MR1); + + /* Configure status bits to ignore based on termio flags. */ + port->read_status_mask = 0; + if (termios->c_iflag & INPCK) + port->read_status_mask |= UART_SR_PAR_FRAME_ERR; + if (termios->c_iflag & (BRKINT | PARMRK)) + port->read_status_mask |= UART_SR_RX_BREAK; + + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static const char *msm_type(struct uart_port *port) +{ + return "MSM"; +} + +static void msm_release_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *resource; + resource_size_t size; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return; + size = resource->end - resource->start + 1; + + release_mem_region(port->mapbase, size); + iounmap(port->membase); + port->membase = NULL; +} + +static int msm_request_port(struct uart_port *port) +{ + struct platform_device *pdev = to_platform_device(port->dev); + struct resource *resource; + resource_size_t size; + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return -ENXIO; + size = resource->end - resource->start + 1; + + if (unlikely(!request_mem_region(port->mapbase, size, "msm_serial"))) + return -EBUSY; + + port->membase = ioremap(port->mapbase, size); + if (!port->membase) { + release_mem_region(port->mapbase, size); + return -EBUSY; + } + + return 0; +} + +static void msm_config_port(struct uart_port *port, int flags) +{ + if (flags & UART_CONFIG_TYPE) { + port->type = PORT_MSM; + msm_request_port(port); + } +} + +static int msm_verify_port(struct uart_port *port, struct serial_struct *ser) +{ + if (unlikely(ser->type != PORT_UNKNOWN && ser->type != PORT_MSM)) + return -EINVAL; + if (unlikely(port->irq != ser->irq)) + return -EINVAL; + return 0; +} + +static void msm_power(struct uart_port *port, unsigned int state, + unsigned int oldstate) +{ + struct msm_port *msm_port = UART_TO_MSM(port); + + switch (state) { + case 0: + clk_enable(msm_port->clk); + break; + case 3: + clk_disable(msm_port->clk); + break; + default: + printk(KERN_ERR "msm_serial: Unknown PM state %d\n", state); + } +} + +static struct uart_ops msm_uart_pops = { + .tx_empty = msm_tx_empty, + .set_mctrl = msm_set_mctrl, + .get_mctrl = msm_get_mctrl, + .stop_tx = msm_stop_tx, + .start_tx = msm_start_tx, + .stop_rx = msm_stop_rx, + .enable_ms = msm_enable_ms, + .break_ctl = msm_break_ctl, + .startup = msm_startup, + .shutdown = msm_shutdown, + .set_termios = msm_set_termios, + .type = msm_type, + .release_port = msm_release_port, + .request_port = msm_request_port, + .config_port = msm_config_port, + .verify_port = msm_verify_port, + .pm = msm_power, +}; + +static struct msm_port msm_uart_ports[] = { + { + .uart = { + .iotype = UPIO_MEM, + .ops = &msm_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .fifosize = 512, + .line = 0, + }, + }, + { + .uart = { + .iotype = UPIO_MEM, + .ops = &msm_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .fifosize = 512, + .line = 1, + }, + }, + { + .uart = { + .iotype = UPIO_MEM, + .ops = &msm_uart_pops, + .flags = UPF_BOOT_AUTOCONF, + .fifosize = 64, + .line = 2, + }, + }, +}; + +#define UART_NR ARRAY_SIZE(msm_uart_ports) + +static inline struct uart_port *get_port_from_line(unsigned int line) +{ + return &msm_uart_ports[line].uart; +} + +#ifdef CONFIG_SERIAL_MSM_CONSOLE + +static void msm_console_putchar(struct uart_port *port, int c) +{ + while (!(msm_read(port, UART_SR) & UART_SR_TX_READY)) + ; + msm_write(port, c, UART_TF); +} + +static void msm_console_write(struct console *co, const char *s, + unsigned int count) +{ + struct uart_port *port; + struct msm_port *msm_port; + + BUG_ON(co->index < 0 || co->index >= UART_NR); + + port = get_port_from_line(co->index); + msm_port = UART_TO_MSM(port); + + spin_lock(&port->lock); + uart_console_write(port, s, count, msm_console_putchar); + spin_unlock(&port->lock); +} + +static int __init msm_console_setup(struct console *co, char *options) +{ + struct uart_port *port; + int baud, flow, bits, parity; + + if (unlikely(co->index >= UART_NR || co->index < 0)) + return -ENXIO; + + port = get_port_from_line(co->index); + + if (unlikely(!port->membase)) + return -ENXIO; + + port->cons = co; + + msm_init_clock(port); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); + + bits = 8; + parity = 'n'; + flow = 'n'; + msm_write(port, UART_MR2_BITS_PER_CHAR_8 | UART_MR2_STOP_BIT_LEN_ONE, + UART_MR2); /* 8N1 */ + + if (baud < 300 || baud > 115200) + baud = 115200; + msm_set_baud_rate(port, baud); + + msm_reset(port); + + printk(KERN_INFO "msm_serial: console setup on port #%d\n", port->line); + + return uart_set_options(port, co, baud, parity, bits, flow); +} + +static struct uart_driver msm_uart_driver; + +static struct console msm_console = { + .name = "ttyMSM", + .write = msm_console_write, + .device = uart_console_device, + .setup = msm_console_setup, + .flags = CON_PRINTBUFFER, + .index = -1, + .data = &msm_uart_driver, +}; + +#define MSM_CONSOLE (&msm_console) + +#else +#define MSM_CONSOLE NULL +#endif + +static struct uart_driver msm_uart_driver = { + .owner = THIS_MODULE, + .driver_name = "msm_serial", + .dev_name = "ttyMSM", + .nr = UART_NR, + .cons = MSM_CONSOLE, +}; + +static int __init msm_serial_probe(struct platform_device *pdev) +{ + struct msm_port *msm_port; + struct resource *resource; + struct uart_port *port; + + if (unlikely(pdev->id < 0 || pdev->id >= UART_NR)) + return -ENXIO; + + printk(KERN_INFO "msm_serial: detected port #%d\n", pdev->id); + + port = get_port_from_line(pdev->id); + port->dev = &pdev->dev; + msm_port = UART_TO_MSM(port); + + msm_port->clk = clk_get(&pdev->dev, "uart_clk"); + if (unlikely(IS_ERR(msm_port->clk))) + return PTR_ERR(msm_port->clk); + port->uartclk = clk_get_rate(msm_port->clk); + + resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!resource)) + return -ENXIO; + port->mapbase = resource->start; + + port->irq = platform_get_irq(pdev, 0); + if (unlikely(port->irq < 0)) + return -ENXIO; + + platform_set_drvdata(pdev, port); + + return uart_add_one_port(&msm_uart_driver, port); +} + +static int __devexit msm_serial_remove(struct platform_device *pdev) +{ + struct msm_port *msm_port = platform_get_drvdata(pdev); + + clk_put(msm_port->clk); + + return 0; +} + +static struct platform_driver msm_platform_driver = { + .probe = msm_serial_probe, + .remove = msm_serial_remove, + .driver = { + .name = "msm_serial", + .owner = THIS_MODULE, + }, +}; + +static int __init msm_serial_init(void) +{ + int ret; + + ret = uart_register_driver(&msm_uart_driver); + if (unlikely(ret)) + return ret; + + ret = platform_driver_probe(&msm_platform_driver, msm_serial_probe); + if (unlikely(ret)) + uart_unregister_driver(&msm_uart_driver); + + printk(KERN_INFO "msm_serial: driver initialized\n"); + + return ret; +} + +static void __exit msm_serial_exit(void) +{ +#ifdef CONFIG_SERIAL_MSM_CONSOLE + unregister_console(&msm_console); +#endif + platform_driver_unregister(&msm_platform_driver); + uart_unregister_driver(&msm_uart_driver); +} + +module_init(msm_serial_init); +module_exit(msm_serial_exit); + +MODULE_AUTHOR("Robert Love <rlove@google.com>"); +MODULE_DESCRIPTION("Driver for msm7x serial device"); +MODULE_LICENSE("GPL"); diff --git a/drivers/serial/msm_serial.h b/drivers/serial/msm_serial.h new file mode 100644 index 00000000000..689f1fa0e84 --- /dev/null +++ b/drivers/serial/msm_serial.h @@ -0,0 +1,117 @@ +/* + * drivers/serial/msm_serial.h + * + * Copyright (C) 2007 Google, Inc. + * Author: Robert Love <rlove@google.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. + */ + +#ifndef __DRIVERS_SERIAL_MSM_SERIAL_H +#define __DRIVERS_SERIAL_MSM_SERIAL_H + +#define UART_MR1 0x0000 + +#define UART_MR1_AUTO_RFR_LEVEL0 0x3F +#define UART_MR1_AUTO_RFR_LEVEL1 0x3FF00 +#define UART_MR1_RX_RDY_CTL (1 << 7) +#define UART_MR1_CTS_CTL (1 << 6) + +#define UART_MR2 0x0004 +#define UART_MR2_ERROR_MODE (1 << 6) +#define UART_MR2_BITS_PER_CHAR 0x30 +#define UART_MR2_BITS_PER_CHAR_5 (0x0 << 4) +#define UART_MR2_BITS_PER_CHAR_6 (0x1 << 4) +#define UART_MR2_BITS_PER_CHAR_7 (0x2 << 4) +#define UART_MR2_BITS_PER_CHAR_8 (0x3 << 4) +#define UART_MR2_STOP_BIT_LEN_ONE (0x1 << 2) +#define UART_MR2_STOP_BIT_LEN_TWO (0x3 << 2) +#define UART_MR2_PARITY_MODE_NONE 0x0 +#define UART_MR2_PARITY_MODE_ODD 0x1 +#define UART_MR2_PARITY_MODE_EVEN 0x2 +#define UART_MR2_PARITY_MODE_SPACE 0x3 +#define UART_MR2_PARITY_MODE 0x3 + +#define UART_CSR 0x0008 +#define UART_CSR_115200 0xFF +#define UART_CSR_57600 0xEE +#define UART_CSR_38400 0xDD +#define UART_CSR_28800 0xCC +#define UART_CSR_19200 0xBB +#define UART_CSR_14400 0xAA +#define UART_CSR_9600 0x99 +#define UART_CSR_4800 0x77 +#define UART_CSR_2400 0x55 +#define UART_CSR_1200 0x44 +#define UART_CSR_600 0x33 +#define UART_CSR_300 0x22 + +#define UART_TF 0x000C + +#define UART_CR 0x0010 +#define UART_CR_CMD_NULL (0 << 4) +#define UART_CR_CMD_RESET_RX (1 << 4) +#define UART_CR_CMD_RESET_TX (2 << 4) +#define UART_CR_CMD_RESET_ERR (3 << 4) +#define UART_CR_CMD_RESET_BREAK_INT (4 << 4) +#define UART_CR_CMD_START_BREAK (5 << 4) +#define UART_CR_CMD_STOP_BREAK (6 << 4) +#define UART_CR_CMD_RESET_CTS (7 << 4) +#define UART_CR_CMD_PACKET_MODE (9 << 4) +#define UART_CR_CMD_MODE_RESET (12 << 4) +#define UART_CR_CMD_SET_RFR (13 << 4) +#define UART_CR_CMD_RESET_RFR (14 << 4) +#define UART_CR_TX_DISABLE (1 << 3) +#define UART_CR_TX_ENABLE (1 << 3) +#define UART_CR_RX_DISABLE (1 << 3) +#define UART_CR_RX_ENABLE (1 << 3) + +#define UART_IMR 0x0014 +#define UART_IMR_TXLEV (1 << 0) +#define UART_IMR_RXSTALE (1 << 3) +#define UART_IMR_RXLEV (1 << 4) +#define UART_IMR_DELTA_CTS (1 << 5) +#define UART_IMR_CURRENT_CTS (1 << 6) + +#define UART_IPR_RXSTALE_LAST 0x20 +#define UART_IPR_STALE_LSB 0x1F +#define UART_IPR_STALE_TIMEOUT_MSB 0x3FF80 + +#define UART_IPR 0x0018 +#define UART_TFWR 0x001C +#define UART_RFWR 0x0020 +#define UART_HCR 0x0024 + +#define UART_MREG 0x0028 +#define UART_NREG 0x002C +#define UART_DREG 0x0030 +#define UART_MNDREG 0x0034 +#define UART_IRDA 0x0038 +#define UART_MISR_MODE 0x0040 +#define UART_MISR_RESET 0x0044 +#define UART_MISR_EXPORT 0x0048 +#define UART_MISR_VAL 0x004C +#define UART_TEST_CTRL 0x0050 + +#define UART_SR 0x0008 +#define UART_SR_HUNT_CHAR (1 << 7) +#define UART_SR_RX_BREAK (1 << 6) +#define UART_SR_PAR_FRAME_ERR (1 << 5) +#define UART_SR_OVERRUN (1 << 4) +#define UART_SR_TX_EMPTY (1 << 3) +#define UART_SR_TX_READY (1 << 2) +#define UART_SR_RX_FULL (1 << 1) +#define UART_SR_RX_READY (1 << 0) + +#define UART_RF 0x000C +#define UART_MISR 0x0010 +#define UART_ISR 0x0014 + +#endif /* __DRIVERS_SERIAL_MSM_SERIAL_H */ diff --git a/drivers/serial/s3c2400.c b/drivers/serial/s3c2400.c index 4873f2978bd..fb00ed5296e 100644 --- a/drivers/serial/s3c2400.c +++ b/drivers/serial/s3c2400.c @@ -78,7 +78,7 @@ static int s3c2400_serial_probe(struct platform_device *dev) static struct platform_driver s3c2400_serial_drv = { .probe = s3c2400_serial_probe, - .remove = s3c24xx_serial_remove, + .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c2400-uart", .owner = THIS_MODULE, diff --git a/drivers/serial/s3c2410.c b/drivers/serial/s3c2410.c index 87c182ef71b..b5d7cbcba2a 100644 --- a/drivers/serial/s3c2410.c +++ b/drivers/serial/s3c2410.c @@ -90,7 +90,7 @@ static int s3c2410_serial_probe(struct platform_device *dev) static struct platform_driver s3c2410_serial_drv = { .probe = s3c2410_serial_probe, - .remove = s3c24xx_serial_remove, + .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c2410-uart", .owner = THIS_MODULE, diff --git a/drivers/serial/s3c2412.c b/drivers/serial/s3c2412.c index fd017b37556..11dcb90bdfe 100644 --- a/drivers/serial/s3c2412.c +++ b/drivers/serial/s3c2412.c @@ -123,7 +123,7 @@ static int s3c2412_serial_probe(struct platform_device *dev) static struct platform_driver s3c2412_serial_drv = { .probe = s3c2412_serial_probe, - .remove = s3c24xx_serial_remove, + .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c2412-uart", .owner = THIS_MODULE, diff --git a/drivers/serial/s3c2440.c b/drivers/serial/s3c2440.c index 29cbb0afef8..06c5b0cc47a 100644 --- a/drivers/serial/s3c2440.c +++ b/drivers/serial/s3c2440.c @@ -153,7 +153,7 @@ static int s3c2440_serial_probe(struct platform_device *dev) static struct platform_driver s3c2440_serial_drv = { .probe = s3c2440_serial_probe, - .remove = s3c24xx_serial_remove, + .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c2440-uart", .owner = THIS_MODULE, diff --git a/drivers/serial/s3c24a0.c b/drivers/serial/s3c24a0.c index ebf2fd3c8f7..786a067d62a 100644 --- a/drivers/serial/s3c24a0.c +++ b/drivers/serial/s3c24a0.c @@ -94,7 +94,7 @@ static int s3c24a0_serial_probe(struct platform_device *dev) static struct platform_driver s3c24a0_serial_drv = { .probe = s3c24a0_serial_probe, - .remove = s3c24xx_serial_remove, + .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c24a0-uart", .owner = THIS_MODULE, diff --git a/drivers/serial/s3c6400.c b/drivers/serial/s3c6400.c index 3e378523368..48f1a3781f0 100644 --- a/drivers/serial/s3c6400.c +++ b/drivers/serial/s3c6400.c @@ -124,7 +124,7 @@ static int s3c6400_serial_probe(struct platform_device *dev) static struct platform_driver s3c6400_serial_drv = { .probe = s3c6400_serial_probe, - .remove = s3c24xx_serial_remove, + .remove = __devexit_p(s3c24xx_serial_remove), .driver = { .name = "s3c6400-uart", .owner = THIS_MODULE, diff --git a/drivers/serial/samsung.c b/drivers/serial/samsung.c index 93b5d75db12..c8851a0db63 100644 --- a/drivers/serial/samsung.c +++ b/drivers/serial/samsung.c @@ -1174,7 +1174,7 @@ int s3c24xx_serial_probe(struct platform_device *dev, EXPORT_SYMBOL_GPL(s3c24xx_serial_probe); -int s3c24xx_serial_remove(struct platform_device *dev) +int __devexit s3c24xx_serial_remove(struct platform_device *dev) { struct uart_port *port = s3c24xx_dev_to_port(&dev->dev); diff --git a/drivers/serial/samsung.h b/drivers/serial/samsung.h index 7afb94843a0..d3fe315969f 100644 --- a/drivers/serial/samsung.h +++ b/drivers/serial/samsung.h @@ -72,7 +72,7 @@ struct s3c24xx_uart_port { extern int s3c24xx_serial_probe(struct platform_device *dev, struct s3c24xx_uart_info *uart); -extern int s3c24xx_serial_remove(struct platform_device *dev); +extern int __devexit s3c24xx_serial_remove(struct platform_device *dev); extern int s3c24xx_serial_initconsole(struct platform_driver *drv, struct s3c24xx_uart_info *uart); diff --git a/drivers/serial/sb1250-duart.c b/drivers/serial/sb1250-duart.c index a4fb343a08d..319e8b83f6b 100644 --- a/drivers/serial/sb1250-duart.c +++ b/drivers/serial/sb1250-duart.c @@ -204,7 +204,7 @@ static int sbd_receive_drain(struct sbd_port *sport) { int loops = 10000; - while (sbd_receive_ready(sport) && loops--) + while (sbd_receive_ready(sport) && --loops) read_sbdchn(sport, R_DUART_RX_HOLD); return loops; } @@ -218,7 +218,7 @@ static int __maybe_unused sbd_transmit_drain(struct sbd_port *sport) { int loops = 10000; - while (!sbd_transmit_ready(sport) && loops--) + while (!sbd_transmit_ready(sport) && --loops) udelay(2); return loops; } @@ -232,7 +232,7 @@ static int sbd_line_drain(struct sbd_port *sport) { int loops = 10000; - while (!sbd_transmit_empty(sport) && loops--) + while (!sbd_transmit_empty(sport) && --loops) udelay(2); return loops; } diff --git a/drivers/serial/serial_txx9.c b/drivers/serial/serial_txx9.c index 7313c2edcb8..54dd16d66a4 100644 --- a/drivers/serial/serial_txx9.c +++ b/drivers/serial/serial_txx9.c @@ -461,6 +461,94 @@ static void serial_txx9_break_ctl(struct uart_port *port, int break_state) spin_unlock_irqrestore(&up->port.lock, flags); } +#if defined(CONFIG_SERIAL_TXX9_CONSOLE) || (CONFIG_CONSOLE_POLL) +/* + * Wait for transmitter & holding register to empty + */ +static void wait_for_xmitr(struct uart_txx9_port *up) +{ + unsigned int tmout = 10000; + + /* Wait up to 10ms for the character(s) to be sent. */ + while (--tmout && + !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS)) + udelay(1); + + /* Wait up to 1s for flow control if necessary */ + if (up->port.flags & UPF_CONS_FLOW) { + tmout = 1000000; + while (--tmout && + (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) + udelay(1); + } +} +#endif + +#ifdef CONFIG_CONSOLE_POLL +/* + * Console polling routines for writing and reading from the uart while + * in an interrupt or debug context. + */ + +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; + + /* + * First save the IER then disable the interrupts + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + + while (sio_in(up, TXX9_SIDISR) & TXX9_SIDISR_UVALID) + ; + + c = sio_in(up, TXX9_SIRFIFO); + + /* + * Finally, clear RX interrupt status + * and restore the IER + */ + sio_mask(up, TXX9_SIDISR, TXX9_SIDISR_RDIS); + sio_out(up, TXX9_SIDICR, ier); + return c; +} + + +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; + + /* + * First save the IER then disable the interrupts + */ + ier = sio_in(up, TXX9_SIDICR); + sio_out(up, TXX9_SIDICR, 0); + + wait_for_xmitr(up); + /* + * Send the character out. + * If a LF, also do CR... + */ + sio_out(up, TXX9_SITFIFO, c); + if (c == 10) { + wait_for_xmitr(up); + sio_out(up, TXX9_SITFIFO, 13); + } + + /* + * Finally, wait for transmitter to become empty + * and restore the IER + */ + wait_for_xmitr(up); + sio_out(up, TXX9_SIDICR, ier); +} + +#endif /* CONFIG_CONSOLE_POLL */ + static int serial_txx9_startup(struct uart_port *port) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; @@ -781,6 +869,10 @@ static struct uart_ops serial_txx9_pops = { .release_port = serial_txx9_release_port, .request_port = serial_txx9_request_port, .config_port = serial_txx9_config_port, +#ifdef CONFIG_CONSOLE_POLL + .poll_get_char = serial_txx9_get_poll_char, + .poll_put_char = serial_txx9_put_poll_char, +#endif }; static struct uart_txx9_port serial_txx9_ports[UART_NR]; @@ -803,27 +895,6 @@ static void __init serial_txx9_register_ports(struct uart_driver *drv, #ifdef CONFIG_SERIAL_TXX9_CONSOLE -/* - * Wait for transmitter & holding register to empty - */ -static inline void wait_for_xmitr(struct uart_txx9_port *up) -{ - unsigned int tmout = 10000; - - /* Wait up to 10ms for the character(s) to be sent. */ - while (--tmout && - !(sio_in(up, TXX9_SICISR) & TXX9_SICISR_TXALS)) - udelay(1); - - /* Wait up to 1s for flow control if necessary */ - if (up->port.flags & UPF_CONS_FLOW) { - tmout = 1000000; - while (--tmout && - (sio_in(up, TXX9_SICISR) & TXX9_SICISR_CTSS)) - udelay(1); - } -} - static void serial_txx9_console_putchar(struct uart_port *port, int ch) { struct uart_txx9_port *up = (struct uart_txx9_port *)port; diff --git a/drivers/serial/sunhv.c b/drivers/serial/sunhv.c index a94a2ab4b57..1df5325faab 100644 --- a/drivers/serial/sunhv.c +++ b/drivers/serial/sunhv.c @@ -461,7 +461,7 @@ static void sunhv_console_write_paged(struct console *con, const char *s, unsign break; udelay(1); } - if (limit <= 0) + if (limit < 0) break; page_bytes -= written; ra += written; diff --git a/drivers/serial/timbuart.c b/drivers/serial/timbuart.c index ac9e5d5f742..063a313b755 100644 --- a/drivers/serial/timbuart.c +++ b/drivers/serial/timbuart.c @@ -33,29 +33,29 @@ struct timbuart_port { struct uart_port port; struct tasklet_struct tasklet; int usedma; - u8 last_ier; + u32 last_ier; struct platform_device *dev; }; static int baudrates[] = {9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600, 1843200, 3250000}; -static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier); +static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier); static irqreturn_t timbuart_handleinterrupt(int irq, void *devid); static void timbuart_stop_rx(struct uart_port *port) { /* spin lock held by upper layer, disable all RX interrupts */ - u8 ier = ioread8(port->membase + TIMBUART_IER) & ~RXFLAGS; - iowrite8(ier, port->membase + TIMBUART_IER); + u32 ier = ioread32(port->membase + TIMBUART_IER) & ~RXFLAGS; + iowrite32(ier, port->membase + TIMBUART_IER); } static void timbuart_stop_tx(struct uart_port *port) { /* spinlock held by upper layer, disable TX interrupt */ - u8 ier = ioread8(port->membase + TIMBUART_IER) & ~TXBAE; - iowrite8(ier, port->membase + TIMBUART_IER); + u32 ier = ioread32(port->membase + TIMBUART_IER) & ~TXBAE; + iowrite32(ier, port->membase + TIMBUART_IER); } static void timbuart_start_tx(struct uart_port *port) @@ -72,14 +72,14 @@ static void timbuart_flush_buffer(struct uart_port *port) u8 ctl = ioread8(port->membase + TIMBUART_CTRL) | TIMBUART_CTRL_FLSHTX; iowrite8(ctl, port->membase + TIMBUART_CTRL); - iowrite8(TXBF, port->membase + TIMBUART_ISR); + iowrite32(TXBF, port->membase + TIMBUART_ISR); } static void timbuart_rx_chars(struct uart_port *port) { struct tty_struct *tty = port->info->port.tty; - while (ioread8(port->membase + TIMBUART_ISR) & RXDP) { + while (ioread32(port->membase + TIMBUART_ISR) & RXDP) { u8 ch = ioread8(port->membase + TIMBUART_RXFIFO); port->icount.rx++; tty_insert_flip_char(tty, ch, TTY_NORMAL); @@ -97,7 +97,7 @@ static void timbuart_tx_chars(struct uart_port *port) { struct circ_buf *xmit = &port->info->xmit; - while (!(ioread8(port->membase + TIMBUART_ISR) & TXBF) && + while (!(ioread32(port->membase + TIMBUART_ISR) & TXBF) && !uart_circ_empty(xmit)) { iowrite8(xmit->buf[xmit->tail], port->membase + TIMBUART_TXFIFO); @@ -114,7 +114,7 @@ static void timbuart_tx_chars(struct uart_port *port) ioread8(port->membase + TIMBUART_BAUDRATE)); } -static void timbuart_handle_tx_port(struct uart_port *port, u8 isr, u8 *ier) +static void timbuart_handle_tx_port(struct uart_port *port, u32 isr, u32 *ier) { struct timbuart_port *uart = container_of(port, struct timbuart_port, port); @@ -129,7 +129,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u8 isr, u8 *ier) if (isr & TXFLAGS) { timbuart_tx_chars(port); /* clear all TX interrupts */ - iowrite8(TXFLAGS, port->membase + TIMBUART_ISR); + iowrite32(TXFLAGS, port->membase + TIMBUART_ISR); if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(port); @@ -148,7 +148,7 @@ static void timbuart_handle_tx_port(struct uart_port *port, u8 isr, u8 *ier) dev_dbg(port->dev, "%s - leaving\n", __func__); } -void timbuart_handle_rx_port(struct uart_port *port, u8 isr, u8 *ier) +void timbuart_handle_rx_port(struct uart_port *port, u32 isr, u32 *ier) { if (isr & RXFLAGS) { /* Some RX status is set */ @@ -161,7 +161,7 @@ void timbuart_handle_rx_port(struct uart_port *port, u8 isr, u8 *ier) timbuart_rx_chars(port); /* ack all RX interrupts */ - iowrite8(RXFLAGS, port->membase + TIMBUART_ISR); + iowrite32(RXFLAGS, port->membase + TIMBUART_ISR); } /* always have the RX interrupts enabled */ @@ -173,11 +173,11 @@ void timbuart_handle_rx_port(struct uart_port *port, u8 isr, u8 *ier) void timbuart_tasklet(unsigned long arg) { struct timbuart_port *uart = (struct timbuart_port *)arg; - u8 isr, ier = 0; + u32 isr, ier = 0; spin_lock(&uart->port.lock); - isr = ioread8(uart->port.membase + TIMBUART_ISR); + isr = ioread32(uart->port.membase + TIMBUART_ISR); dev_dbg(uart->port.dev, "%s ISR: %x\n", __func__, isr); if (!uart->usedma) @@ -188,7 +188,7 @@ void timbuart_tasklet(unsigned long arg) if (!uart->usedma) timbuart_handle_rx_port(&uart->port, isr, &ier); - iowrite8(ier, uart->port.membase + TIMBUART_IER); + iowrite32(ier, uart->port.membase + TIMBUART_IER); spin_unlock(&uart->port.lock); dev_dbg(uart->port.dev, "%s leaving\n", __func__); @@ -196,9 +196,9 @@ void timbuart_tasklet(unsigned long arg) static unsigned int timbuart_tx_empty(struct uart_port *port) { - u8 isr = ioread8(port->membase + TIMBUART_ISR); + u32 isr = ioread32(port->membase + TIMBUART_ISR); - return (isr & TXBAE) ? TIOCSER_TEMT : 0; + return (isr & TXBE) ? TIOCSER_TEMT : 0; } static unsigned int timbuart_get_mctrl(struct uart_port *port) @@ -222,13 +222,13 @@ static void timbuart_set_mctrl(struct uart_port *port, unsigned int mctrl) iowrite8(TIMBUART_CTRL_RTS, port->membase + TIMBUART_CTRL); } -static void timbuart_mctrl_check(struct uart_port *port, u8 isr, u8 *ier) +static void timbuart_mctrl_check(struct uart_port *port, u32 isr, u32 *ier) { unsigned int cts; if (isr & CTS_DELTA) { /* ack */ - iowrite8(CTS_DELTA, port->membase + TIMBUART_ISR); + iowrite32(CTS_DELTA, port->membase + TIMBUART_ISR); cts = timbuart_get_mctrl(port); uart_handle_cts_change(port, cts & TIOCM_CTS); wake_up_interruptible(&port->info->delta_msr_wait); @@ -255,9 +255,9 @@ static int timbuart_startup(struct uart_port *port) dev_dbg(port->dev, "%s\n", __func__); iowrite8(TIMBUART_CTRL_FLSHRX, port->membase + TIMBUART_CTRL); - iowrite8(0xff, port->membase + TIMBUART_ISR); + iowrite32(0x1ff, port->membase + TIMBUART_ISR); /* Enable all but TX interrupts */ - iowrite8(RXBAF | RXBF | RXTT | CTS_DELTA, + iowrite32(RXBAF | RXBF | RXTT | CTS_DELTA, port->membase + TIMBUART_IER); return request_irq(port->irq, timbuart_handleinterrupt, IRQF_SHARED, @@ -270,7 +270,7 @@ static void timbuart_shutdown(struct uart_port *port) container_of(port, struct timbuart_port, port); dev_dbg(port->dev, "%s\n", __func__); free_irq(port->irq, uart); - iowrite8(0, port->membase + TIMBUART_IER); + iowrite32(0, port->membase + TIMBUART_IER); } static int get_bindex(int baud) @@ -359,10 +359,10 @@ static irqreturn_t timbuart_handleinterrupt(int irq, void *devid) struct timbuart_port *uart = (struct timbuart_port *)devid; if (ioread8(uart->port.membase + TIMBUART_IPR)) { - uart->last_ier = ioread8(uart->port.membase + TIMBUART_IER); + uart->last_ier = ioread32(uart->port.membase + TIMBUART_IER); /* disable interrupts, the tasklet enables them again */ - iowrite8(0, uart->port.membase + TIMBUART_IER); + iowrite32(0, uart->port.membase + TIMBUART_IER); /* fire off bottom half */ tasklet_schedule(&uart->tasklet); diff --git a/drivers/serial/zs.c b/drivers/serial/zs.c index 9e6a873f820..d8c2809b1ab 100644 --- a/drivers/serial/zs.c +++ b/drivers/serial/zs.c @@ -231,7 +231,7 @@ static int zs_receive_drain(struct zs_port *zport) { int loops = 10000; - while ((read_zsreg(zport, R0) & Rx_CH_AV) && loops--) + while ((read_zsreg(zport, R0) & Rx_CH_AV) && --loops) read_zsdata(zport); return loops; } @@ -241,7 +241,7 @@ static int zs_transmit_drain(struct zs_port *zport, int irq) struct zs_scc *scc = zport->scc; int loops = 10000; - while (!(read_zsreg(zport, R0) & Tx_BUF_EMP) && loops--) { + while (!(read_zsreg(zport, R0) & Tx_BUF_EMP) && --loops) { zs_spin_unlock_cond_irq(&scc->zlock, irq); udelay(2); zs_spin_lock_cond_irq(&scc->zlock, irq); @@ -254,7 +254,7 @@ static int zs_line_drain(struct zs_port *zport, int irq) struct zs_scc *scc = zport->scc; int loops = 10000; - while (!(read_zsreg(zport, R1) & ALL_SNT) && loops--) { + while (!(read_zsreg(zport, R1) & ALL_SNT) && --loops) { zs_spin_unlock_cond_irq(&scc->zlock, irq); udelay(2); zs_spin_lock_cond_irq(&scc->zlock, irq); diff --git a/drivers/staging/serqt_usb2/serqt_usb2.c b/drivers/staging/serqt_usb2/serqt_usb2.c index 581232b719f..90b29b56463 100644 --- a/drivers/staging/serqt_usb2/serqt_usb2.c +++ b/drivers/staging/serqt_usb2/serqt_usb2.c @@ -284,21 +284,12 @@ static void ProcessModemStatus(struct quatech_port *qt_port, return; } -static void ProcessRxChar(struct usb_serial_port *port, unsigned char Data) +static void ProcessRxChar(struct tty_struct *tty, struct usb_serial_port *port, + unsigned char data) { - struct tty_struct *tty; struct urb *urb = port->read_urb; - tty = tty_port_tty_get(&port->port); - - /* if we insert more than TTY_FLIPBUF_SIZE characters, we drop them. */ - - if (tty && urb->actual_length) { - tty_buffer_request_room(tty, 1); - tty_insert_flip_string(tty, &Data, 1); - /* tty_flip_buffer_push(tty); */ - } - - return; + if (urb->actual_length) + tty_insert_flip_char(tty, data, TTY_NORMAL); } static void qt_write_bulk_callback(struct urb *urb) @@ -435,8 +426,10 @@ static void qt_read_bulk_callback(struct urb *urb) case 0xff: dbg("No status sequence. \n"); - ProcessRxChar(port, data[i]); - ProcessRxChar(port, data[i + 1]); + if (tty) { + ProcessRxChar(tty, port, data[i]); + ProcessRxChar(tty, port, data[i + 1]); + } i += 2; break; } @@ -444,10 +437,8 @@ static void qt_read_bulk_callback(struct urb *urb) continue; } - if (tty && urb->actual_length) { - tty_buffer_request_room(tty, 1); - tty_insert_flip_string(tty, (data + i), 1); - } + if (tty && urb->actual_length) + tty_insert_flip_char(tty, data[i], TTY_NORMAL); } tty_flip_buffer_push(tty); diff --git a/drivers/uwb/hwa-rc.c b/drivers/uwb/hwa-rc.c index 559f8784acf..9052bcb4f52 100644 --- a/drivers/uwb/hwa-rc.c +++ b/drivers/uwb/hwa-rc.c @@ -501,7 +501,7 @@ int hwarc_filter_event_WUSB_0100(struct uwb_rc *rc, struct uwb_rceb **header, int result = -ENOANO; struct uwb_rceb *rceb = *header; int event = le16_to_cpu(rceb->wEvent); - size_t event_size; + ssize_t event_size; size_t core_size, offset; if (rceb->bEventType != UWB_RC_CET_GENERAL) diff --git a/drivers/uwb/wlp/txrx.c b/drivers/uwb/wlp/txrx.c index cd2035768b4..86a853b8411 100644 --- a/drivers/uwb/wlp/txrx.c +++ b/drivers/uwb/wlp/txrx.c @@ -326,7 +326,7 @@ int wlp_prepare_tx_frame(struct device *dev, struct wlp *wlp, int result = -EINVAL; struct ethhdr *eth_hdr = (void *) skb->data; - if (is_broadcast_ether_addr(eth_hdr->h_dest)) { + if (is_multicast_ether_addr(eth_hdr->h_dest)) { result = wlp_eda_for_each(&wlp->eda, wlp_wss_send_copy, skb); if (result < 0) { if (printk_ratelimit()) diff --git a/drivers/video/nvidia/nv_setup.c b/drivers/video/nvidia/nv_setup.c index 135ae18bfce..eef2bb298d9 100644 --- a/drivers/video/nvidia/nv_setup.c +++ b/drivers/video/nvidia/nv_setup.c @@ -543,8 +543,7 @@ int NVCommonSetup(struct fb_info *info) } else if (analog_on_B) { CRTCnumber = outputBfromCRTC; FlatPanel = 0; - printk("nvidiafb: CRTC %i" - "appears to have a " + printk("nvidiafb: CRTC %i appears to have a " "CRT attached\n", CRTCnumber); } else if (slaved_on_A) { CRTCnumber = 0; diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index b166f2852a6..b1ccc04f3c9 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -240,6 +240,32 @@ config ORION_WATCHDOG To compile this driver as a module, choose M here: the module will be called orion_wdt. +config COH901327_WATCHDOG + bool "ST-Ericsson COH 901 327 watchdog" + depends on ARCH_U300 + default y if MACH_U300 + help + Say Y here to include Watchdog timer support for the + watchdog embedded into the ST-Ericsson U300 series platforms. + This watchdog is used to reset the system and thus cannot be + compiled as a module. + +config TWL4030_WATCHDOG + tristate "TWL4030 Watchdog" + depends on TWL4030_CORE + help + Support for TI TWL4030 watchdog. Say 'Y' here to enable the + watchdog timer support for TWL4030 chips. + +config STMP3XXX_WATCHDOG + tristate "Freescale STMP3XXX watchdog" + depends on ARCH_STMP3XXX + help + Say Y here if to include support for the watchdog timer + for the Sigmatel STMP37XX/378X SoC. + To compile this driver as a module, choose M here: the + module will be called stmp3xxx_wdt. + # AVR32 Architecture config AT32AP700X_WDT @@ -703,6 +729,12 @@ config SBC_EPX_C3_WATCHDOG # MIPS Architecture +config BCM47XX_WDT + tristate "Broadcom BCM47xx Watchdog Timer" + depends on BCM47XX + help + Hardware driver for the Broadcom BCM47xx Watchog Timer. + config RC32434_WDT tristate "IDT RC32434 SoC Watchdog Timer" depends on MIKROTIK_RB532 @@ -729,6 +761,15 @@ config WDT_MTX1 Hardware driver for the MTX-1 boards. This is a watchdog timer that will reboot the machine after a 100 seconds timer expired. +config PNX833X_WDT + tristate "PNX833x Hardware Watchdog" + depends on SOC_PNX8335 + help + Hardware driver for the PNX833x's watchdog. This is a + watchdog timer that will reboot the machine after a programable + timer has expired and no process has written to /dev/watchdog during + that time. + config WDT_RM9K_GPI tristate "RM9000/GPI hardware watchdog" depends on CPU_RM9000 @@ -966,24 +1007,16 @@ config WDTPCI ---help--- If you have a PCI-WDT500/501 watchdog board, say Y here, otherwise N. - To compile this driver as a module, choose M here: the - module will be called wdt_pci. - -config WDT_501_PCI - bool "PCI-WDT501 features" - depends on WDTPCI - help - Saying Y here and creating a character special file /dev/temperature - with major number 10 and minor number 131 ("man mknod") will give - you a thermometer inside your computer: reading from - /dev/temperature yields one byte, the temperature in degrees - Fahrenheit. This works only if you have a PCI-WDT501 watchdog board - installed. + If you have a PCI-WDT501 watchdog board then you can enable the + temperature sensor by setting the type parameter to 501. If you want to enable the Fan Tachometer on the PCI-WDT501, then you can do this via the tachometer parameter. Only do this if you have a fan tachometer actually set up. + To compile this driver as a module, choose M here: the + module will be called wdt_pci. + # # USB-based Watchdog Cards # diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index c3afa14d5be..3d774294a2b 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o +obj-$(CONFIG_TWL4030_WATCHDOG) += twl4030_wdt.o obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o @@ -41,6 +42,8 @@ obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o +obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o +obj-$(CONFIG_STMP3XXX_WATCHDOG) += stmp3xxx_wdt.o # AVR32 Architecture obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o @@ -98,9 +101,11 @@ obj-$(CONFIG_SBC_EPX_C3_WATCHDOG) += sbc_epx_c3.o # M68KNOMMU Architecture # MIPS Architecture +obj-$(CONFIG_BCM47XX_WDT) += bcm47xx_wdt.o obj-$(CONFIG_RC32434_WDT) += rc32434_wdt.o obj-$(CONFIG_INDYDOG) += indydog.o obj-$(CONFIG_WDT_MTX1) += mtx-1_wdt.o +obj-$(CONFIG_PNX833X_WDT) += pnx833x_wdt.o obj-$(CONFIG_WDT_RM9K_GPI) += rm9k_wdt.o obj-$(CONFIG_SIBYTE_WDOG) += sb_wdog.o obj-$(CONFIG_AR7_WDT) += ar7_wdt.o diff --git a/drivers/watchdog/bcm47xx_wdt.c b/drivers/watchdog/bcm47xx_wdt.c new file mode 100644 index 00000000000..5c7011cda6a --- /dev/null +++ b/drivers/watchdog/bcm47xx_wdt.c @@ -0,0 +1,286 @@ +/* + * Watchdog driver for Broadcom BCM47XX + * + * Copyright (C) 2008 Aleksandar Radovanovic <biblbroks@sezampro.rs> + * Copyright (C) 2009 Matthieu CASTET <castet.matthieu@free.fr> + * + * 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/bitops.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/reboot.h> +#include <linux/types.h> +#include <linux/uaccess.h> +#include <linux/watchdog.h> +#include <linux/timer.h> +#include <linux/jiffies.h> +#include <linux/ssb/ssb_embedded.h> +#include <asm/mach-bcm47xx/bcm47xx.h> + +#define DRV_NAME "bcm47xx_wdt" + +#define WDT_DEFAULT_TIME 30 /* seconds */ +#define WDT_MAX_TIME 255 /* seconds */ + +static int wdt_time = WDT_DEFAULT_TIME; +static int nowayout = WATCHDOG_NOWAYOUT; + +module_param(wdt_time, int, 0); +MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default=" + __MODULE_STRING(WDT_DEFAULT_TIME) ")"); + +#ifdef CONFIG_WATCHDOG_NOWAYOUT +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, + "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +#endif + +static unsigned long bcm47xx_wdt_busy; +static char expect_release; +static struct timer_list wdt_timer; +static atomic_t ticks; + +static inline void bcm47xx_wdt_hw_start(void) +{ + /* this is 2,5s on 100Mhz clock and 2s on 133 Mhz */ + ssb_watchdog_timer_set(&ssb_bcm47xx, 0xfffffff); +} + +static inline int bcm47xx_wdt_hw_stop(void) +{ + return ssb_watchdog_timer_set(&ssb_bcm47xx, 0); +} + +static void bcm47xx_timer_tick(unsigned long unused) +{ + if (!atomic_dec_and_test(&ticks)) { + bcm47xx_wdt_hw_start(); + mod_timer(&wdt_timer, jiffies + HZ); + } else { + printk(KERN_CRIT DRV_NAME "Watchdog will fire soon!!!\n"); + } +} + +static inline void bcm47xx_wdt_pet(void) +{ + atomic_set(&ticks, wdt_time); +} + +static void bcm47xx_wdt_start(void) +{ + bcm47xx_wdt_pet(); + bcm47xx_timer_tick(0); +} + +static void bcm47xx_wdt_pause(void) +{ + del_timer_sync(&wdt_timer); + bcm47xx_wdt_hw_stop(); +} + +static void bcm47xx_wdt_stop(void) +{ + bcm47xx_wdt_pause(); +} + +static int bcm47xx_wdt_settimeout(int new_time) +{ + if ((new_time <= 0) || (new_time > WDT_MAX_TIME)) + return -EINVAL; + + wdt_time = new_time; + return 0; +} + +static int bcm47xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &bcm47xx_wdt_busy)) + return -EBUSY; + + bcm47xx_wdt_start(); + return nonseekable_open(inode, file); +} + +static int bcm47xx_wdt_release(struct inode *inode, struct file *file) +{ + if (expect_release == 42) { + bcm47xx_wdt_stop(); + } else { + printk(KERN_CRIT DRV_NAME + ": Unexpected close, not stopping watchdog!\n"); + bcm47xx_wdt_start(); + } + + clear_bit(0, &bcm47xx_wdt_busy); + expect_release = 0; + return 0; +} + +static ssize_t bcm47xx_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + expect_release = 0; + + for (i = 0; i != len; i++) { + char c; + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + expect_release = 42; + } + } + bcm47xx_wdt_pet(); + } + return len; +} + +static struct watchdog_info bcm47xx_wdt_info = { + .identity = DRV_NAME, + .options = WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING | + WDIOF_MAGICCLOSE, +}; + +static long bcm47xx_wdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_value, retval = -EINVAL;; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &bcm47xx_wdt_info, + sizeof(bcm47xx_wdt_info)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_SETOPTIONS: + if (get_user(new_value, p)) + return -EFAULT; + + if (new_value & WDIOS_DISABLECARD) { + bcm47xx_wdt_stop(); + retval = 0; + } + + if (new_value & WDIOS_ENABLECARD) { + bcm47xx_wdt_start(); + retval = 0; + } + + return retval; + + case WDIOC_KEEPALIVE: + bcm47xx_wdt_pet(); + return 0; + + case WDIOC_SETTIMEOUT: + if (get_user(new_value, p)) + return -EFAULT; + + if (bcm47xx_wdt_settimeout(new_value)) + return -EINVAL; + + bcm47xx_wdt_pet(); + + case WDIOC_GETTIMEOUT: + return put_user(wdt_time, p); + + default: + return -ENOTTY; + } +} + +static int bcm47xx_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + bcm47xx_wdt_stop(); + return NOTIFY_DONE; +} + +static const struct file_operations bcm47xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .unlocked_ioctl = bcm47xx_wdt_ioctl, + .open = bcm47xx_wdt_open, + .release = bcm47xx_wdt_release, + .write = bcm47xx_wdt_write, +}; + +static struct miscdevice bcm47xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &bcm47xx_wdt_fops, +}; + +static struct notifier_block bcm47xx_wdt_notifier = { + .notifier_call = bcm47xx_wdt_notify_sys, +}; + +static int __init bcm47xx_wdt_init(void) +{ + int ret; + + if (bcm47xx_wdt_hw_stop() < 0) + return -ENODEV; + + setup_timer(&wdt_timer, bcm47xx_timer_tick, 0L); + + if (bcm47xx_wdt_settimeout(wdt_time)) { + bcm47xx_wdt_settimeout(WDT_DEFAULT_TIME); + printk(KERN_INFO DRV_NAME ": " + "wdt_time value must be 0 < wdt_time < %d, using %d\n", + (WDT_MAX_TIME + 1), wdt_time); + } + + ret = register_reboot_notifier(&bcm47xx_wdt_notifier); + if (ret) + return ret; + + ret = misc_register(&bcm47xx_wdt_miscdev); + if (ret) { + unregister_reboot_notifier(&bcm47xx_wdt_notifier); + return ret; + } + + printk(KERN_INFO "BCM47xx Watchdog Timer enabled (%d seconds%s)\n", + wdt_time, nowayout ? ", nowayout" : ""); + return 0; +} + +static void __exit bcm47xx_wdt_exit(void) +{ + if (!nowayout) + bcm47xx_wdt_stop(); + + misc_deregister(&bcm47xx_wdt_miscdev); + + unregister_reboot_notifier(&bcm47xx_wdt_notifier); +} + +module_init(bcm47xx_wdt_init); +module_exit(bcm47xx_wdt_exit); + +MODULE_AUTHOR("Aleksandar Radovanovic"); +MODULE_DESCRIPTION("Watchdog driver for Broadcom BCM47xx"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/coh901327_wdt.c b/drivers/watchdog/coh901327_wdt.c new file mode 100644 index 00000000000..fecb307d28e --- /dev/null +++ b/drivers/watchdog/coh901327_wdt.c @@ -0,0 +1,537 @@ +/* + * coh901327_wdt.c + * + * Copyright (C) 2008-2009 ST-Ericsson AB + * License terms: GNU General Public License (GPL) version 2 + * Watchdog driver for the ST-Ericsson AB COH 901 327 IP core + * Author: Linus Walleij <linus.walleij@stericsson.com> + */ +#include <linux/module.h> +#include <linux/types.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/interrupt.h> +#include <linux/pm.h> +#include <linux/platform_device.h> +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> +#include <linux/clk.h> + +#define DRV_NAME "WDOG COH 901 327" + +/* + * COH 901 327 register definitions + */ + +/* WDOG_FEED Register 32bit (-/W) */ +#define U300_WDOG_FR 0x00 +#define U300_WDOG_FR_FEED_RESTART_TIMER 0xFEEDU +/* WDOG_TIMEOUT Register 32bit (R/W) */ +#define U300_WDOG_TR 0x04 +#define U300_WDOG_TR_TIMEOUT_MASK 0x7FFFU +/* WDOG_DISABLE1 Register 32bit (-/W) */ +#define U300_WDOG_D1R 0x08 +#define U300_WDOG_D1R_DISABLE1_DISABLE_TIMER 0x2BADU +/* WDOG_DISABLE2 Register 32bit (R/W) */ +#define U300_WDOG_D2R 0x0C +#define U300_WDOG_D2R_DISABLE2_DISABLE_TIMER 0xCAFEU +#define U300_WDOG_D2R_DISABLE_STATUS_DISABLED 0xDABEU +#define U300_WDOG_D2R_DISABLE_STATUS_ENABLED 0x0000U +/* WDOG_STATUS Register 32bit (R/W) */ +#define U300_WDOG_SR 0x10 +#define U300_WDOG_SR_STATUS_TIMED_OUT 0xCFE8U +#define U300_WDOG_SR_STATUS_NORMAL 0x0000U +#define U300_WDOG_SR_RESET_STATUS_RESET 0xE8B4U +/* WDOG_COUNT Register 32bit (R/-) */ +#define U300_WDOG_CR 0x14 +#define U300_WDOG_CR_VALID_IND 0x8000U +#define U300_WDOG_CR_VALID_STABLE 0x0000U +#define U300_WDOG_CR_COUNT_VALUE_MASK 0x7FFFU +/* WDOG_JTAGOVR Register 32bit (R/W) */ +#define U300_WDOG_JOR 0x18 +#define U300_WDOG_JOR_JTAG_MODE_IND 0x0002U +#define U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE 0x0001U +/* WDOG_RESTART Register 32bit (-/W) */ +#define U300_WDOG_RR 0x1C +#define U300_WDOG_RR_RESTART_VALUE_RESUME 0xACEDU +/* WDOG_IRQ_EVENT Register 32bit (R/W) */ +#define U300_WDOG_IER 0x20 +#define U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND 0x0001U +#define U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE 0x0001U +/* WDOG_IRQ_MASK Register 32bit (R/W) */ +#define U300_WDOG_IMR 0x24 +#define U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE 0x0001U +/* WDOG_IRQ_FORCE Register 32bit (R/W) */ +#define U300_WDOG_IFR 0x28 +#define U300_WDOG_IFR_WILL_BARK_IRQ_FORCE_ENABLE 0x0001U + +/* Default timeout in seconds = 1 minute */ +static int margin = 60; +static resource_size_t phybase; +static resource_size_t physize; +static int irq; +static void __iomem *virtbase; +static unsigned long coh901327_users; +static unsigned long boot_status; +static u16 wdogenablestore; +static u16 irqmaskstore; +static struct device *parent; + +/* + * The watchdog block is of course always clocked, the + * clk_enable()/clk_disable() calls are mainly for performing reference + * counting higher up in the clock hierarchy. + */ +static struct clk *clk; + +/* + * Enabling and disabling functions. + */ +static void coh901327_enable(u16 timeout) +{ + u16 val; + + clk_enable(clk); + /* Restart timer if it is disabled */ + val = readw(virtbase + U300_WDOG_D2R); + if (val == U300_WDOG_D2R_DISABLE_STATUS_DISABLED) + writew(U300_WDOG_RR_RESTART_VALUE_RESUME, + virtbase + U300_WDOG_RR); + /* Acknowledge any pending interrupt so it doesn't just fire off */ + writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, + virtbase + U300_WDOG_IER); + /* Enable the watchdog interrupt */ + writew(U300_WDOG_IMR_WILL_BARK_IRQ_ENABLE, virtbase + U300_WDOG_IMR); + /* Activate the watchdog timer */ + writew(timeout, virtbase + U300_WDOG_TR); + /* Start the watchdog timer */ + writew(U300_WDOG_FR_FEED_RESTART_TIMER, virtbase + U300_WDOG_FR); + /* + * Extra read so that this change propagate in the watchdog. + */ + (void) readw(virtbase + U300_WDOG_CR); + val = readw(virtbase + U300_WDOG_D2R); + clk_disable(clk); + if (val != U300_WDOG_D2R_DISABLE_STATUS_ENABLED) + dev_err(parent, + "%s(): watchdog not enabled! D2R value %04x\n", + __func__, val); +} + +static void coh901327_disable(void) +{ + u16 val; + + clk_enable(clk); + /* Disable the watchdog interrupt if it is active */ + writew(0x0000U, virtbase + U300_WDOG_IMR); + /* If the watchdog is currently enabled, attempt to disable it */ + val = readw(virtbase + U300_WDOG_D2R); + if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) { + writew(U300_WDOG_D1R_DISABLE1_DISABLE_TIMER, + virtbase + U300_WDOG_D1R); + writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, + virtbase + U300_WDOG_D2R); + /* Write this twice (else problems occur) */ + writew(U300_WDOG_D2R_DISABLE2_DISABLE_TIMER, + virtbase + U300_WDOG_D2R); + } + val = readw(virtbase + U300_WDOG_D2R); + clk_disable(clk); + if (val != U300_WDOG_D2R_DISABLE_STATUS_DISABLED) + dev_err(parent, + "%s(): watchdog not disabled! D2R value %04x\n", + __func__, val); +} + +static void coh901327_start(void) +{ + coh901327_enable(margin * 100); +} + +static void coh901327_keepalive(void) +{ + clk_enable(clk); + /* Feed the watchdog */ + writew(U300_WDOG_FR_FEED_RESTART_TIMER, + virtbase + U300_WDOG_FR); + clk_disable(clk); +} + +static int coh901327_settimeout(int time) +{ + /* + * Max margin is 327 since the 10ms + * timeout register is max + * 0x7FFF = 327670ms ~= 327s. + */ + if (time <= 0 || time > 327) + return -EINVAL; + + margin = time; + clk_enable(clk); + /* Set new timeout value */ + writew(margin * 100, virtbase + U300_WDOG_TR); + /* Feed the dog */ + writew(U300_WDOG_FR_FEED_RESTART_TIMER, + virtbase + U300_WDOG_FR); + clk_disable(clk); + return 0; +} + +/* + * This interrupt occurs 10 ms before the watchdog WILL bark. + */ +static irqreturn_t coh901327_interrupt(int irq, void *data) +{ + u16 val; + + /* + * Ack IRQ? If this occurs we're FUBAR anyway, so + * just acknowledge, disable the interrupt and await the imminent end. + * If you at some point need a host of callbacks to be called + * when the system is about to watchdog-reset, add them here! + * + * NOTE: on future versions of this IP-block, it will be possible + * to prevent a watchdog reset by feeding the watchdog at this + * point. + */ + clk_enable(clk); + val = readw(virtbase + U300_WDOG_IER); + if (val == U300_WDOG_IER_WILL_BARK_IRQ_EVENT_IND) + writew(U300_WDOG_IER_WILL_BARK_IRQ_ACK_ENABLE, + virtbase + U300_WDOG_IER); + writew(0x0000U, virtbase + U300_WDOG_IMR); + clk_disable(clk); + dev_crit(parent, "watchdog is barking!\n"); + return IRQ_HANDLED; +} + +/* + * Allow only one user (daemon) to open the watchdog + */ +static int coh901327_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, &coh901327_users)) + return -EBUSY; + coh901327_start(); + return nonseekable_open(inode, file); +} + +static int coh901327_release(struct inode *inode, struct file *file) +{ + clear_bit(1, &coh901327_users); + coh901327_disable(); + return 0; +} + +static ssize_t coh901327_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) + coh901327_keepalive(); + return len; +} + +static long coh901327_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret = -ENOTTY; + u16 val; + int time; + int new_options; + union { + struct watchdog_info __user *ident; + int __user *i; + } uarg; + static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | + WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "COH 901 327 Watchdog", + .firmware_version = 1, + }; + uarg.i = (int __user *)arg; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user(uarg.ident, &ident, + sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, uarg.i); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, uarg.i); + break; + + case WDIOC_SETOPTIONS: + ret = get_user(new_options, uarg.i); + if (ret) + break; + if (new_options & WDIOS_DISABLECARD) + coh901327_disable(); + if (new_options & WDIOS_ENABLECARD) + coh901327_start(); + ret = 0; + break; + + case WDIOC_KEEPALIVE: + coh901327_keepalive(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + ret = get_user(time, uarg.i); + if (ret) + break; + + ret = coh901327_settimeout(time); + if (ret) + break; + /* Then fall through to return set value */ + + case WDIOC_GETTIMEOUT: + ret = put_user(margin, uarg.i); + break; + + case WDIOC_GETTIMELEFT: + clk_enable(clk); + /* Read repeatedly until the value is stable! */ + val = readw(virtbase + U300_WDOG_CR); + while (val & U300_WDOG_CR_VALID_IND) + val = readw(virtbase + U300_WDOG_CR); + val &= U300_WDOG_CR_COUNT_VALUE_MASK; + clk_disable(clk); + if (val != 0) + val /= 100; + ret = put_user(val, uarg.i); + break; + } + return ret; +} + +static const struct file_operations coh901327_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = coh901327_write, + .unlocked_ioctl = coh901327_ioctl, + .open = coh901327_open, + .release = coh901327_release, +}; + +static struct miscdevice coh901327_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &coh901327_fops, +}; + +static int __exit coh901327_remove(struct platform_device *pdev) +{ + misc_deregister(&coh901327_miscdev); + coh901327_disable(); + free_irq(irq, pdev); + clk_put(clk); + iounmap(virtbase); + release_mem_region(phybase, physize); + return 0; +} + + +static int __init coh901327_probe(struct platform_device *pdev) +{ + int ret; + u16 val; + struct resource *res; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + parent = &pdev->dev; + physize = resource_size(res); + phybase = res->start; + + if (request_mem_region(phybase, physize, DRV_NAME) == NULL) { + ret = -EBUSY; + goto out; + } + + virtbase = ioremap(phybase, physize); + if (!virtbase) { + ret = -ENOMEM; + goto out_no_remap; + } + + clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err(&pdev->dev, "could not get clock\n"); + goto out_no_clk; + } + ret = clk_enable(clk); + if (ret) { + dev_err(&pdev->dev, "could not enable clock\n"); + goto out_no_clk_enable; + } + + val = readw(virtbase + U300_WDOG_SR); + switch (val) { + case U300_WDOG_SR_STATUS_TIMED_OUT: + dev_info(&pdev->dev, + "watchdog timed out since last chip reset!\n"); + boot_status = WDIOF_CARDRESET; + /* Status will be cleared below */ + break; + case U300_WDOG_SR_STATUS_NORMAL: + dev_info(&pdev->dev, + "in normal status, no timeouts have occurred.\n"); + break; + default: + dev_info(&pdev->dev, + "contains an illegal status code (%08x)\n", val); + break; + } + + val = readw(virtbase + U300_WDOG_D2R); + switch (val) { + case U300_WDOG_D2R_DISABLE_STATUS_DISABLED: + dev_info(&pdev->dev, "currently disabled.\n"); + break; + case U300_WDOG_D2R_DISABLE_STATUS_ENABLED: + dev_info(&pdev->dev, + "currently enabled! (disabling it now)\n"); + coh901327_disable(); + break; + default: + dev_err(&pdev->dev, + "contains an illegal enable/disable code (%08x)\n", + val); + break; + } + + /* Reset the watchdog */ + writew(U300_WDOG_SR_RESET_STATUS_RESET, virtbase + U300_WDOG_SR); + + irq = platform_get_irq(pdev, 0); + if (request_irq(irq, coh901327_interrupt, IRQF_DISABLED, + DRV_NAME " Bark", pdev)) { + ret = -EIO; + goto out_no_irq; + } + + clk_disable(clk); + + ret = misc_register(&coh901327_miscdev); + if (ret == 0) + dev_info(&pdev->dev, + "initialized. timer margin=%d sec\n", margin); + else + goto out_no_wdog; + + return 0; + +out_no_wdog: + free_irq(irq, pdev); +out_no_irq: + clk_disable(clk); +out_no_clk_enable: + clk_put(clk); +out_no_clk: + iounmap(virtbase); +out_no_remap: + release_mem_region(phybase, SZ_4K); +out: + return ret; +} + +#ifdef CONFIG_PM +static int coh901327_suspend(struct platform_device *pdev, pm_message_t state) +{ + irqmaskstore = readw(virtbase + U300_WDOG_IMR) & 0x0001U; + wdogenablestore = readw(virtbase + U300_WDOG_D2R); + /* If watchdog is on, disable it here and now */ + if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) + coh901327_disable(); + return 0; +} + +static int coh901327_resume(struct platform_device *pdev) +{ + /* Restore the watchdog interrupt */ + writew(irqmaskstore, virtbase + U300_WDOG_IMR); + if (wdogenablestore == U300_WDOG_D2R_DISABLE_STATUS_ENABLED) { + /* Restart the watchdog timer */ + writew(U300_WDOG_RR_RESTART_VALUE_RESUME, + virtbase + U300_WDOG_RR); + writew(U300_WDOG_FR_FEED_RESTART_TIMER, + virtbase + U300_WDOG_FR); + } + return 0; +} +#else +#define coh901327_suspend NULL +#define coh901327_resume NULL +#endif + +/* + * Mistreating the watchdog is the only way to perform a software reset of the + * system on EMP platforms. So we implement this and export a symbol for it. + */ +void coh901327_watchdog_reset(void) +{ + /* Enable even if on JTAG too */ + writew(U300_WDOG_JOR_JTAG_WATCHDOG_ENABLE, + virtbase + U300_WDOG_JOR); + /* + * Timeout = 5s, we have to wait for the watchdog reset to + * actually take place: the watchdog will be reloaded with the + * default value immediately, so we HAVE to reboot and get back + * into the kernel in 30s, or the device will reboot again! + * The boot loader will typically deactivate the watchdog, so we + * need time enough for the boot loader to get to the point of + * deactivating the watchdog before it is shut down by it. + * + * NOTE: on future versions of the watchdog, this restriction is + * gone: the watchdog will be reloaded with a defaul value (1 min) + * instead of last value, and you can conveniently set the watchdog + * timeout to 10ms (value = 1) without any problems. + */ + coh901327_enable(500); + /* Return and await doom */ +} + +static struct platform_driver coh901327_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "coh901327_wdog", + }, + .remove = __exit_p(coh901327_remove), + .suspend = coh901327_suspend, + .resume = coh901327_resume, +}; + +static int __init coh901327_init(void) +{ + return platform_driver_probe(&coh901327_driver, coh901327_probe); +} +module_init(coh901327_init); + +static void __exit coh901327_exit(void) +{ + platform_driver_unregister(&coh901327_driver); +} +module_exit(coh901327_exit); + +MODULE_AUTHOR("Linus Walleij <linus.walleij@stericsson.com>"); +MODULE_DESCRIPTION("COH 901 327 Watchdog"); + +module_param(margin, int, 0); +MODULE_PARM_DESC(margin, "Watchdog margin in seconds (default 60s)"); + +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/hpwdt.c b/drivers/watchdog/hpwdt.c index c0b9169ba5d..a6c5674c78e 100644 --- a/drivers/watchdog/hpwdt.c +++ b/drivers/watchdog/hpwdt.c @@ -120,7 +120,8 @@ static int nowayout = WATCHDOG_NOWAYOUT; static char expect_release; static unsigned long hpwdt_is_open; static unsigned int allow_kdump; -static int hpwdt_nmi_sourcing; +static unsigned int hpwdt_nmi_sourcing; +static unsigned int priority; /* hpwdt at end of die_notify list */ static void __iomem *pci_mem_addr; /* the PCI-memory address */ static unsigned long __iomem *hpwdt_timer_reg; @@ -623,7 +624,7 @@ static struct miscdevice hpwdt_miscdev = { static struct notifier_block die_notifier = { .notifier_call = hpwdt_pretimeout, - .priority = 0x7FFFFFFF, + .priority = 0, }; /* @@ -641,7 +642,8 @@ static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) hpwdt_nmi_sourcing = 1; else dev_warn(&dev->dev, "NMI sourcing is disabled. To enable this " - "functionality you must reboot with nmi_watchdog=0.\n"); + "functionality you must reboot with nmi_watchdog=0 " + "and load the hpwdt driver with priority=1.\n"); } #else static void __devinit hpwdt_check_nmi_sourcing(struct pci_dev *dev) @@ -714,6 +716,14 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, cmn_regs.u1.rah = 0x0D; cmn_regs.u1.ral = 0x02; + /* + * If the priority is set to 1, then we will be put first on the + * die notify list to handle a critical NMI. The default is to + * be last so other users of the NMI signal can function. + */ + if (priority) + die_notifier.priority = 0x7FFFFFFF; + retval = register_die_notifier(&die_notifier); if (retval != 0) { dev_warn(&dev->dev, @@ -733,9 +743,11 @@ static int __devinit hpwdt_init_one(struct pci_dev *dev, printk(KERN_INFO "hp Watchdog Timer Driver: %s" ", timer margin: %d seconds (nowayout=%d)" - ", allow kernel dump: %s (default = 0/OFF).\n", + ", allow kernel dump: %s (default = 0/OFF)" + ", priority: %s (default = 0/LAST).\n", HPWDT_VERSION, soft_margin, nowayout, - (allow_kdump == 0) ? "OFF" : "ON"); + (allow_kdump == 0) ? "OFF" : "ON", + (priority == 0) ? "LAST" : "FIRST"); return 0; @@ -798,5 +810,9 @@ module_param(nowayout, int, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +module_param(priority, int, 0); +MODULE_PARM_DESC(priority, "The hpwdt driver handles NMIs first or last" + " (default = 0/Last)\n"); + module_init(hpwdt_init); module_exit(hpwdt_cleanup); diff --git a/drivers/watchdog/omap_wdt.c b/drivers/watchdog/omap_wdt.c index f2713851aaa..3ed571a2ab1 100644 --- a/drivers/watchdog/omap_wdt.c +++ b/drivers/watchdog/omap_wdt.c @@ -159,6 +159,7 @@ static int omap_wdt_open(struct inode *inode, struct file *file) file->private_data = (void *) wdev; omap_wdt_set_timeout(wdev); + omap_wdt_ping(wdev); /* trigger loading of new timeout value */ omap_wdt_enable(wdev); return nonseekable_open(inode, file); @@ -313,6 +314,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) platform_set_drvdata(pdev, wdev); + clk_enable(wdev->ick); + clk_enable(wdev->fck); + omap_wdt_disable(wdev); omap_wdt_adjust_timeout(timer_margin); @@ -332,6 +336,9 @@ static int __devinit omap_wdt_probe(struct platform_device *pdev) /* autogate OCP interface clock */ __raw_writel(0x01, wdev->base + OMAP_WATCHDOG_SYS_CONFIG); + clk_disable(wdev->ick); + clk_disable(wdev->fck); + omap_wdt_dev = pdev; return 0; diff --git a/drivers/watchdog/pnx833x_wdt.c b/drivers/watchdog/pnx833x_wdt.c new file mode 100644 index 00000000000..538ec2c0519 --- /dev/null +++ b/drivers/watchdog/pnx833x_wdt.c @@ -0,0 +1,282 @@ +/* + * PNX833x Hardware Watchdog Driver + * Copyright 2008 NXP Semiconductors + * Daniel Laird <daniel.j.laird@nxp.com> + * Andre McCurdy <andre.mccurdy@nxp.com> + * + * Heavily based upon - IndyDog 0.3 + * A Hardware Watchdog Device for SGI IP22 + * + * (c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, 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. + * + * based on softdog.c by Alan Cox <alan@redhat.com> + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/notifier.h> +#include <linux/reboot.h> +#include <linux/init.h> +#include <asm/mach-pnx833x/pnx833x.h> + +#define PFX "pnx833x: " +#define WATCHDOG_TIMEOUT 30 /* 30 sec Maximum timeout */ +#define WATCHDOG_COUNT_FREQUENCY 68000000U /* Watchdog counts at 68MHZ. */ + +/** CONFIG block */ +#define PNX833X_CONFIG (0x07000U) +#define PNX833X_CONFIG_CPU_WATCHDOG (0x54) +#define PNX833X_CONFIG_CPU_WATCHDOG_COMPARE (0x58) +#define PNX833X_CONFIG_CPU_COUNTERS_CONTROL (0x1c) + +/** RESET block */ +#define PNX833X_RESET (0x08000U) +#define PNX833X_RESET_CONFIG (0x08) + +static int pnx833x_wdt_alive; + +/* Set default timeout in MHZ.*/ +static int pnx833x_wdt_timeout = (WATCHDOG_TIMEOUT * WATCHDOG_COUNT_FREQUENCY); +module_param(pnx833x_wdt_timeout, int, 0); +MODULE_PARM_DESC(timeout, "Watchdog timeout in Mhz. (68Mhz clock), default=" + __MODULE_STRING(pnx833x_wdt_timeout) "(30 seconds)."); + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int start_enabled = 1; +module_param(start_enabled, int, 0); +MODULE_PARM_DESC(start_enabled, "Watchdog is started on module insertion " + "(default=" __MODULE_STRING(start_enabled) ")"); + +static void pnx833x_wdt_start(void) +{ + /* Enable watchdog causing reset. */ + PNX833X_REG(PNX833X_RESET + PNX833X_RESET_CONFIG) |= 0x1; + /* Set timeout.*/ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; + /* Enable watchdog. */ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_COUNTERS_CONTROL) |= 0x1; + + printk(KERN_INFO PFX "Started watchdog timer.\n"); +} + +static void pnx833x_wdt_stop(void) +{ + /* Disable watchdog causing reset. */ + PNX833X_REG(PNX833X_RESET + PNX833X_CONFIG) &= 0xFFFFFFFE; + /* Disable watchdog.*/ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_COUNTERS_CONTROL) &= 0xFFFFFFFE; + + printk(KERN_INFO PFX "Stopped watchdog timer.\n"); +} + +static void pnx833x_wdt_ping(void) +{ + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = pnx833x_wdt_timeout; +} + +/* + * Allow only one person to hold it open + */ +static int pnx833x_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &pnx833x_wdt_alive)) + return -EBUSY; + + if (nowayout) + __module_get(THIS_MODULE); + + /* Activate timer */ + if (!start_enabled) + pnx833x_wdt_start(); + + pnx833x_wdt_ping(); + + printk(KERN_INFO "Started watchdog timer.\n"); + + return nonseekable_open(inode, file); +} + +static int pnx833x_wdt_release(struct inode *inode, struct file *file) +{ + /* Shut off the timer. + * Lock it in if it's a module and we defined ...NOWAYOUT */ + if (!nowayout) + pnx833x_wdt_stop(); /* Turn the WDT off */ + + clear_bit(0, &pnx833x_wdt_alive); + return 0; +} + +static ssize_t pnx833x_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos) +{ + /* Refresh the timer. */ + if (len) + pnx833x_wdt_ping(); + + return len; +} + +static long pnx833x_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int options, new_timeout = 0; + uint32_t timeout, timeout_left = 0; + + static struct watchdog_info ident = { + .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT, + .firmware_version = 0, + .identity = "Hardware Watchdog for PNX833x", + }; + + switch (cmd) { + default: + return -ENOTTY; + + case WDIOC_GETSUPPORT: + if (copy_to_user((struct watchdog_info *)arg, + &ident, sizeof(ident))) + return -EFAULT; + return 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, (int *)arg); + + case WDIOC_SETOPTIONS: + if (get_user(options, (int *)arg)) + return -EFAULT; + + if (options & WDIOS_DISABLECARD) + pnx833x_wdt_stop(); + + if (options & WDIOS_ENABLECARD) + pnx833x_wdt_start(); + + return 0; + + case WDIOC_KEEPALIVE: + pnx833x_wdt_ping(); + return 0; + + case WDIOC_SETTIMEOUT: + { + if (get_user(new_timeout, (int *)arg)) + return -EFAULT; + + pnx833x_wdt_timeout = new_timeout; + PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE) = new_timeout; + return put_user(new_timeout, (int *)arg); + } + + case WDIOC_GETTIMEOUT: + timeout = PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG_COMPARE); + return put_user(timeout, (int *)arg); + + case WDIOC_GETTIMELEFT: + timeout_left = PNX833X_REG(PNX833X_CONFIG + + PNX833X_CONFIG_CPU_WATCHDOG); + return put_user(timeout_left, (int *)arg); + + } +} + +static int pnx833x_wdt_notify_sys(struct notifier_block *this, + unsigned long code, void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + pnx833x_wdt_stop(); /* Turn the WDT off */ + + return NOTIFY_DONE; +} + +static const struct file_operations pnx833x_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = pnx833x_wdt_write, + .unlocked_ioctl = pnx833x_wdt_ioctl, + .open = pnx833x_wdt_open, + .release = pnx833x_wdt_release, +}; + +static struct miscdevice pnx833x_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &pnx833x_wdt_fops, +}; + +static struct notifier_block pnx833x_wdt_notifier = { + .notifier_call = pnx833x_wdt_notify_sys, +}; + +static char banner[] __initdata = + KERN_INFO PFX "Hardware Watchdog Timer for PNX833x: Version 0.1\n"; + +static int __init watchdog_init(void) +{ + int ret, cause; + + /* Lets check the reason for the reset.*/ + cause = PNX833X_REG(PNX833X_RESET); + /*If bit 31 is set then watchdog was cause of reset.*/ + if (cause & 0x80000000) { + printk(KERN_INFO PFX "The system was previously reset due to " + "the watchdog firing - please investigate...\n"); + } + + ret = register_reboot_notifier(&pnx833x_wdt_notifier); + if (ret) { + printk(KERN_ERR PFX + "cannot register reboot notifier (err=%d)\n", ret); + return ret; + } + + ret = misc_register(&pnx833x_wdt_miscdev); + if (ret) { + printk(KERN_ERR PFX + "cannot register miscdev on minor=%d (err=%d)\n", + WATCHDOG_MINOR, ret); + unregister_reboot_notifier(&pnx833x_wdt_notifier); + return ret; + } + + printk(banner); + if (start_enabled) + pnx833x_wdt_start(); + + return 0; +} + +static void __exit watchdog_exit(void) +{ + misc_deregister(&pnx833x_wdt_miscdev); + unregister_reboot_notifier(&pnx833x_wdt_notifier); +} + +module_init(watchdog_init); +module_exit(watchdog_exit); + +MODULE_AUTHOR("Daniel Laird/Andre McCurdy"); +MODULE_DESCRIPTION("Hardware Watchdog Device for PNX833x"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/stmp3xxx_wdt.c b/drivers/watchdog/stmp3xxx_wdt.c new file mode 100644 index 00000000000..5dd952681f3 --- /dev/null +++ b/drivers/watchdog/stmp3xxx_wdt.c @@ -0,0 +1,296 @@ +/* + * Watchdog driver for Freescale STMP37XX/STMP378X + * + * Author: Vitaly Wool <vital@embeddedalley.com> + * + * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved. + */ +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/uaccess.h> + +#include <mach/platform.h> +#include <mach/regs-rtc.h> + +#define DEFAULT_HEARTBEAT 19 +#define MAX_HEARTBEAT (0x10000000 >> 6) + +/* missing bitmask in headers */ +#define BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER 0x80000000 + +#define WDT_IN_USE 0 +#define WDT_OK_TO_CLOSE 1 + +#define WDOG_COUNTER_RATE 1000 /* 1 kHz clock */ + +static DEFINE_SPINLOCK(stmp3xxx_wdt_io_lock); +static unsigned long wdt_status; +static const int nowayout = WATCHDOG_NOWAYOUT; +static int heartbeat = DEFAULT_HEARTBEAT; +static unsigned long boot_status; + +static void wdt_enable(u32 value) +{ + spin_lock(&stmp3xxx_wdt_io_lock); + __raw_writel(value, REGS_RTC_BASE + HW_RTC_WATCHDOG); + stmp3xxx_setl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); + stmp3xxx_setl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, + REGS_RTC_BASE + HW_RTC_PERSISTENT1); + spin_unlock(&stmp3xxx_wdt_io_lock); +} + +static void wdt_disable(void) +{ + spin_lock(&stmp3xxx_wdt_io_lock); + stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, + REGS_RTC_BASE + HW_RTC_PERSISTENT1); + stmp3xxx_clearl(BM_RTC_CTRL_WATCHDOGEN, REGS_RTC_BASE + HW_RTC_CTRL); + spin_unlock(&stmp3xxx_wdt_io_lock); +} + +static void wdt_ping(void) +{ + wdt_enable(heartbeat * WDOG_COUNTER_RATE); +} + +static int stmp3xxx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(WDT_IN_USE, &wdt_status)) + return -EBUSY; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + wdt_ping(); + + return nonseekable_open(inode, file); +} + +static ssize_t stmp3xxx_wdt_write(struct file *file, const char __user *data, + size_t len, loff_t *ppos) +{ + if (len) { + if (!nowayout) { + size_t i; + + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + + for (i = 0; i != len; i++) { + char c; + + if (get_user(c, data + i)) + return -EFAULT; + if (c == 'V') + set_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + wdt_ping(); + } + + return len; +} + +static struct watchdog_info ident = { + .options = WDIOF_CARDRESET | + WDIOF_MAGICCLOSE | + WDIOF_SETTIMEOUT | + WDIOF_KEEPALIVEPING, + .identity = "STMP3XXX Watchdog", +}; + +static long stmp3xxx_wdt_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_heartbeat, opts; + int ret = -ENOTTY; + + switch (cmd) { + case WDIOC_GETSUPPORT: + ret = copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; + break; + + case WDIOC_GETSTATUS: + ret = put_user(0, p); + break; + + case WDIOC_GETBOOTSTATUS: + ret = put_user(boot_status, p); + break; + + case WDIOC_SETOPTIONS: + if (get_user(opts, p)) { + ret = -EFAULT; + break; + } + if (opts & WDIOS_DISABLECARD) + wdt_disable(); + else if (opts & WDIOS_ENABLECARD) + wdt_ping(); + else { + pr_debug("%s: unknown option 0x%x\n", __func__, opts); + ret = -EINVAL; + break; + } + ret = 0; + break; + + case WDIOC_KEEPALIVE: + wdt_ping(); + ret = 0; + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_heartbeat, p)) { + ret = -EFAULT; + break; + } + if (new_heartbeat <= 0 || new_heartbeat > MAX_HEARTBEAT) { + ret = -EINVAL; + break; + } + + heartbeat = new_heartbeat; + wdt_ping(); + /* Fall through */ + + case WDIOC_GETTIMEOUT: + ret = put_user(heartbeat, p); + break; + } + return ret; +} + +static int stmp3xxx_wdt_release(struct inode *inode, struct file *file) +{ + int ret = 0; + + if (!nowayout) { + if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status)) { + wdt_ping(); + pr_debug("%s: Device closed unexpectdly\n", __func__); + ret = -EINVAL; + } else { + wdt_disable(); + clear_bit(WDT_OK_TO_CLOSE, &wdt_status); + } + } + clear_bit(WDT_IN_USE, &wdt_status); + + return ret; +} + +static const struct file_operations stmp3xxx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = stmp3xxx_wdt_write, + .unlocked_ioctl = stmp3xxx_wdt_ioctl, + .open = stmp3xxx_wdt_open, + .release = stmp3xxx_wdt_release, +}; + +static struct miscdevice stmp3xxx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &stmp3xxx_wdt_fops, +}; + +static int __devinit stmp3xxx_wdt_probe(struct platform_device *pdev) +{ + int ret = 0; + + if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT) + heartbeat = DEFAULT_HEARTBEAT; + + boot_status = __raw_readl(REGS_RTC_BASE + HW_RTC_PERSISTENT1) & + BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER; + boot_status = !!boot_status; + stmp3xxx_clearl(BV_RTC_PERSISTENT1_GENERAL__RTC_FORCE_UPDATER, + REGS_RTC_BASE + HW_RTC_PERSISTENT1); + wdt_disable(); /* disable for now */ + + ret = misc_register(&stmp3xxx_wdt_miscdev); + if (ret < 0) { + dev_err(&pdev->dev, "cannot register misc device\n"); + return ret; + } + + printk(KERN_INFO "stmp3xxx watchdog: initialized, heartbeat %d sec\n", + heartbeat); + + return ret; +} + +static int __devexit stmp3xxx_wdt_remove(struct platform_device *pdev) +{ + misc_deregister(&stmp3xxx_wdt_miscdev); + return 0; +} + +#ifdef CONFIG_PM +static int wdt_suspended; +static u32 wdt_saved_time; + +static int stmp3xxx_wdt_suspend(struct platform_device *pdev, + pm_message_t state) +{ + if (__raw_readl(REGS_RTC_BASE + HW_RTC_CTRL) & + BM_RTC_CTRL_WATCHDOGEN) { + wdt_suspended = 1; + wdt_saved_time = __raw_readl(REGS_RTC_BASE + HW_RTC_WATCHDOG); + wdt_disable(); + } + return 0; +} + +static int stmp3xxx_wdt_resume(struct platform_device *pdev) +{ + if (wdt_suspended) { + wdt_enable(wdt_saved_time); + wdt_suspended = 0; + } + return 0; +} +#else +#define stmp3xxx_wdt_suspend NULL +#define stmp3xxx_wdt_resume NULL +#endif + +static struct platform_driver platform_wdt_driver = { + .driver = { + .name = "stmp3xxx_wdt", + }, + .probe = stmp3xxx_wdt_probe, + .remove = __devexit_p(stmp3xxx_wdt_remove), + .suspend = stmp3xxx_wdt_suspend, + .resume = stmp3xxx_wdt_resume, +}; + +static int __init stmp3xxx_wdt_init(void) +{ + return platform_driver_register(&platform_wdt_driver); +} + +static void __exit stmp3xxx_wdt_exit(void) +{ + return platform_driver_unregister(&platform_wdt_driver); +} + +module_init(stmp3xxx_wdt_init); +module_exit(stmp3xxx_wdt_exit); + +MODULE_DESCRIPTION("STMP3XXX Watchdog Driver"); +MODULE_LICENSE("GPL"); + +module_param(heartbeat, int, 0); +MODULE_PARM_DESC(heartbeat, + "Watchdog heartbeat period in seconds from 1 to " + __MODULE_STRING(MAX_HEARTBEAT) ", default " + __MODULE_STRING(DEFAULT_HEARTBEAT)); + +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/watchdog/twl4030_wdt.c b/drivers/watchdog/twl4030_wdt.c new file mode 100644 index 00000000000..cb46556f297 --- /dev/null +++ b/drivers/watchdog/twl4030_wdt.c @@ -0,0 +1,272 @@ +/* + * Copyright (C) Nokia Corporation + * + * Written by Timo Kokkonen <timo.t.kokkonen at 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/watchdog.h> +#include <linux/platform_device.h> +#include <linux/miscdevice.h> +#include <linux/uaccess.h> +#include <linux/i2c/twl4030.h> + +#define TWL4030_WATCHDOG_CFG_REG_OFFS 0x3 + +#define TWL4030_WDT_STATE_OPEN 0x1 +#define TWL4030_WDT_STATE_ACTIVE 0x8 + +static struct platform_device *twl4030_wdt_dev; + +struct twl4030_wdt { + struct miscdevice miscdev; + int timer_margin; + unsigned long state; +}; + +static int nowayout = WATCHDOG_NOWAYOUT; +module_param(nowayout, int, 0); +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started " + "(default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); + +static int twl4030_wdt_write(unsigned char val) +{ + return twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER, val, + TWL4030_WATCHDOG_CFG_REG_OFFS); +} + +static int twl4030_wdt_enable(struct twl4030_wdt *wdt) +{ + return twl4030_wdt_write(wdt->timer_margin + 1); +} + +static int twl4030_wdt_disable(struct twl4030_wdt *wdt) +{ + return twl4030_wdt_write(0); +} + +static int twl4030_wdt_set_timeout(struct twl4030_wdt *wdt, int timeout) +{ + if (timeout < 0 || timeout > 30) { + dev_warn(wdt->miscdev.parent, + "Timeout can only be in the range [0-30] seconds"); + return -EINVAL; + } + wdt->timer_margin = timeout; + return twl4030_wdt_enable(wdt); +} + +static ssize_t twl4030_wdt_write_fop(struct file *file, + const char __user *data, size_t len, loff_t *ppos) +{ + struct twl4030_wdt *wdt = file->private_data; + + if (len) + twl4030_wdt_enable(wdt); + + return len; +} + +static long twl4030_wdt_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + int __user *p = argp; + int new_margin; + struct twl4030_wdt *wdt = file->private_data; + + static const struct watchdog_info twl4030_wd_ident = { + .identity = "TWL4030 Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + case WDIOC_GETSUPPORT: + return copy_to_user(argp, &twl4030_wd_ident, + sizeof(twl4030_wd_ident)) ? -EFAULT : 0; + + case WDIOC_GETSTATUS: + case WDIOC_GETBOOTSTATUS: + return put_user(0, p); + + case WDIOC_KEEPALIVE: + twl4030_wdt_enable(wdt); + break; + + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, p)) + return -EFAULT; + if (twl4030_wdt_set_timeout(wdt, new_margin)) + return -EINVAL; + return put_user(wdt->timer_margin, p); + + case WDIOC_GETTIMEOUT: + return put_user(wdt->timer_margin, p); + + default: + return -ENOTTY; + } + + return 0; +} + +static int twl4030_wdt_open(struct inode *inode, struct file *file) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(twl4030_wdt_dev); + + /* /dev/watchdog can only be opened once */ + if (test_and_set_bit(0, &wdt->state)) + return -EBUSY; + + wdt->state |= TWL4030_WDT_STATE_ACTIVE; + file->private_data = (void *) wdt; + + twl4030_wdt_enable(wdt); + return nonseekable_open(inode, file); +} + +static int twl4030_wdt_release(struct inode *inode, struct file *file) +{ + struct twl4030_wdt *wdt = file->private_data; + if (nowayout) { + dev_alert(wdt->miscdev.parent, + "Unexpected close, watchdog still running!\n"); + twl4030_wdt_enable(wdt); + } else { + if (twl4030_wdt_disable(wdt)) + return -EFAULT; + wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; + } + + clear_bit(0, &wdt->state); + return 0; +} + +static const struct file_operations twl4030_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = twl4030_wdt_open, + .release = twl4030_wdt_release, + .unlocked_ioctl = twl4030_wdt_ioctl, + .write = twl4030_wdt_write_fop, +}; + +static int __devinit twl4030_wdt_probe(struct platform_device *pdev) +{ + int ret = 0; + struct twl4030_wdt *wdt; + + wdt = kzalloc(sizeof(struct twl4030_wdt), GFP_KERNEL); + if (!wdt) + return -ENOMEM; + + wdt->state = 0; + wdt->timer_margin = 30; + wdt->miscdev.parent = &pdev->dev; + wdt->miscdev.fops = &twl4030_wdt_fops; + wdt->miscdev.minor = WATCHDOG_MINOR; + wdt->miscdev.name = "watchdog"; + + platform_set_drvdata(pdev, wdt); + + twl4030_wdt_dev = pdev; + + ret = misc_register(&wdt->miscdev); + if (ret) { + dev_err(wdt->miscdev.parent, + "Failed to register misc device\n"); + platform_set_drvdata(pdev, NULL); + kfree(wdt); + twl4030_wdt_dev = NULL; + return ret; + } + return 0; +} + +static int __devexit twl4030_wdt_remove(struct platform_device *pdev) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(pdev); + + if (wdt->state & TWL4030_WDT_STATE_ACTIVE) + if (twl4030_wdt_disable(wdt)) + return -EFAULT; + + wdt->state &= ~TWL4030_WDT_STATE_ACTIVE; + misc_deregister(&wdt->miscdev); + + platform_set_drvdata(pdev, NULL); + kfree(wdt); + twl4030_wdt_dev = NULL; + + return 0; +} + +#ifdef CONFIG_PM +static int twl4030_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(pdev); + if (wdt->state & TWL4030_WDT_STATE_ACTIVE) + return twl4030_wdt_disable(wdt); + + return 0; +} + +static int twl4030_wdt_resume(struct platform_device *pdev) +{ + struct twl4030_wdt *wdt = platform_get_drvdata(pdev); + if (wdt->state & TWL4030_WDT_STATE_ACTIVE) + return twl4030_wdt_enable(wdt); + + return 0; +} +#else +#define twl4030_wdt_suspend NULL +#define twl4030_wdt_resume NULL +#endif + +static struct platform_driver twl4030_wdt_driver = { + .probe = twl4030_wdt_probe, + .remove = __devexit_p(twl4030_wdt_remove), + .suspend = twl4030_wdt_suspend, + .resume = twl4030_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "twl4030_wdt", + }, +}; + +static int __devinit twl4030_wdt_init(void) +{ + return platform_driver_register(&twl4030_wdt_driver); +} +module_init(twl4030_wdt_init); + +static void __devexit twl4030_wdt_exit(void) +{ + platform_driver_unregister(&twl4030_wdt_driver); +} +module_exit(twl4030_wdt_exit); + +MODULE_AUTHOR("Nokia Corporation"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); +MODULE_ALIAS("platform:twl4030_wdt"); + diff --git a/drivers/watchdog/wdt_pci.c b/drivers/watchdog/wdt_pci.c index c45839a4a34..7a1bdc7c95a 100644 --- a/drivers/watchdog/wdt_pci.c +++ b/drivers/watchdog/wdt_pci.c @@ -2,7 +2,7 @@ * Industrial Computer Source PCI-WDT500/501 driver * * (c) Copyright 1996-1997 Alan Cox <alan@lxorguk.ukuu.org.uk>, - * All Rights Reserved. + * 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 @@ -99,14 +99,16 @@ MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); -#ifdef CONFIG_WDT_501_PCI /* Support for the Fan Tachometer on the PCI-WDT501 */ static int tachometer; - module_param(tachometer, int, 0); MODULE_PARM_DESC(tachometer, - "PCI-WDT501 Fan Tachometer support (0=disable, default=0)"); -#endif /* CONFIG_WDT_501_PCI */ + "PCI-WDT501 Fan Tachometer support (0=disable, default=0)"); + +static int type = 500; +module_param(type, int, 0); +MODULE_PARM_DESC(type, + "PCI-WDT501 Card type (500 or 501 , default=500)"); /* * Programming support @@ -266,22 +268,21 @@ static int wdtpci_get_status(int *status) *status |= WDIOF_EXTERN1; if (new_status & WDC_SR_ISII1) *status |= WDIOF_EXTERN2; -#ifdef CONFIG_WDT_501_PCI - if (!(new_status & WDC_SR_TGOOD)) - *status |= WDIOF_OVERHEAT; - if (!(new_status & WDC_SR_PSUOVER)) - *status |= WDIOF_POWEROVER; - if (!(new_status & WDC_SR_PSUUNDR)) - *status |= WDIOF_POWERUNDER; - if (tachometer) { - if (!(new_status & WDC_SR_FANGOOD)) - *status |= WDIOF_FANFAULT; + if (type == 501) { + if (!(new_status & WDC_SR_TGOOD)) + *status |= WDIOF_OVERHEAT; + if (!(new_status & WDC_SR_PSUOVER)) + *status |= WDIOF_POWEROVER; + if (!(new_status & WDC_SR_PSUUNDR)) + *status |= WDIOF_POWERUNDER; + if (tachometer) { + if (!(new_status & WDC_SR_FANGOOD)) + *status |= WDIOF_FANFAULT; + } } -#endif /* CONFIG_WDT_501_PCI */ return 0; } -#ifdef CONFIG_WDT_501_PCI /** * wdtpci_get_temperature: * @@ -300,7 +301,6 @@ static int wdtpci_get_temperature(int *temperature) *temperature = (c * 11 / 15) + 7; return 0; } -#endif /* CONFIG_WDT_501_PCI */ /** * wdtpci_interrupt: @@ -327,22 +327,22 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) printk(KERN_CRIT PFX "status %d\n", status); -#ifdef CONFIG_WDT_501_PCI - if (!(status & WDC_SR_TGOOD)) { - u8 alarm = inb(WDT_RT); - printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", alarm); - udelay(8); - } - if (!(status & WDC_SR_PSUOVER)) - printk(KERN_CRIT PFX "PSU over voltage.\n"); - if (!(status & WDC_SR_PSUUNDR)) - printk(KERN_CRIT PFX "PSU under voltage.\n"); - if (tachometer) { - if (!(status & WDC_SR_FANGOOD)) - printk(KERN_CRIT PFX "Possible fan fault.\n"); + if (type == 501) { + if (!(status & WDC_SR_TGOOD)) { + printk(KERN_CRIT PFX "Overheat alarm.(%d)\n", + inb(WDT_RT)); + udelay(8); + } + if (!(status & WDC_SR_PSUOVER)) + printk(KERN_CRIT PFX "PSU over voltage.\n"); + if (!(status & WDC_SR_PSUUNDR)) + printk(KERN_CRIT PFX "PSU under voltage.\n"); + if (tachometer) { + if (!(status & WDC_SR_FANGOOD)) + printk(KERN_CRIT PFX "Possible fan fault.\n"); + } } -#endif /* CONFIG_WDT_501_PCI */ - if (!(status&WDC_SR_WCCR)) { + if (!(status & WDC_SR_WCCR)) { #ifdef SOFTWARE_REBOOT #ifdef ONLY_TESTING printk(KERN_CRIT PFX "Would Reboot.\n"); @@ -371,12 +371,13 @@ static irqreturn_t wdtpci_interrupt(int irq, void *dev_id) */ static ssize_t wdtpci_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) + size_t count, loff_t *ppos) { if (count) { if (!nowayout) { size_t i; + /* In case it was set long ago */ expect_close = 0; for (i = 0; i != count; i++) { @@ -406,10 +407,10 @@ static ssize_t wdtpci_write(struct file *file, const char __user *buf, static long wdtpci_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - int new_heartbeat; - int status; void __user *argp = (void __user *)arg; int __user *p = argp; + int new_heartbeat; + int status; static struct watchdog_info ident = { .options = WDIOF_SETTIMEOUT| @@ -421,11 +422,12 @@ static long wdtpci_ioctl(struct file *file, unsigned int cmd, /* Add options according to the card we have */ ident.options |= (WDIOF_EXTERN1|WDIOF_EXTERN2); -#ifdef CONFIG_WDT_501_PCI - ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER|WDIOF_POWEROVER); - if (tachometer) - ident.options |= WDIOF_FANFAULT; -#endif /* CONFIG_WDT_501_PCI */ + if (type == 501) { + ident.options |= (WDIOF_OVERHEAT|WDIOF_POWERUNDER| + WDIOF_POWEROVER); + if (tachometer) + ident.options |= WDIOF_FANFAULT; + } switch (cmd) { case WDIOC_GETSUPPORT: @@ -503,7 +505,6 @@ static int wdtpci_release(struct inode *inode, struct file *file) return 0; } -#ifdef CONFIG_WDT_501_PCI /** * wdtpci_temp_read: * @file: file handle to the watchdog board @@ -554,7 +555,6 @@ static int wdtpci_temp_release(struct inode *inode, struct file *file) { return 0; } -#endif /* CONFIG_WDT_501_PCI */ /** * notify_sys: @@ -596,7 +596,6 @@ static struct miscdevice wdtpci_miscdev = { .fops = &wdtpci_fops, }; -#ifdef CONFIG_WDT_501_PCI static const struct file_operations wdtpci_temp_fops = { .owner = THIS_MODULE, .llseek = no_llseek, @@ -610,7 +609,6 @@ static struct miscdevice temp_miscdev = { .name = "temperature", .fops = &wdtpci_temp_fops, }; -#endif /* CONFIG_WDT_501_PCI */ /* * The WDT card needs to learn about soft shutdowns in order to @@ -633,6 +631,11 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, return -ENODEV; } + if (type != 500 && type != 501) { + printk(KERN_ERR PFX "unknown card type '%d'.\n", type); + return -ENODEV; + } + if (pci_enable_device(dev)) { printk(KERN_ERR PFX "Not possible to enable PCI Device\n"); return -ENODEV; @@ -678,15 +681,15 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, goto out_irq; } -#ifdef CONFIG_WDT_501_PCI - ret = misc_register(&temp_miscdev); - if (ret) { - printk(KERN_ERR PFX + if (type == 501) { + ret = misc_register(&temp_miscdev); + if (ret) { + printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n", - TEMP_MINOR, ret); - goto out_rbt; + TEMP_MINOR, ret); + goto out_rbt; + } } -#endif /* CONFIG_WDT_501_PCI */ ret = misc_register(&wdtpci_miscdev); if (ret) { @@ -698,20 +701,18 @@ static int __devinit wdtpci_init_one(struct pci_dev *dev, printk(KERN_INFO PFX "initialized. heartbeat=%d sec (nowayout=%d)\n", heartbeat, nowayout); -#ifdef CONFIG_WDT_501_PCI - printk(KERN_INFO "wdt: Fan Tachometer is %s\n", + if (type == 501) + printk(KERN_INFO "wdt: Fan Tachometer is %s\n", (tachometer ? "Enabled" : "Disabled")); -#endif /* CONFIG_WDT_501_PCI */ ret = 0; out: return ret; out_misc: -#ifdef CONFIG_WDT_501_PCI - misc_deregister(&temp_miscdev); + if (type == 501) + misc_deregister(&temp_miscdev); out_rbt: -#endif /* CONFIG_WDT_501_PCI */ unregister_reboot_notifier(&wdtpci_notifier); out_irq: free_irq(irq, &wdtpci_miscdev); @@ -728,9 +729,8 @@ static void __devexit wdtpci_remove_one(struct pci_dev *pdev) /* here we assume only one device will ever have * been picked up and registered by probe function */ misc_deregister(&wdtpci_miscdev); -#ifdef CONFIG_WDT_501_PCI - misc_deregister(&temp_miscdev); -#endif /* CONFIG_WDT_501_PCI */ + if (type == 501) + misc_deregister(&temp_miscdev); unregister_reboot_notifier(&wdtpci_notifier); free_irq(irq, &wdtpci_miscdev); release_region(io, 16); |