diff options
author | Paul Mundt <lethal@linux-sh.org> | 2009-09-01 13:54:14 +0900 |
---|---|---|
committer | Paul Mundt <lethal@linux-sh.org> | 2009-09-01 13:54:14 +0900 |
commit | ac6a0cf6716bb46813d0161024c66c2af66e53d1 (patch) | |
tree | c7f53b1a04c590032c022549f3186fb9b04f8358 /drivers | |
parent | e76a0136a3cf1859fbc07f122e42293d22229558 (diff) | |
parent | ce3f7cb96e67d6518c7fc7b361a76409c3817d64 (diff) |
Merge branch 'master' into sh/smp
Conflicts:
arch/sh/mm/cache-sh4.c
Diffstat (limited to 'drivers')
163 files changed, 5211 insertions, 1421 deletions
diff --git a/drivers/base/dd.c b/drivers/base/dd.c index f0106875f01..7b34b3a48f6 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -23,6 +23,7 @@ #include <linux/kthread.h> #include <linux/wait.h> #include <linux/async.h> +#include <linux/pm_runtime.h> #include "base.h" #include "power/power.h" @@ -202,7 +203,10 @@ int driver_probe_device(struct device_driver *drv, struct device *dev) pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); + pm_runtime_get_noresume(dev); + pm_runtime_barrier(dev); ret = really_probe(dev, drv); + pm_runtime_put_sync(dev); return ret; } @@ -245,7 +249,9 @@ int device_attach(struct device *dev) ret = 0; } } else { + pm_runtime_get_noresume(dev); ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach); + pm_runtime_put_sync(dev); } up(&dev->sem); return ret; @@ -306,6 +312,9 @@ static void __device_release_driver(struct device *dev) drv = dev->driver; if (drv) { + pm_runtime_get_noresume(dev); + pm_runtime_barrier(dev); + driver_sysfs_remove(dev); if (dev->bus) @@ -324,6 +333,8 @@ static void __device_release_driver(struct device *dev) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_UNBOUND_DRIVER, dev); + + pm_runtime_put_sync(dev); } } diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 456594bd97b..0f7d434ce98 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -17,6 +17,7 @@ #include <linux/bootmem.h> #include <linux/err.h> #include <linux/slab.h> +#include <linux/pm_runtime.h> #include "base.h" @@ -625,30 +626,6 @@ static int platform_legacy_suspend(struct device *dev, pm_message_t mesg) return ret; } -static int platform_legacy_suspend_late(struct device *dev, pm_message_t mesg) -{ - struct platform_driver *pdrv = to_platform_driver(dev->driver); - struct platform_device *pdev = to_platform_device(dev); - int ret = 0; - - if (dev->driver && pdrv->suspend_late) - ret = pdrv->suspend_late(pdev, mesg); - - return ret; -} - -static int platform_legacy_resume_early(struct device *dev) -{ - struct platform_driver *pdrv = to_platform_driver(dev->driver); - struct platform_device *pdev = to_platform_device(dev); - int ret = 0; - - if (dev->driver && pdrv->resume_early) - ret = pdrv->resume_early(pdev); - - return ret; -} - static int platform_legacy_resume(struct device *dev) { struct platform_driver *pdrv = to_platform_driver(dev->driver); @@ -680,6 +657,13 @@ static void platform_pm_complete(struct device *dev) drv->pm->complete(dev); } +#else /* !CONFIG_PM_SLEEP */ + +#define platform_pm_prepare NULL +#define platform_pm_complete NULL + +#endif /* !CONFIG_PM_SLEEP */ + #ifdef CONFIG_SUSPEND static int platform_pm_suspend(struct device *dev) @@ -711,8 +695,6 @@ static int platform_pm_suspend_noirq(struct device *dev) if (drv->pm) { if (drv->pm->suspend_noirq) ret = drv->pm->suspend_noirq(dev); - } else { - ret = platform_legacy_suspend_late(dev, PMSG_SUSPEND); } return ret; @@ -747,8 +729,6 @@ static int platform_pm_resume_noirq(struct device *dev) if (drv->pm) { if (drv->pm->resume_noirq) ret = drv->pm->resume_noirq(dev); - } else { - ret = platform_legacy_resume_early(dev); } return ret; @@ -794,8 +774,6 @@ static int platform_pm_freeze_noirq(struct device *dev) if (drv->pm) { if (drv->pm->freeze_noirq) ret = drv->pm->freeze_noirq(dev); - } else { - ret = platform_legacy_suspend_late(dev, PMSG_FREEZE); } return ret; @@ -830,8 +808,6 @@ static int platform_pm_thaw_noirq(struct device *dev) if (drv->pm) { if (drv->pm->thaw_noirq) ret = drv->pm->thaw_noirq(dev); - } else { - ret = platform_legacy_resume_early(dev); } return ret; @@ -866,8 +842,6 @@ static int platform_pm_poweroff_noirq(struct device *dev) if (drv->pm) { if (drv->pm->poweroff_noirq) ret = drv->pm->poweroff_noirq(dev); - } else { - ret = platform_legacy_suspend_late(dev, PMSG_HIBERNATE); } return ret; @@ -902,8 +876,6 @@ static int platform_pm_restore_noirq(struct device *dev) if (drv->pm) { if (drv->pm->restore_noirq) ret = drv->pm->restore_noirq(dev); - } else { - ret = platform_legacy_resume_early(dev); } return ret; @@ -922,7 +894,32 @@ static int platform_pm_restore_noirq(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -static struct dev_pm_ops platform_dev_pm_ops = { +#ifdef CONFIG_PM_RUNTIME + +int __weak platform_pm_runtime_suspend(struct device *dev) +{ + return -ENOSYS; +}; + +int __weak platform_pm_runtime_resume(struct device *dev) +{ + return -ENOSYS; +}; + +int __weak platform_pm_runtime_idle(struct device *dev) +{ + return -ENOSYS; +}; + +#else /* !CONFIG_PM_RUNTIME */ + +#define platform_pm_runtime_suspend NULL +#define platform_pm_runtime_resume NULL +#define platform_pm_runtime_idle NULL + +#endif /* !CONFIG_PM_RUNTIME */ + +static const struct dev_pm_ops platform_dev_pm_ops = { .prepare = platform_pm_prepare, .complete = platform_pm_complete, .suspend = platform_pm_suspend, @@ -937,22 +934,17 @@ static struct dev_pm_ops platform_dev_pm_ops = { .thaw_noirq = platform_pm_thaw_noirq, .poweroff_noirq = platform_pm_poweroff_noirq, .restore_noirq = platform_pm_restore_noirq, + .runtime_suspend = platform_pm_runtime_suspend, + .runtime_resume = platform_pm_runtime_resume, + .runtime_idle = platform_pm_runtime_idle, }; -#define PLATFORM_PM_OPS_PTR (&platform_dev_pm_ops) - -#else /* !CONFIG_PM_SLEEP */ - -#define PLATFORM_PM_OPS_PTR NULL - -#endif /* !CONFIG_PM_SLEEP */ - struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, - .pm = PLATFORM_PM_OPS_PTR, + .pm = &platform_dev_pm_ops, }; EXPORT_SYMBOL_GPL(platform_bus_type); diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 911208b8925..3ce3519e8f3 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_PM) += sysfs.o obj-$(CONFIG_PM_SLEEP) += main.o +obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 58a3e572f2c..86990011277 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -21,6 +21,7 @@ #include <linux/kallsyms.h> #include <linux/mutex.h> #include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/resume-trace.h> #include <linux/rwsem.h> #include <linux/interrupt.h> @@ -49,6 +50,16 @@ static DEFINE_MUTEX(dpm_list_mtx); static bool transition_started; /** + * device_pm_init - Initialize the PM-related part of a device object + * @dev: Device object being initialized. + */ +void device_pm_init(struct device *dev) +{ + dev->power.status = DPM_ON; + pm_runtime_init(dev); +} + +/** * device_pm_lock - lock the list of active devices used by the PM core */ void device_pm_lock(void) @@ -105,6 +116,7 @@ void device_pm_remove(struct device *dev) mutex_lock(&dpm_list_mtx); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); + pm_runtime_remove(dev); } /** @@ -157,8 +169,9 @@ void device_pm_move_last(struct device *dev) * @ops: PM operations to choose from. * @state: PM transition of the system being carried out. */ -static int pm_op(struct device *dev, struct dev_pm_ops *ops, - pm_message_t state) +static int pm_op(struct device *dev, + const struct dev_pm_ops *ops, + pm_message_t state) { int error = 0; @@ -220,7 +233,8 @@ static int pm_op(struct device *dev, struct dev_pm_ops *ops, * The operation is executed with interrupts disabled by the only remaining * functional CPU in the system. */ -static int pm_noirq_op(struct device *dev, struct dev_pm_ops *ops, +static int pm_noirq_op(struct device *dev, + const struct dev_pm_ops *ops, pm_message_t state) { int error = 0; @@ -510,6 +524,7 @@ static void dpm_complete(pm_message_t state) mutex_unlock(&dpm_list_mtx); device_complete(dev, state); + pm_runtime_put_noidle(dev); mutex_lock(&dpm_list_mtx); } @@ -755,7 +770,14 @@ static int dpm_prepare(pm_message_t state) dev->power.status = DPM_PREPARING; mutex_unlock(&dpm_list_mtx); - error = device_prepare(dev, state); + pm_runtime_get_noresume(dev); + if (pm_runtime_barrier(dev) && device_may_wakeup(dev)) { + /* Wake-up requested during system sleep transition. */ + pm_runtime_put_noidle(dev); + error = -EBUSY; + } else { + error = device_prepare(dev, state); + } mutex_lock(&dpm_list_mtx); if (error) { diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index c7cb4fc3735..b8fa1aa5225 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -1,7 +1,14 @@ -static inline void device_pm_init(struct device *dev) -{ - dev->power.status = DPM_ON; -} +#ifdef CONFIG_PM_RUNTIME + +extern void pm_runtime_init(struct device *dev); +extern void pm_runtime_remove(struct device *dev); + +#else /* !CONFIG_PM_RUNTIME */ + +static inline void pm_runtime_init(struct device *dev) {} +static inline void pm_runtime_remove(struct device *dev) {} + +#endif /* !CONFIG_PM_RUNTIME */ #ifdef CONFIG_PM_SLEEP @@ -16,23 +23,33 @@ static inline struct device *to_device(struct list_head *entry) return container_of(entry, struct device, power.entry); } +extern void device_pm_init(struct device *dev); extern void device_pm_add(struct device *); extern void device_pm_remove(struct device *); extern void device_pm_move_before(struct device *, struct device *); extern void device_pm_move_after(struct device *, struct device *); extern void device_pm_move_last(struct device *); -#else /* CONFIG_PM_SLEEP */ +#else /* !CONFIG_PM_SLEEP */ + +static inline void device_pm_init(struct device *dev) +{ + pm_runtime_init(dev); +} + +static inline void device_pm_remove(struct device *dev) +{ + pm_runtime_remove(dev); +} static inline void device_pm_add(struct device *dev) {} -static inline void device_pm_remove(struct device *dev) {} static inline void device_pm_move_before(struct device *deva, struct device *devb) {} static inline void device_pm_move_after(struct device *deva, struct device *devb) {} static inline void device_pm_move_last(struct device *dev) {} -#endif +#endif /* !CONFIG_PM_SLEEP */ #ifdef CONFIG_PM diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c new file mode 100644 index 00000000000..38556f6cc22 --- /dev/null +++ b/drivers/base/power/runtime.c @@ -0,0 +1,1011 @@ +/* + * drivers/base/power/runtime.c - Helper functions for device run-time PM + * + * Copyright (c) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. + * + * This file is released under the GPLv2. + */ + +#include <linux/sched.h> +#include <linux/pm_runtime.h> +#include <linux/jiffies.h> + +static int __pm_runtime_resume(struct device *dev, bool from_wq); +static int __pm_request_idle(struct device *dev); +static int __pm_request_resume(struct device *dev); + +/** + * pm_runtime_deactivate_timer - Deactivate given device's suspend timer. + * @dev: Device to handle. + */ +static void pm_runtime_deactivate_timer(struct device *dev) +{ + if (dev->power.timer_expires > 0) { + del_timer(&dev->power.suspend_timer); + dev->power.timer_expires = 0; + } +} + +/** + * pm_runtime_cancel_pending - Deactivate suspend timer and cancel requests. + * @dev: Device to handle. + */ +static void pm_runtime_cancel_pending(struct device *dev) +{ + pm_runtime_deactivate_timer(dev); + /* + * In case there's a request pending, make sure its work function will + * return without doing anything. + */ + dev->power.request = RPM_REQ_NONE; +} + +/** + * __pm_runtime_idle - Notify device bus type if the device can be suspended. + * @dev: Device to notify the bus type about. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_runtime_idle(struct device *dev) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ + int retval = 0; + + dev_dbg(dev, "__pm_runtime_idle()!\n"); + + if (dev->power.runtime_error) + retval = -EINVAL; + else if (dev->power.idle_notification) + retval = -EINPROGRESS; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0 + || dev->power.runtime_status != RPM_ACTIVE) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + goto out; + + if (dev->power.request_pending) { + /* + * If an idle notification request is pending, cancel it. Any + * other pending request takes precedence over us. + */ + if (dev->power.request == RPM_REQ_IDLE) { + dev->power.request = RPM_REQ_NONE; + } else if (dev->power.request != RPM_REQ_NONE) { + retval = -EAGAIN; + goto out; + } + } + + dev->power.idle_notification = true; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_idle) { + spin_unlock_irq(&dev->power.lock); + + dev->bus->pm->runtime_idle(dev); + + spin_lock_irq(&dev->power.lock); + } + + dev->power.idle_notification = false; + wake_up_all(&dev->power.wait_queue); + + out: + dev_dbg(dev, "__pm_runtime_idle() returns %d!\n", retval); + + return retval; +} + +/** + * pm_runtime_idle - Notify device bus type if the device can be suspended. + * @dev: Device to notify the bus type about. + */ +int pm_runtime_idle(struct device *dev) +{ + int retval; + + spin_lock_irq(&dev->power.lock); + retval = __pm_runtime_idle(dev); + spin_unlock_irq(&dev->power.lock); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_idle); + +/** + * __pm_runtime_suspend - Carry out run-time suspend of given device. + * @dev: Device to suspend. + * @from_wq: If set, the function has been called via pm_wq. + * + * Check if the device can be suspended and run the ->runtime_suspend() callback + * provided by its bus type. If another suspend has been started earlier, wait + * for it to finish. If an idle notification or suspend request is pending or + * scheduled, cancel it. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +int __pm_runtime_suspend(struct device *dev, bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ + struct device *parent = NULL; + bool notify = false; + int retval = 0; + + dev_dbg(dev, "__pm_runtime_suspend()%s!\n", + from_wq ? " from workqueue" : ""); + + repeat: + if (dev->power.runtime_error) { + retval = -EINVAL; + goto out; + } + + /* Pending resume requests take precedence over us. */ + if (dev->power.request_pending + && dev->power.request == RPM_REQ_RESUME) { + retval = -EAGAIN; + goto out; + } + + /* Other scheduled or pending requests need to be canceled. */ + pm_runtime_cancel_pending(dev); + + if (dev->power.runtime_status == RPM_SUSPENDED) + retval = 1; + else if (dev->power.runtime_status == RPM_RESUMING + || dev->power.disable_depth > 0 + || atomic_read(&dev->power.usage_count) > 0) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + goto out; + + if (dev->power.runtime_status == RPM_SUSPENDING) { + DEFINE_WAIT(wait); + + if (from_wq) { + retval = -EINPROGRESS; + goto out; + } + + /* Wait for the other suspend running in parallel with us. */ + for (;;) { + prepare_to_wait(&dev->power.wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + if (dev->power.runtime_status != RPM_SUSPENDING) + break; + + spin_unlock_irq(&dev->power.lock); + + schedule(); + + spin_lock_irq(&dev->power.lock); + } + finish_wait(&dev->power.wait_queue, &wait); + goto repeat; + } + + dev->power.runtime_status = RPM_SUSPENDING; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_suspend) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->bus->pm->runtime_suspend(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; + } else { + retval = -ENOSYS; + } + + if (retval) { + dev->power.runtime_status = RPM_ACTIVE; + pm_runtime_cancel_pending(dev); + dev->power.deferred_resume = false; + + if (retval == -EAGAIN || retval == -EBUSY) { + notify = true; + dev->power.runtime_error = 0; + } + } else { + dev->power.runtime_status = RPM_SUSPENDED; + + if (dev->parent) { + parent = dev->parent; + atomic_add_unless(&parent->power.child_count, -1, 0); + } + } + wake_up_all(&dev->power.wait_queue); + + if (dev->power.deferred_resume) { + dev->power.deferred_resume = false; + __pm_runtime_resume(dev, false); + retval = -EAGAIN; + goto out; + } + + if (notify) + __pm_runtime_idle(dev); + + if (parent && !parent->power.ignore_children) { + spin_unlock_irq(&dev->power.lock); + + pm_request_idle(parent); + + spin_lock_irq(&dev->power.lock); + } + + out: + dev_dbg(dev, "__pm_runtime_suspend() returns %d!\n", retval); + + return retval; +} + +/** + * pm_runtime_suspend - Carry out run-time suspend of given device. + * @dev: Device to suspend. + */ +int pm_runtime_suspend(struct device *dev) +{ + int retval; + + spin_lock_irq(&dev->power.lock); + retval = __pm_runtime_suspend(dev, false); + spin_unlock_irq(&dev->power.lock); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_suspend); + +/** + * __pm_runtime_resume - Carry out run-time resume of given device. + * @dev: Device to resume. + * @from_wq: If set, the function has been called via pm_wq. + * + * Check if the device can be woken up and run the ->runtime_resume() callback + * provided by its bus type. If another resume has been started earlier, wait + * for it to finish. If there's a suspend running in parallel with this + * function, wait for it to finish and resume the device. Cancel any scheduled + * or pending requests. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +int __pm_runtime_resume(struct device *dev, bool from_wq) + __releases(&dev->power.lock) __acquires(&dev->power.lock) +{ + struct device *parent = NULL; + int retval = 0; + + dev_dbg(dev, "__pm_runtime_resume()%s!\n", + from_wq ? " from workqueue" : ""); + + repeat: + if (dev->power.runtime_error) { + retval = -EINVAL; + goto out; + } + + pm_runtime_cancel_pending(dev); + + if (dev->power.runtime_status == RPM_ACTIVE) + retval = 1; + else if (dev->power.disable_depth > 0) + retval = -EAGAIN; + if (retval) + goto out; + + if (dev->power.runtime_status == RPM_RESUMING + || dev->power.runtime_status == RPM_SUSPENDING) { + DEFINE_WAIT(wait); + + if (from_wq) { + if (dev->power.runtime_status == RPM_SUSPENDING) + dev->power.deferred_resume = true; + retval = -EINPROGRESS; + goto out; + } + + /* Wait for the operation carried out in parallel with us. */ + for (;;) { + prepare_to_wait(&dev->power.wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + if (dev->power.runtime_status != RPM_RESUMING + && dev->power.runtime_status != RPM_SUSPENDING) + break; + + spin_unlock_irq(&dev->power.lock); + + schedule(); + + spin_lock_irq(&dev->power.lock); + } + finish_wait(&dev->power.wait_queue, &wait); + goto repeat; + } + + if (!parent && dev->parent) { + /* + * Increment the parent's resume counter and resume it if + * necessary. + */ + parent = dev->parent; + spin_unlock_irq(&dev->power.lock); + + pm_runtime_get_noresume(parent); + + spin_lock_irq(&parent->power.lock); + /* + * We can resume if the parent's run-time PM is disabled or it + * is set to ignore children. + */ + if (!parent->power.disable_depth + && !parent->power.ignore_children) { + __pm_runtime_resume(parent, false); + if (parent->power.runtime_status != RPM_ACTIVE) + retval = -EBUSY; + } + spin_unlock_irq(&parent->power.lock); + + spin_lock_irq(&dev->power.lock); + if (retval) + goto out; + goto repeat; + } + + dev->power.runtime_status = RPM_RESUMING; + + if (dev->bus && dev->bus->pm && dev->bus->pm->runtime_resume) { + spin_unlock_irq(&dev->power.lock); + + retval = dev->bus->pm->runtime_resume(dev); + + spin_lock_irq(&dev->power.lock); + dev->power.runtime_error = retval; + } else { + retval = -ENOSYS; + } + + if (retval) { + dev->power.runtime_status = RPM_SUSPENDED; + pm_runtime_cancel_pending(dev); + } else { + dev->power.runtime_status = RPM_ACTIVE; + if (parent) + atomic_inc(&parent->power.child_count); + } + wake_up_all(&dev->power.wait_queue); + + if (!retval) + __pm_request_idle(dev); + + out: + if (parent) { + spin_unlock_irq(&dev->power.lock); + + pm_runtime_put(parent); + + spin_lock_irq(&dev->power.lock); + } + + dev_dbg(dev, "__pm_runtime_resume() returns %d!\n", retval); + + return retval; +} + +/** + * pm_runtime_resume - Carry out run-time resume of given device. + * @dev: Device to suspend. + */ +int pm_runtime_resume(struct device *dev) +{ + int retval; + + spin_lock_irq(&dev->power.lock); + retval = __pm_runtime_resume(dev, false); + spin_unlock_irq(&dev->power.lock); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_resume); + +/** + * pm_runtime_work - Universal run-time PM work function. + * @work: Work structure used for scheduling the execution of this function. + * + * Use @work to get the device object the work is to be done for, determine what + * is to be done and execute the appropriate run-time PM function. + */ +static void pm_runtime_work(struct work_struct *work) +{ + struct device *dev = container_of(work, struct device, power.work); + enum rpm_request req; + + spin_lock_irq(&dev->power.lock); + + if (!dev->power.request_pending) + goto out; + + req = dev->power.request; + dev->power.request = RPM_REQ_NONE; + dev->power.request_pending = false; + + switch (req) { + case RPM_REQ_NONE: + break; + case RPM_REQ_IDLE: + __pm_runtime_idle(dev); + break; + case RPM_REQ_SUSPEND: + __pm_runtime_suspend(dev, true); + break; + case RPM_REQ_RESUME: + __pm_runtime_resume(dev, true); + break; + } + + out: + spin_unlock_irq(&dev->power.lock); +} + +/** + * __pm_request_idle - Submit an idle notification request for given device. + * @dev: Device to handle. + * + * Check if the device's run-time PM status is correct for suspending the device + * and queue up a request to run __pm_runtime_idle() for it. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_request_idle(struct device *dev) +{ + int retval = 0; + + if (dev->power.runtime_error) + retval = -EINVAL; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0 + || dev->power.runtime_status == RPM_SUSPENDED + || dev->power.runtime_status == RPM_SUSPENDING) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + return retval; + + if (dev->power.request_pending) { + /* Any requests other then RPM_REQ_IDLE take precedence. */ + if (dev->power.request == RPM_REQ_NONE) + dev->power.request = RPM_REQ_IDLE; + else if (dev->power.request != RPM_REQ_IDLE) + retval = -EAGAIN; + return retval; + } + + dev->power.request = RPM_REQ_IDLE; + dev->power.request_pending = true; + queue_work(pm_wq, &dev->power.work); + + return retval; +} + +/** + * pm_request_idle - Submit an idle notification request for given device. + * @dev: Device to handle. + */ +int pm_request_idle(struct device *dev) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&dev->power.lock, flags); + retval = __pm_request_idle(dev); + spin_unlock_irqrestore(&dev->power.lock, flags); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_request_idle); + +/** + * __pm_request_suspend - Submit a suspend request for given device. + * @dev: Device to suspend. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_request_suspend(struct device *dev) +{ + int retval = 0; + + if (dev->power.runtime_error) + return -EINVAL; + + if (dev->power.runtime_status == RPM_SUSPENDED) + retval = 1; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0) + retval = -EAGAIN; + else if (dev->power.runtime_status == RPM_SUSPENDING) + retval = -EINPROGRESS; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval < 0) + return retval; + + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + /* + * Pending resume requests take precedence over us, but we can + * overtake any other pending request. + */ + if (dev->power.request == RPM_REQ_RESUME) + retval = -EAGAIN; + else if (dev->power.request != RPM_REQ_SUSPEND) + dev->power.request = retval ? + RPM_REQ_NONE : RPM_REQ_SUSPEND; + return retval; + } else if (retval) { + return retval; + } + + dev->power.request = RPM_REQ_SUSPEND; + dev->power.request_pending = true; + queue_work(pm_wq, &dev->power.work); + + return 0; +} + +/** + * pm_suspend_timer_fn - Timer function for pm_schedule_suspend(). + * @data: Device pointer passed by pm_schedule_suspend(). + * + * Check if the time is right and execute __pm_request_suspend() in that case. + */ +static void pm_suspend_timer_fn(unsigned long data) +{ + struct device *dev = (struct device *)data; + unsigned long flags; + unsigned long expires; + + spin_lock_irqsave(&dev->power.lock, flags); + + expires = dev->power.timer_expires; + /* If 'expire' is after 'jiffies' we've been called too early. */ + if (expires > 0 && !time_after(expires, jiffies)) { + dev->power.timer_expires = 0; + __pm_request_suspend(dev); + } + + spin_unlock_irqrestore(&dev->power.lock, flags); +} + +/** + * pm_schedule_suspend - Set up a timer to submit a suspend request in future. + * @dev: Device to suspend. + * @delay: Time to wait before submitting a suspend request, in milliseconds. + */ +int pm_schedule_suspend(struct device *dev, unsigned int delay) +{ + unsigned long flags; + int retval = 0; + + spin_lock_irqsave(&dev->power.lock, flags); + + if (dev->power.runtime_error) { + retval = -EINVAL; + goto out; + } + + if (!delay) { + retval = __pm_request_suspend(dev); + goto out; + } + + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + /* + * Pending resume requests take precedence over us, but any + * other pending requests have to be canceled. + */ + if (dev->power.request == RPM_REQ_RESUME) { + retval = -EAGAIN; + goto out; + } + dev->power.request = RPM_REQ_NONE; + } + + if (dev->power.runtime_status == RPM_SUSPENDED) + retval = 1; + else if (dev->power.runtime_status == RPM_SUSPENDING) + retval = -EINPROGRESS; + else if (atomic_read(&dev->power.usage_count) > 0 + || dev->power.disable_depth > 0) + retval = -EAGAIN; + else if (!pm_children_suspended(dev)) + retval = -EBUSY; + if (retval) + goto out; + + dev->power.timer_expires = jiffies + msecs_to_jiffies(delay); + mod_timer(&dev->power.suspend_timer, dev->power.timer_expires); + + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_schedule_suspend); + +/** + * pm_request_resume - Submit a resume request for given device. + * @dev: Device to resume. + * + * This function must be called under dev->power.lock with interrupts disabled. + */ +static int __pm_request_resume(struct device *dev) +{ + int retval = 0; + + if (dev->power.runtime_error) + return -EINVAL; + + if (dev->power.runtime_status == RPM_ACTIVE) + retval = 1; + else if (dev->power.runtime_status == RPM_RESUMING) + retval = -EINPROGRESS; + else if (dev->power.disable_depth > 0) + retval = -EAGAIN; + if (retval < 0) + return retval; + + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + /* If non-resume request is pending, we can overtake it. */ + dev->power.request = retval ? RPM_REQ_NONE : RPM_REQ_RESUME; + return retval; + } else if (retval) { + return retval; + } + + dev->power.request = RPM_REQ_RESUME; + dev->power.request_pending = true; + queue_work(pm_wq, &dev->power.work); + + return retval; +} + +/** + * pm_request_resume - Submit a resume request for given device. + * @dev: Device to resume. + */ +int pm_request_resume(struct device *dev) +{ + unsigned long flags; + int retval; + + spin_lock_irqsave(&dev->power.lock, flags); + retval = __pm_request_resume(dev); + spin_unlock_irqrestore(&dev->power.lock, flags); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_request_resume); + +/** + * __pm_runtime_get - Reference count a device and wake it up, if necessary. + * @dev: Device to handle. + * @sync: If set and the device is suspended, resume it synchronously. + * + * Increment the usage count of the device and if it was zero previously, + * resume it or submit a resume request for it, depending on the value of @sync. + */ +int __pm_runtime_get(struct device *dev, bool sync) +{ + int retval = 1; + + if (atomic_add_return(1, &dev->power.usage_count) == 1) + retval = sync ? pm_runtime_resume(dev) : pm_request_resume(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(__pm_runtime_get); + +/** + * __pm_runtime_put - Decrement the device's usage counter and notify its bus. + * @dev: Device to handle. + * @sync: If the device's bus type is to be notified, do that synchronously. + * + * Decrement the usage count of the device and if it reaches zero, carry out a + * synchronous idle notification or submit an idle notification request for it, + * depending on the value of @sync. + */ +int __pm_runtime_put(struct device *dev, bool sync) +{ + int retval = 0; + + if (atomic_dec_and_test(&dev->power.usage_count)) + retval = sync ? pm_runtime_idle(dev) : pm_request_idle(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(__pm_runtime_put); + +/** + * __pm_runtime_set_status - Set run-time PM status of a device. + * @dev: Device to handle. + * @status: New run-time PM status of the device. + * + * If run-time PM of the device is disabled or its power.runtime_error field is + * different from zero, the status may be changed either to RPM_ACTIVE, or to + * RPM_SUSPENDED, as long as that reflects the actual state of the device. + * However, if the device has a parent and the parent is not active, and the + * parent's power.ignore_children flag is unset, the device's status cannot be + * set to RPM_ACTIVE, so -EBUSY is returned in that case. + * + * If successful, __pm_runtime_set_status() clears the power.runtime_error field + * and the device parent's counter of unsuspended children is modified to + * reflect the new status. If the new status is RPM_SUSPENDED, an idle + * notification request for the parent is submitted. + */ +int __pm_runtime_set_status(struct device *dev, unsigned int status) +{ + struct device *parent = dev->parent; + unsigned long flags; + bool notify_parent = false; + int error = 0; + + if (status != RPM_ACTIVE && status != RPM_SUSPENDED) + return -EINVAL; + + spin_lock_irqsave(&dev->power.lock, flags); + + if (!dev->power.runtime_error && !dev->power.disable_depth) { + error = -EAGAIN; + goto out; + } + + if (dev->power.runtime_status == status) + goto out_set; + + if (status == RPM_SUSPENDED) { + /* It always is possible to set the status to 'suspended'. */ + if (parent) { + atomic_add_unless(&parent->power.child_count, -1, 0); + notify_parent = !parent->power.ignore_children; + } + goto out_set; + } + + if (parent) { + spin_lock_irq(&parent->power.lock); + + /* + * It is invalid to put an active child under a parent that is + * not active, has run-time PM enabled and the + * 'power.ignore_children' flag unset. + */ + if (!parent->power.disable_depth + && !parent->power.ignore_children + && parent->power.runtime_status != RPM_ACTIVE) { + error = -EBUSY; + } else { + if (dev->power.runtime_status == RPM_SUSPENDED) + atomic_inc(&parent->power.child_count); + } + + spin_unlock_irq(&parent->power.lock); + + if (error) + goto out; + } + + out_set: + dev->power.runtime_status = status; + dev->power.runtime_error = 0; + out: + spin_unlock_irqrestore(&dev->power.lock, flags); + + if (notify_parent) + pm_request_idle(parent); + + return error; +} +EXPORT_SYMBOL_GPL(__pm_runtime_set_status); + +/** + * __pm_runtime_barrier - Cancel pending requests and wait for completions. + * @dev: Device to handle. + * + * Flush all pending requests for the device from pm_wq and wait for all + * run-time PM operations involving the device in progress to complete. + * + * Should be called under dev->power.lock with interrupts disabled. + */ +static void __pm_runtime_barrier(struct device *dev) +{ + pm_runtime_deactivate_timer(dev); + + if (dev->power.request_pending) { + dev->power.request = RPM_REQ_NONE; + spin_unlock_irq(&dev->power.lock); + + cancel_work_sync(&dev->power.work); + + spin_lock_irq(&dev->power.lock); + dev->power.request_pending = false; + } + + if (dev->power.runtime_status == RPM_SUSPENDING + || dev->power.runtime_status == RPM_RESUMING + || dev->power.idle_notification) { + DEFINE_WAIT(wait); + + /* Suspend, wake-up or idle notification in progress. */ + for (;;) { + prepare_to_wait(&dev->power.wait_queue, &wait, + TASK_UNINTERRUPTIBLE); + if (dev->power.runtime_status != RPM_SUSPENDING + && dev->power.runtime_status != RPM_RESUMING + && !dev->power.idle_notification) + break; + spin_unlock_irq(&dev->power.lock); + + schedule(); + + spin_lock_irq(&dev->power.lock); + } + finish_wait(&dev->power.wait_queue, &wait); + } +} + +/** + * pm_runtime_barrier - Flush pending requests and wait for completions. + * @dev: Device to handle. + * + * Prevent the device from being suspended by incrementing its usage counter and + * if there's a pending resume request for the device, wake the device up. + * Next, make sure that all pending requests for the device have been flushed + * from pm_wq and wait for all run-time PM operations involving the device in + * progress to complete. + * + * Return value: + * 1, if there was a resume request pending and the device had to be woken up, + * 0, otherwise + */ +int pm_runtime_barrier(struct device *dev) +{ + int retval = 0; + + pm_runtime_get_noresume(dev); + spin_lock_irq(&dev->power.lock); + + if (dev->power.request_pending + && dev->power.request == RPM_REQ_RESUME) { + __pm_runtime_resume(dev, false); + retval = 1; + } + + __pm_runtime_barrier(dev); + + spin_unlock_irq(&dev->power.lock); + pm_runtime_put_noidle(dev); + + return retval; +} +EXPORT_SYMBOL_GPL(pm_runtime_barrier); + +/** + * __pm_runtime_disable - Disable run-time PM of a device. + * @dev: Device to handle. + * @check_resume: If set, check if there's a resume request for the device. + * + * Increment power.disable_depth for the device and if was zero previously, + * cancel all pending run-time PM requests for the device and wait for all + * operations in progress to complete. The device can be either active or + * suspended after its run-time PM has been disabled. + * + * If @check_resume is set and there's a resume request pending when + * __pm_runtime_disable() is called and power.disable_depth is zero, the + * function will wake up the device before disabling its run-time PM. + */ +void __pm_runtime_disable(struct device *dev, bool check_resume) +{ + spin_lock_irq(&dev->power.lock); + + if (dev->power.disable_depth > 0) { + dev->power.disable_depth++; + goto out; + } + + /* + * Wake up the device if there's a resume request pending, because that + * means there probably is some I/O to process and disabling run-time PM + * shouldn't prevent the device from processing the I/O. + */ + if (check_resume && dev->power.request_pending + && dev->power.request == RPM_REQ_RESUME) { + /* + * Prevent suspends and idle notifications from being carried + * out after we have woken up the device. + */ + pm_runtime_get_noresume(dev); + + __pm_runtime_resume(dev, false); + + pm_runtime_put_noidle(dev); + } + + if (!dev->power.disable_depth++) + __pm_runtime_barrier(dev); + + out: + spin_unlock_irq(&dev->power.lock); +} +EXPORT_SYMBOL_GPL(__pm_runtime_disable); + +/** + * pm_runtime_enable - Enable run-time PM of a device. + * @dev: Device to handle. + */ +void pm_runtime_enable(struct device *dev) +{ + unsigned long flags; + + spin_lock_irqsave(&dev->power.lock, flags); + + if (dev->power.disable_depth > 0) + dev->power.disable_depth--; + else + dev_warn(dev, "Unbalanced %s!\n", __func__); + + spin_unlock_irqrestore(&dev->power.lock, flags); +} +EXPORT_SYMBOL_GPL(pm_runtime_enable); + +/** + * pm_runtime_init - Initialize run-time PM fields in given device object. + * @dev: Device object to initialize. + */ +void pm_runtime_init(struct device *dev) +{ + spin_lock_init(&dev->power.lock); + + dev->power.runtime_status = RPM_SUSPENDED; + dev->power.idle_notification = false; + + dev->power.disable_depth = 1; + atomic_set(&dev->power.usage_count, 0); + + dev->power.runtime_error = 0; + + atomic_set(&dev->power.child_count, 0); + pm_suspend_ignore_children(dev, false); + + dev->power.request_pending = false; + dev->power.request = RPM_REQ_NONE; + dev->power.deferred_resume = false; + INIT_WORK(&dev->power.work, pm_runtime_work); + + dev->power.timer_expires = 0; + setup_timer(&dev->power.suspend_timer, pm_suspend_timer_fn, + (unsigned long)dev); + + init_waitqueue_head(&dev->power.wait_queue); +} + +/** + * pm_runtime_remove - Prepare for removing a device from device hierarchy. + * @dev: Device object being removed from device hierarchy. + */ +void pm_runtime_remove(struct device *dev) +{ + __pm_runtime_disable(dev, false); + + /* Change the status back to 'suspended' to match the initial status. */ + if (dev->power.runtime_status == RPM_ACTIVE) + pm_runtime_set_suspended(dev); +} diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 98c9a847bf5..933c143b6a7 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -1399,8 +1399,9 @@ static void dw_shutdown(struct platform_device *pdev) clk_disable(dw->clk); } -static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg) +static int dw_suspend_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); dw_dma_off(platform_get_drvdata(pdev)); @@ -1408,23 +1409,27 @@ static int dw_suspend_late(struct platform_device *pdev, pm_message_t mesg) return 0; } -static int dw_resume_early(struct platform_device *pdev) +static int dw_resume_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct dw_dma *dw = platform_get_drvdata(pdev); clk_enable(dw->clk); dma_writel(dw, CFG, DW_CFG_DMA_EN); return 0; - } +static struct dev_pm_ops dw_dev_pm_ops = { + .suspend_noirq = dw_suspend_noirq, + .resume_noirq = dw_resume_noirq, +}; + static struct platform_driver dw_driver = { .remove = __exit_p(dw_remove), .shutdown = dw_shutdown, - .suspend_late = dw_suspend_late, - .resume_early = dw_resume_early, .driver = { .name = "dw_dmac", + .pm = &dw_dev_pm_ops, }, }; diff --git a/drivers/dma/txx9dmac.c b/drivers/dma/txx9dmac.c index 88dab52926f..7837930146a 100644 --- a/drivers/dma/txx9dmac.c +++ b/drivers/dma/txx9dmac.c @@ -1291,17 +1291,18 @@ static void txx9dmac_shutdown(struct platform_device *pdev) txx9dmac_off(ddev); } -static int txx9dmac_suspend_late(struct platform_device *pdev, - pm_message_t mesg) +static int txx9dmac_suspend_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct txx9dmac_dev *ddev = platform_get_drvdata(pdev); txx9dmac_off(ddev); return 0; } -static int txx9dmac_resume_early(struct platform_device *pdev) +static int txx9dmac_resume_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct txx9dmac_dev *ddev = platform_get_drvdata(pdev); struct txx9dmac_platform_data *pdata = pdev->dev.platform_data; u32 mcr; @@ -1314,6 +1315,11 @@ static int txx9dmac_resume_early(struct platform_device *pdev) } +static struct dev_pm_ops txx9dmac_dev_pm_ops = { + .suspend_noirq = txx9dmac_suspend_noirq, + .resume_noirq = txx9dmac_resume_noirq, +}; + static struct platform_driver txx9dmac_chan_driver = { .remove = __exit_p(txx9dmac_chan_remove), .driver = { @@ -1324,10 +1330,9 @@ static struct platform_driver txx9dmac_chan_driver = { static struct platform_driver txx9dmac_driver = { .remove = __exit_p(txx9dmac_remove), .shutdown = txx9dmac_shutdown, - .suspend_late = txx9dmac_suspend_late, - .resume_early = txx9dmac_resume_early, .driver = { .name = "txx9dmac", + .pm = &txx9dmac_dev_pm_ops, }, }; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 33be210d672..2f631c75f70 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -258,31 +258,6 @@ void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type) EXPORT_SYMBOL(drm_mode_object_find); /** - * drm_crtc_from_fb - find the CRTC structure associated with an fb - * @dev: DRM device - * @fb: framebuffer in question - * - * LOCKING: - * Caller must hold mode_config lock. - * - * Find CRTC in the mode_config structure that matches @fb. - * - * RETURNS: - * Pointer to the CRTC or NULL if it wasn't found. - */ -struct drm_crtc *drm_crtc_from_fb(struct drm_device *dev, - struct drm_framebuffer *fb) -{ - struct drm_crtc *crtc; - - list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb == fb) - return crtc; - } - return NULL; -} - -/** * drm_framebuffer_init - initialize a framebuffer * @dev: DRM device * @@ -328,11 +303,20 @@ void drm_framebuffer_cleanup(struct drm_framebuffer *fb) { struct drm_device *dev = fb->dev; struct drm_crtc *crtc; + struct drm_mode_set set; + int ret; /* remove from any CRTC */ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { - if (crtc->fb == fb) - crtc->fb = NULL; + if (crtc->fb == fb) { + /* should turn off the crtc */ + memset(&set, 0, sizeof(struct drm_mode_set)); + set.crtc = crtc; + set.fb = NULL; + ret = crtc->funcs->set_config(&set); + if (ret) + DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); + } } drm_mode_object_put(dev, &fb->base); @@ -1511,7 +1495,7 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, set.mode = mode; set.connectors = connector_set; set.num_connectors = crtc_req->count_connectors; - set.fb =fb; + set.fb = fb; ret = crtc->funcs->set_config(&set); out: diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index 80cc6d06d61..7f2728bbc16 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -502,12 +502,40 @@ static int add_detailed_info(struct drm_connector *connector, struct detailed_non_pixel *data = &timing->data.other_data; struct drm_display_mode *newmode; - /* EDID up to and including 1.2 may put monitor info here */ - if (edid->version == 1 && edid->revision < 3) - continue; - - /* Detailed mode timing */ - if (timing->pixel_clock) { + /* X server check is version 1.1 or higher */ + if (edid->version == 1 && edid->revision >= 1 && + !timing->pixel_clock) { + /* Other timing or info */ + switch (data->type) { + case EDID_DETAIL_MONITOR_SERIAL: + break; + case EDID_DETAIL_MONITOR_STRING: + break; + case EDID_DETAIL_MONITOR_RANGE: + /* Get monitor range data */ + break; + case EDID_DETAIL_MONITOR_NAME: + break; + case EDID_DETAIL_MONITOR_CPDATA: + break; + case EDID_DETAIL_STD_MODES: + /* Five modes per detailed section */ + for (j = 0; j < 5; i++) { + struct std_timing *std; + struct drm_display_mode *newmode; + + std = &data->data.timings[j]; + newmode = drm_mode_std(dev, std); + if (newmode) { + drm_mode_probed_add(connector, newmode); + modes++; + } + } + break; + default: + break; + } + } else { newmode = drm_mode_detailed(dev, edid, timing, quirks); if (!newmode) continue; @@ -518,38 +546,6 @@ static int add_detailed_info(struct drm_connector *connector, drm_mode_probed_add(connector, newmode); modes++; - continue; - } - - /* Other timing or info */ - switch (data->type) { - case EDID_DETAIL_MONITOR_SERIAL: - break; - case EDID_DETAIL_MONITOR_STRING: - break; - case EDID_DETAIL_MONITOR_RANGE: - /* Get monitor range data */ - break; - case EDID_DETAIL_MONITOR_NAME: - break; - case EDID_DETAIL_MONITOR_CPDATA: - break; - case EDID_DETAIL_STD_MODES: - /* Five modes per detailed section */ - for (j = 0; j < 5; i++) { - struct std_timing *std; - struct drm_display_mode *newmode; - - std = &data->data.timings[j]; - newmode = drm_mode_std(dev, std); - if (newmode) { - drm_mode_probed_add(connector, newmode); - modes++; - } - } - break; - default: - break; } } diff --git a/drivers/gpu/drm/drm_sysfs.c b/drivers/gpu/drm/drm_sysfs.c index 85ec31b3ff0..f7a615b80c7 100644 --- a/drivers/gpu/drm/drm_sysfs.c +++ b/drivers/gpu/drm/drm_sysfs.c @@ -22,44 +22,50 @@ #define to_drm_minor(d) container_of(d, struct drm_minor, kdev) #define to_drm_connector(d) container_of(d, struct drm_connector, kdev) +static struct device_type drm_sysfs_device_minor = { + .name = "drm_minor" +}; + /** - * drm_sysfs_suspend - DRM class suspend hook + * drm_class_suspend - DRM class suspend hook * @dev: Linux device to suspend * @state: power state to enter * * Just figures out what the actual struct drm_device associated with * @dev is and calls its suspend hook, if present. */ -static int drm_sysfs_suspend(struct device *dev, pm_message_t state) +static int drm_class_suspend(struct device *dev, pm_message_t state) { - struct drm_minor *drm_minor = to_drm_minor(dev); - struct drm_device *drm_dev = drm_minor->dev; - - if (drm_minor->type == DRM_MINOR_LEGACY && - !drm_core_check_feature(drm_dev, DRIVER_MODESET) && - drm_dev->driver->suspend) - return drm_dev->driver->suspend(drm_dev, state); - + if (dev->type == &drm_sysfs_device_minor) { + struct drm_minor *drm_minor = to_drm_minor(dev); + struct drm_device *drm_dev = drm_minor->dev; + + if (drm_minor->type == DRM_MINOR_LEGACY && + !drm_core_check_feature(drm_dev, DRIVER_MODESET) && + drm_dev->driver->suspend) + return drm_dev->driver->suspend(drm_dev, state); + } return 0; } /** - * drm_sysfs_resume - DRM class resume hook + * drm_class_resume - DRM class resume hook * @dev: Linux device to resume * * Just figures out what the actual struct drm_device associated with * @dev is and calls its resume hook, if present. */ -static int drm_sysfs_resume(struct device *dev) +static int drm_class_resume(struct device *dev) { - struct drm_minor *drm_minor = to_drm_minor(dev); - struct drm_device *drm_dev = drm_minor->dev; - - if (drm_minor->type == DRM_MINOR_LEGACY && - !drm_core_check_feature(drm_dev, DRIVER_MODESET) && - drm_dev->driver->resume) - return drm_dev->driver->resume(drm_dev); - + if (dev->type == &drm_sysfs_device_minor) { + struct drm_minor *drm_minor = to_drm_minor(dev); + struct drm_device *drm_dev = drm_minor->dev; + + if (drm_minor->type == DRM_MINOR_LEGACY && + !drm_core_check_feature(drm_dev, DRIVER_MODESET) && + drm_dev->driver->resume) + return drm_dev->driver->resume(drm_dev); + } return 0; } @@ -99,8 +105,8 @@ struct class *drm_sysfs_create(struct module *owner, char *name) goto err_out; } - class->suspend = drm_sysfs_suspend; - class->resume = drm_sysfs_resume; + class->suspend = drm_class_suspend; + class->resume = drm_class_resume; err = class_create_file(class, &class_attr_version); if (err) @@ -480,6 +486,7 @@ int drm_sysfs_device_add(struct drm_minor *minor) minor->kdev.class = drm_class; minor->kdev.release = drm_sysfs_device_release; minor->kdev.devt = minor->device; + minor->kdev.type = &drm_sysfs_device_minor; if (minor->type == DRM_MINOR_CONTROL) minor_str = "controlD%d"; else if (minor->type == DRM_MINOR_RENDER) diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c index f1ba8ff4113..68e728e8be4 100644 --- a/drivers/gpu/drm/radeon/r100.c +++ b/drivers/gpu/drm/radeon/r100.c @@ -254,6 +254,72 @@ void r100_mc_fini(struct radeon_device *rdev) /* + * Interrupts + */ +int r100_irq_set(struct radeon_device *rdev) +{ + uint32_t tmp = 0; + + if (rdev->irq.sw_int) { + tmp |= RADEON_SW_INT_ENABLE; + } + if (rdev->irq.crtc_vblank_int[0]) { + tmp |= RADEON_CRTC_VBLANK_MASK; + } + if (rdev->irq.crtc_vblank_int[1]) { + tmp |= RADEON_CRTC2_VBLANK_MASK; + } + WREG32(RADEON_GEN_INT_CNTL, tmp); + return 0; +} + +static inline uint32_t r100_irq_ack(struct radeon_device *rdev) +{ + uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS); + uint32_t irq_mask = RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT | + RADEON_CRTC2_VBLANK_STAT; + + if (irqs) { + WREG32(RADEON_GEN_INT_STATUS, irqs); + } + return irqs & irq_mask; +} + +int r100_irq_process(struct radeon_device *rdev) +{ + uint32_t status; + + status = r100_irq_ack(rdev); + if (!status) { + return IRQ_NONE; + } + while (status) { + /* SW interrupt */ + if (status & RADEON_SW_INT_TEST) { + radeon_fence_process(rdev); + } + /* Vertical blank interrupts */ + if (status & RADEON_CRTC_VBLANK_STAT) { + drm_handle_vblank(rdev->ddev, 0); + } + if (status & RADEON_CRTC2_VBLANK_STAT) { + drm_handle_vblank(rdev->ddev, 1); + } + status = r100_irq_ack(rdev); + } + return IRQ_HANDLED; +} + +u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc) +{ + if (crtc == 0) + return RREG32(RADEON_CRTC_CRNT_FRAME); + else + return RREG32(RADEON_CRTC2_CRNT_FRAME); +} + + +/* * Fence emission */ void r100_fence_ring_emit(struct radeon_device *rdev, @@ -1025,6 +1091,16 @@ static int r100_packet0_check(struct radeon_cs_parser *p, tmp |= tile_flags; ib[idx] = tmp; break; + case RADEON_RB3D_ZPASS_ADDR: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + break; default: /* FIXME: we don't want to allow anyothers packet */ break; @@ -1556,26 +1632,6 @@ void r100_pll_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) r100_pll_errata_after_data(rdev); } -uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg) -{ - if (reg < 0x10000) - return readl(((void __iomem *)rdev->rmmio) + reg); - else { - writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); - return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); - } -} - -void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) -{ - if (reg < 0x10000) - writel(v, ((void __iomem *)rdev->rmmio) + reg); - else { - writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); - writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); - } -} - int r100_init(struct radeon_device *rdev) { return 0; diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c index 9c8d41534a5..053f4ec397f 100644 --- a/drivers/gpu/drm/radeon/r300.c +++ b/drivers/gpu/drm/radeon/r300.c @@ -83,8 +83,8 @@ void rv370_pcie_gart_tlb_flush(struct radeon_device *rdev) WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp | RADEON_PCIE_TX_GART_INVALIDATE_TLB); (void)RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL); WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp); - mb(); } + mb(); } int rv370_pcie_gart_enable(struct radeon_device *rdev) @@ -448,6 +448,7 @@ void r300_gpu_init(struct radeon_device *rdev) /* rv350,rv370,rv380 */ rdev->num_gb_pipes = 1; } + rdev->num_z_pipes = 1; gb_tile_config = (R300_ENABLE_TILING | R300_TILE_SIZE_16); switch (rdev->num_gb_pipes) { case 2: @@ -486,7 +487,8 @@ void r300_gpu_init(struct radeon_device *rdev) printk(KERN_WARNING "Failed to wait MC idle while " "programming pipes. Bad things might happen.\n"); } - DRM_INFO("radeon: %d pipes initialized.\n", rdev->num_gb_pipes); + DRM_INFO("radeon: %d quad pipes, %d Z pipes initialized.\n", + rdev->num_gb_pipes, rdev->num_z_pipes); } int r300_ga_reset(struct radeon_device *rdev) @@ -593,27 +595,6 @@ void r300_vram_info(struct radeon_device *rdev) /* - * Indirect registers accessor - */ -uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg) -{ - uint32_t r; - - WREG8(RADEON_PCIE_INDEX, ((reg) & 0xff)); - (void)RREG32(RADEON_PCIE_INDEX); - r = RREG32(RADEON_PCIE_DATA); - return r; -} - -void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) -{ - WREG8(RADEON_PCIE_INDEX, ((reg) & 0xff)); - (void)RREG32(RADEON_PCIE_INDEX); - WREG32(RADEON_PCIE_DATA, (v)); - (void)RREG32(RADEON_PCIE_DATA); -} - -/* * PCIE Lanes */ @@ -1403,6 +1384,21 @@ static int r300_packet0_check(struct radeon_cs_parser *p, tmp = (ib_chunk->kdata[idx] >> 22) & 0xF; track->textures[i].txdepth = tmp; break; + case R300_ZB_ZPASS_ADDR: + r = r100_cs_packet_next_reloc(p, &reloc); + if (r) { + DRM_ERROR("No reloc for ib[%d]=0x%04X\n", + idx, reg); + r100_cs_dump_packet(p, pkt); + return r; + } + ib[idx] = ib_chunk->kdata[idx] + ((u32)reloc->lobj.gpu_offset); + break; + case 0x4be8: + /* valid register only on RV530 */ + if (p->rdev->family == CHIP_RV530) + break; + /* fallthrough do not move */ default: printk(KERN_ERR "Forbidden register 0x%04X in cs at %d\n", reg, idx); diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c index dea497a979f..97426a6f370 100644 --- a/drivers/gpu/drm/radeon/r420.c +++ b/drivers/gpu/drm/radeon/r420.c @@ -165,7 +165,18 @@ void r420_pipes_init(struct radeon_device *rdev) printk(KERN_WARNING "Failed to wait GUI idle while " "programming pipes. Bad things might happen.\n"); } - DRM_INFO("radeon: %d pipes initialized.\n", rdev->num_gb_pipes); + + if (rdev->family == CHIP_RV530) { + tmp = RREG32(RV530_GB_PIPE_SELECT2); + if ((tmp & 3) == 3) + rdev->num_z_pipes = 2; + else + rdev->num_z_pipes = 1; + } else + rdev->num_z_pipes = 1; + + DRM_INFO("radeon: %d quad pipes, %d z pipes initialized.\n", + rdev->num_gb_pipes, rdev->num_z_pipes); } void r420_gpu_init(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h index 036691b38cb..e1d5e0331e1 100644 --- a/drivers/gpu/drm/radeon/r500_reg.h +++ b/drivers/gpu/drm/radeon/r500_reg.h @@ -350,6 +350,7 @@ #define AVIVO_D1CRTC_BLANK_CONTROL 0x6084 #define AVIVO_D1CRTC_INTERLACE_CONTROL 0x6088 #define AVIVO_D1CRTC_INTERLACE_STATUS 0x608c +#define AVIVO_D1CRTC_FRAME_COUNT 0x60a4 #define AVIVO_D1CRTC_STEREO_CONTROL 0x60c4 /* master controls */ @@ -438,14 +439,15 @@ # define AVIVO_DC_LB_DISP1_END_ADR_SHIFT 4 # define AVIVO_DC_LB_DISP1_END_ADR_MASK 0x7ff -#define R500_DxMODE_INT_MASK 0x6540 -#define R500_D1MODE_INT_MASK (1<<0) -#define R500_D2MODE_INT_MASK (1<<8) - #define AVIVO_D1MODE_DATA_FORMAT 0x6528 # define AVIVO_D1MODE_INTERLEAVE_EN (1 << 0) #define AVIVO_D1MODE_DESKTOP_HEIGHT 0x652C +#define AVIVO_D1MODE_VBLANK_STATUS 0x6534 +# define AVIVO_VBLANK_ACK (1 << 4) #define AVIVO_D1MODE_VLINE_START_END 0x6538 +#define AVIVO_DxMODE_INT_MASK 0x6540 +# define AVIVO_D1MODE_INT_MASK (1 << 0) +# define AVIVO_D2MODE_INT_MASK (1 << 8) #define AVIVO_D1MODE_VIEWPORT_START 0x6580 #define AVIVO_D1MODE_VIEWPORT_SIZE 0x6584 #define AVIVO_D1MODE_EXT_OVERSCAN_LEFT_RIGHT 0x6588 @@ -475,6 +477,7 @@ #define AVIVO_D2CRTC_BLANK_CONTROL 0x6884 #define AVIVO_D2CRTC_INTERLACE_CONTROL 0x6888 #define AVIVO_D2CRTC_INTERLACE_STATUS 0x688c +#define AVIVO_D2CRTC_FRAME_COUNT 0x68a4 #define AVIVO_D2CRTC_STEREO_CONTROL 0x68c4 #define AVIVO_D2GRPH_ENABLE 0x6900 @@ -497,6 +500,7 @@ #define AVIVO_D2CUR_SIZE 0x6c10 #define AVIVO_D2CUR_POSITION 0x6c14 +#define AVIVO_D2MODE_VBLANK_STATUS 0x6d34 #define AVIVO_D2MODE_VLINE_START_END 0x6d38 #define AVIVO_D2MODE_VIEWPORT_START 0x6d80 #define AVIVO_D2MODE_VIEWPORT_SIZE 0x6d84 @@ -748,4 +752,8 @@ # define AVIVO_I2C_EN (1 << 0) # define AVIVO_I2C_RESET (1 << 8) +#define AVIVO_DISP_INTERRUPT_STATUS 0x7edc +# define AVIVO_D1_VBLANK_INTERRUPT (1 << 4) +# define AVIVO_D2_VBLANK_INTERRUPT (1 << 5) + #endif diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c index 09fb0b6ec7d..ebd6b0f7bdf 100644 --- a/drivers/gpu/drm/radeon/r520.c +++ b/drivers/gpu/drm/radeon/r520.c @@ -177,7 +177,6 @@ void r520_gpu_init(struct radeon_device *rdev) */ /* workaround for RV530 */ if (rdev->family == CHIP_RV530) { - WREG32(0x4124, 1); WREG32(0x4128, 0xFF); } r420_pipes_init(rdev); diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index b1d945b8ed6..b519fb2fecb 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h @@ -242,6 +242,7 @@ int radeon_object_pin(struct radeon_object *robj, uint32_t domain, uint64_t *gpu_addr); void radeon_object_unpin(struct radeon_object *robj); int radeon_object_wait(struct radeon_object *robj); +int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement); int radeon_object_evict_vram(struct radeon_device *rdev); int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset); void radeon_object_force_delete(struct radeon_device *rdev); @@ -574,6 +575,7 @@ struct radeon_asic { void (*ring_start)(struct radeon_device *rdev); int (*irq_set)(struct radeon_device *rdev); int (*irq_process)(struct radeon_device *rdev); + u32 (*get_vblank_counter)(struct radeon_device *rdev, int crtc); void (*fence_ring_emit)(struct radeon_device *rdev, struct radeon_fence *fence); int (*cs_parse)(struct radeon_cs_parser *p); int (*copy_blit)(struct radeon_device *rdev, @@ -653,6 +655,7 @@ struct radeon_device { int usec_timeout; enum radeon_pll_errata pll_errata; int num_gb_pipes; + int num_z_pipes; int disp_priority; /* BIOS */ uint8_t *bios; @@ -666,14 +669,11 @@ struct radeon_device { resource_size_t rmmio_base; resource_size_t rmmio_size; void *rmmio; - radeon_rreg_t mm_rreg; - radeon_wreg_t mm_wreg; radeon_rreg_t mc_rreg; radeon_wreg_t mc_wreg; radeon_rreg_t pll_rreg; radeon_wreg_t pll_wreg; - radeon_rreg_t pcie_rreg; - radeon_wreg_t pcie_wreg; + uint32_t pcie_reg_mask; radeon_rreg_t pciep_rreg; radeon_wreg_t pciep_wreg; struct radeon_clock clock; @@ -705,22 +705,42 @@ int radeon_device_init(struct radeon_device *rdev, void radeon_device_fini(struct radeon_device *rdev); int radeon_gpu_wait_for_idle(struct radeon_device *rdev); +static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg) +{ + if (reg < 0x10000) + return readl(((void __iomem *)rdev->rmmio) + reg); + else { + writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); + return readl(((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); + } +} + +static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + if (reg < 0x10000) + writel(v, ((void __iomem *)rdev->rmmio) + reg); + else { + writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX); + writel(v, ((void __iomem *)rdev->rmmio) + RADEON_MM_DATA); + } +} + /* * Registers read & write functions. */ #define RREG8(reg) readb(((void __iomem *)rdev->rmmio) + (reg)) #define WREG8(reg, v) writeb(v, ((void __iomem *)rdev->rmmio) + (reg)) -#define RREG32(reg) rdev->mm_rreg(rdev, (reg)) -#define WREG32(reg, v) rdev->mm_wreg(rdev, (reg), (v)) +#define RREG32(reg) r100_mm_rreg(rdev, (reg)) +#define WREG32(reg, v) r100_mm_wreg(rdev, (reg), (v)) #define REG_SET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK) #define REG_GET(FIELD, v) (((v) << FIELD##_SHIFT) & FIELD##_MASK) #define RREG32_PLL(reg) rdev->pll_rreg(rdev, (reg)) #define WREG32_PLL(reg, v) rdev->pll_wreg(rdev, (reg), (v)) #define RREG32_MC(reg) rdev->mc_rreg(rdev, (reg)) #define WREG32_MC(reg, v) rdev->mc_wreg(rdev, (reg), (v)) -#define RREG32_PCIE(reg) rdev->pcie_rreg(rdev, (reg)) -#define WREG32_PCIE(reg, v) rdev->pcie_wreg(rdev, (reg), (v)) +#define RREG32_PCIE(reg) rv370_pcie_rreg(rdev, (reg)) +#define WREG32_PCIE(reg, v) rv370_pcie_wreg(rdev, (reg), (v)) #define WREG32_P(reg, val, mask) \ do { \ uint32_t tmp_ = RREG32(reg); \ @@ -736,6 +756,24 @@ int radeon_gpu_wait_for_idle(struct radeon_device *rdev); WREG32_PLL(reg, tmp_); \ } while (0) +/* + * Indirect registers accessor + */ +static inline uint32_t rv370_pcie_rreg(struct radeon_device *rdev, uint32_t reg) +{ + uint32_t r; + + WREG32(RADEON_PCIE_INDEX, ((reg) & rdev->pcie_reg_mask)); + r = RREG32(RADEON_PCIE_DATA); + return r; +} + +static inline void rv370_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) +{ + WREG32(RADEON_PCIE_INDEX, ((reg) & rdev->pcie_reg_mask)); + WREG32(RADEON_PCIE_DATA, (v)); +} + void r100_pll_errata_after_index(struct radeon_device *rdev); @@ -862,6 +900,7 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v) #define radeon_ring_start(rdev) (rdev)->asic->ring_start((rdev)) #define radeon_irq_set(rdev) (rdev)->asic->irq_set((rdev)) #define radeon_irq_process(rdev) (rdev)->asic->irq_process((rdev)) +#define radeon_get_vblank_counter(rdev, crtc) (rdev)->asic->get_vblank_counter((rdev), (crtc)) #define radeon_fence_ring_emit(rdev, fence) (rdev)->asic->fence_ring_emit((rdev), (fence)) #define radeon_copy_blit(rdev, s, d, np, f) (rdev)->asic->copy_blit((rdev), (s), (d), (np), (f)) #define radeon_copy_dma(rdev, s, d, np, f) (rdev)->asic->copy_dma((rdev), (s), (d), (np), (f)) diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h index 9a75876e0c3..7ca6c13569b 100644 --- a/drivers/gpu/drm/radeon/radeon_asic.h +++ b/drivers/gpu/drm/radeon/radeon_asic.h @@ -49,6 +49,7 @@ void r100_vram_info(struct radeon_device *rdev); int r100_gpu_reset(struct radeon_device *rdev); int r100_mc_init(struct radeon_device *rdev); void r100_mc_fini(struct radeon_device *rdev); +u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc); int r100_wb_init(struct radeon_device *rdev); void r100_wb_fini(struct radeon_device *rdev); int r100_gart_enable(struct radeon_device *rdev); @@ -96,6 +97,7 @@ static struct radeon_asic r100_asic = { .ring_start = &r100_ring_start, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, + .get_vblank_counter = &r100_get_vblank_counter, .fence_ring_emit = &r100_fence_ring_emit, .cs_parse = &r100_cs_parse, .copy_blit = &r100_copy_blit, @@ -156,6 +158,7 @@ static struct radeon_asic r300_asic = { .ring_start = &r300_ring_start, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, + .get_vblank_counter = &r100_get_vblank_counter, .fence_ring_emit = &r300_fence_ring_emit, .cs_parse = &r300_cs_parse, .copy_blit = &r100_copy_blit, @@ -196,6 +199,7 @@ static struct radeon_asic r420_asic = { .ring_start = &r300_ring_start, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, + .get_vblank_counter = &r100_get_vblank_counter, .fence_ring_emit = &r300_fence_ring_emit, .cs_parse = &r300_cs_parse, .copy_blit = &r100_copy_blit, @@ -243,6 +247,7 @@ static struct radeon_asic rs400_asic = { .ring_start = &r300_ring_start, .irq_set = &r100_irq_set, .irq_process = &r100_irq_process, + .get_vblank_counter = &r100_get_vblank_counter, .fence_ring_emit = &r300_fence_ring_emit, .cs_parse = &r300_cs_parse, .copy_blit = &r100_copy_blit, @@ -266,6 +271,8 @@ void rs600_vram_info(struct radeon_device *rdev); int rs600_mc_init(struct radeon_device *rdev); void rs600_mc_fini(struct radeon_device *rdev); int rs600_irq_set(struct radeon_device *rdev); +int rs600_irq_process(struct radeon_device *rdev); +u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc); int rs600_gart_enable(struct radeon_device *rdev); void rs600_gart_disable(struct radeon_device *rdev); void rs600_gart_tlb_flush(struct radeon_device *rdev); @@ -291,7 +298,8 @@ static struct radeon_asic rs600_asic = { .cp_disable = &r100_cp_disable, .ring_start = &r300_ring_start, .irq_set = &rs600_irq_set, - .irq_process = &r100_irq_process, + .irq_process = &rs600_irq_process, + .get_vblank_counter = &rs600_get_vblank_counter, .fence_ring_emit = &r300_fence_ring_emit, .cs_parse = &r300_cs_parse, .copy_blit = &r100_copy_blit, @@ -308,6 +316,7 @@ static struct radeon_asic rs600_asic = { /* * rs690,rs740 */ +int rs690_init(struct radeon_device *rdev); void rs690_errata(struct radeon_device *rdev); void rs690_vram_info(struct radeon_device *rdev); int rs690_mc_init(struct radeon_device *rdev); @@ -316,7 +325,7 @@ uint32_t rs690_mc_rreg(struct radeon_device *rdev, uint32_t reg); void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v); void rs690_bandwidth_update(struct radeon_device *rdev); static struct radeon_asic rs690_asic = { - .init = &r300_init, + .init = &rs690_init, .errata = &rs690_errata, .vram_info = &rs690_vram_info, .gpu_reset = &r300_gpu_reset, @@ -333,7 +342,8 @@ static struct radeon_asic rs690_asic = { .cp_disable = &r100_cp_disable, .ring_start = &r300_ring_start, .irq_set = &rs600_irq_set, - .irq_process = &r100_irq_process, + .irq_process = &rs600_irq_process, + .get_vblank_counter = &rs600_get_vblank_counter, .fence_ring_emit = &r300_fence_ring_emit, .cs_parse = &r300_cs_parse, .copy_blit = &r100_copy_blit, @@ -381,8 +391,9 @@ static struct radeon_asic rv515_asic = { .cp_fini = &r100_cp_fini, .cp_disable = &r100_cp_disable, .ring_start = &rv515_ring_start, - .irq_set = &r100_irq_set, - .irq_process = &r100_irq_process, + .irq_set = &rs600_irq_set, + .irq_process = &rs600_irq_process, + .get_vblank_counter = &rs600_get_vblank_counter, .fence_ring_emit = &r300_fence_ring_emit, .cs_parse = &r300_cs_parse, .copy_blit = &r100_copy_blit, @@ -423,8 +434,9 @@ static struct radeon_asic r520_asic = { .cp_fini = &r100_cp_fini, .cp_disable = &r100_cp_disable, .ring_start = &rv515_ring_start, - .irq_set = &r100_irq_set, - .irq_process = &r100_irq_process, + .irq_set = &rs600_irq_set, + .irq_process = &rs600_irq_process, + .get_vblank_counter = &rs600_get_vblank_counter, .fence_ring_emit = &r300_fence_ring_emit, .cs_parse = &r300_cs_parse, .copy_blit = &r100_copy_blit, diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c index afc4db280b9..2a027e00762 100644 --- a/drivers/gpu/drm/radeon/radeon_combios.c +++ b/drivers/gpu/drm/radeon/radeon_combios.c @@ -685,23 +685,15 @@ static const uint32_t default_tvdac_adj[CHIP_LAST] = { 0x00780000, /* rs480 */ }; -static struct radeon_encoder_tv_dac - *radeon_legacy_get_tv_dac_info_from_table(struct radeon_device *rdev) +static void radeon_legacy_get_tv_dac_info_from_table(struct radeon_device *rdev, + struct radeon_encoder_tv_dac *tv_dac) { - struct radeon_encoder_tv_dac *tv_dac = NULL; - - tv_dac = kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL); - - if (!tv_dac) - return NULL; - tv_dac->ps2_tvdac_adj = default_tvdac_adj[rdev->family]; if ((rdev->flags & RADEON_IS_MOBILITY) && (rdev->family == CHIP_RV250)) tv_dac->ps2_tvdac_adj = 0x00880000; tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; - - return tv_dac; + return; } struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct @@ -713,19 +705,18 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct uint16_t dac_info; uint8_t rev, bg, dac; struct radeon_encoder_tv_dac *tv_dac = NULL; + int found = 0; + + tv_dac = kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL); + if (!tv_dac) + return NULL; if (rdev->bios == NULL) - return radeon_legacy_get_tv_dac_info_from_table(rdev); + goto out; /* first check TV table */ dac_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE); if (dac_info) { - tv_dac = - kzalloc(sizeof(struct radeon_encoder_tv_dac), GFP_KERNEL); - - if (!tv_dac) - return NULL; - rev = RBIOS8(dac_info + 0x3); if (rev > 4) { bg = RBIOS8(dac_info + 0xc) & 0xf; @@ -739,6 +730,7 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct bg = RBIOS8(dac_info + 0x10) & 0xf; dac = RBIOS8(dac_info + 0x11) & 0xf; tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20); + found = 1; } else if (rev > 1) { bg = RBIOS8(dac_info + 0xc) & 0xf; dac = (RBIOS8(dac_info + 0xc) >> 4) & 0xf; @@ -751,22 +743,15 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct bg = RBIOS8(dac_info + 0xe) & 0xf; dac = (RBIOS8(dac_info + 0xe) >> 4) & 0xf; tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20); + found = 1; } - tv_dac->tv_std = radeon_combios_get_tv_info(encoder); - - } else { + } + if (!found) { /* then check CRT table */ dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE); if (dac_info) { - tv_dac = - kzalloc(sizeof(struct radeon_encoder_tv_dac), - GFP_KERNEL); - - if (!tv_dac) - return NULL; - rev = RBIOS8(dac_info) & 0x3; if (rev < 2) { bg = RBIOS8(dac_info + 0x3) & 0xf; @@ -775,6 +760,7 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct (bg << 16) | (dac << 20); tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; + found = 1; } else { bg = RBIOS8(dac_info + 0x4) & 0xf; dac = RBIOS8(dac_info + 0x5) & 0xf; @@ -782,13 +768,17 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct (bg << 16) | (dac << 20); tv_dac->pal_tvdac_adj = tv_dac->ps2_tvdac_adj; tv_dac->ntsc_tvdac_adj = tv_dac->ps2_tvdac_adj; + found = 1; } } else { DRM_INFO("No TV DAC info found in BIOS\n"); - return radeon_legacy_get_tv_dac_info_from_table(rdev); } } +out: + if (!found) /* fallback to defaults */ + radeon_legacy_get_tv_dac_info_from_table(rdev, tv_dac); + return tv_dac; } diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c index d8356827ef1..7a52c461145 100644 --- a/drivers/gpu/drm/radeon/radeon_cp.c +++ b/drivers/gpu/drm/radeon/radeon_cp.c @@ -406,6 +406,15 @@ static void radeon_init_pipes(drm_radeon_private_t *dev_priv) { uint32_t gb_tile_config, gb_pipe_sel = 0; + if ((dev_priv->flags & RADEON_FAMILY_MASK) == CHIP_RV530) { + uint32_t z_pipe_sel = RADEON_READ(RV530_GB_PIPE_SELECT2); + if ((z_pipe_sel & 3) == 3) + dev_priv->num_z_pipes = 2; + else + dev_priv->num_z_pipes = 1; + } else + dev_priv->num_z_pipes = 1; + /* RS4xx/RS6xx/R4xx/R5xx */ if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R420) { gb_pipe_sel = RADEON_READ(R400_GB_PIPE_SELECT); diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 9ff6dcb97f9..7693f7c67bd 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c @@ -225,25 +225,18 @@ void radeon_invalid_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) void radeon_register_accessor_init(struct radeon_device *rdev) { - rdev->mm_rreg = &r100_mm_rreg; - rdev->mm_wreg = &r100_mm_wreg; rdev->mc_rreg = &radeon_invalid_rreg; rdev->mc_wreg = &radeon_invalid_wreg; rdev->pll_rreg = &radeon_invalid_rreg; rdev->pll_wreg = &radeon_invalid_wreg; - rdev->pcie_rreg = &radeon_invalid_rreg; - rdev->pcie_wreg = &radeon_invalid_wreg; rdev->pciep_rreg = &radeon_invalid_rreg; rdev->pciep_wreg = &radeon_invalid_wreg; /* Don't change order as we are overridding accessor. */ if (rdev->family < CHIP_RV515) { - rdev->pcie_rreg = &rv370_pcie_rreg; - rdev->pcie_wreg = &rv370_pcie_wreg; - } - if (rdev->family >= CHIP_RV515) { - rdev->pcie_rreg = &rv515_pcie_rreg; - rdev->pcie_wreg = &rv515_pcie_wreg; + rdev->pcie_reg_mask = 0xff; + } else { + rdev->pcie_reg_mask = 0x7ff; } /* FIXME: not sure here */ if (rdev->family <= CHIP_R580) { diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index 3933f8216a3..6fa32dac4e9 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h @@ -100,9 +100,10 @@ * 1.28- Add support for VBL on CRTC2 * 1.29- R500 3D cmd buffer support * 1.30- Add support for occlusion queries + * 1.31- Add support for num Z pipes from GET_PARAM */ #define DRIVER_MAJOR 1 -#define DRIVER_MINOR 30 +#define DRIVER_MINOR 31 #define DRIVER_PATCHLEVEL 0 /* @@ -329,6 +330,7 @@ typedef struct drm_radeon_private { resource_size_t fb_aper_offset; int num_gb_pipes; + int num_z_pipes; int track_flush; drm_local_map_t *mmio; @@ -689,6 +691,7 @@ extern void r600_page_table_cleanup(struct drm_device *dev, struct drm_ati_pciga /* pipe config regs */ #define R400_GB_PIPE_SELECT 0x402c +#define RV530_GB_PIPE_SELECT2 0x4124 #define R500_DYN_SCLK_PWMEM_PIPE 0x000d /* PLL */ #define R300_GB_TILE_CONFIG 0x4018 # define R300_ENABLE_TILING (1 << 0) diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index 3206c0ad7b6..ec383edf5f3 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c @@ -574,6 +574,8 @@ int radeonfb_create(struct radeon_device *rdev, goto out_unref; } + memset_io(fbptr, 0, aligned_size); + strcpy(info->fix.id, "radeondrmfb"); info->fix.type = FB_TYPE_PACKED_PIXELS; info->fix.visual = FB_VISUAL_TRUECOLOR; diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c index cded5180c75..14c19980292 100644 --- a/drivers/gpu/drm/radeon/radeon_gem.c +++ b/drivers/gpu/drm/radeon/radeon_gem.c @@ -262,8 +262,28 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data, int radeon_gem_busy_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) { - /* FIXME: implement */ - return 0; + struct drm_radeon_gem_busy *args = data; + struct drm_gem_object *gobj; + struct radeon_object *robj; + int r; + uint32_t cur_placement; + + gobj = drm_gem_object_lookup(dev, filp, args->handle); + if (gobj == NULL) { + return -EINVAL; + } + robj = gobj->driver_private; + r = radeon_object_busy_domain(robj, &cur_placement); + if (cur_placement == TTM_PL_VRAM) + args->domain = RADEON_GEM_DOMAIN_VRAM; + if (cur_placement == TTM_PL_FLAG_TT) + args->domain = RADEON_GEM_DOMAIN_GTT; + if (cur_placement == TTM_PL_FLAG_SYSTEM) + args->domain = RADEON_GEM_DOMAIN_CPU; + mutex_lock(&dev->struct_mutex); + drm_gem_object_unreference(gobj); + mutex_unlock(&dev->struct_mutex); + return r; } int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data, diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 491d569deb0..9805e4b6ca1 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -32,60 +32,6 @@ #include "radeon.h" #include "atom.h" -static inline uint32_t r100_irq_ack(struct radeon_device *rdev) -{ - uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS); - uint32_t irq_mask = RADEON_SW_INT_TEST; - - if (irqs) { - WREG32(RADEON_GEN_INT_STATUS, irqs); - } - return irqs & irq_mask; -} - -int r100_irq_set(struct radeon_device *rdev) -{ - uint32_t tmp = 0; - - if (rdev->irq.sw_int) { - tmp |= RADEON_SW_INT_ENABLE; - } - /* Todo go through CRTC and enable vblank int or not */ - WREG32(RADEON_GEN_INT_CNTL, tmp); - return 0; -} - -int r100_irq_process(struct radeon_device *rdev) -{ - uint32_t status; - - status = r100_irq_ack(rdev); - if (!status) { - return IRQ_NONE; - } - while (status) { - /* SW interrupt */ - if (status & RADEON_SW_INT_TEST) { - radeon_fence_process(rdev); - } - status = r100_irq_ack(rdev); - } - return IRQ_HANDLED; -} - -int rs600_irq_set(struct radeon_device *rdev) -{ - uint32_t tmp = 0; - - if (rdev->irq.sw_int) { - tmp |= RADEON_SW_INT_ENABLE; - } - WREG32(RADEON_GEN_INT_CNTL, tmp); - /* Todo go through CRTC and enable vblank int or not */ - WREG32(R500_DxMODE_INT_MASK, 0); - return 0; -} - irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS) { struct drm_device *dev = (struct drm_device *) arg; diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 3357110e30c..dce09ada32b 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c @@ -95,6 +95,9 @@ int radeon_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) case RADEON_INFO_NUM_GB_PIPES: value = rdev->num_gb_pipes; break; + case RADEON_INFO_NUM_Z_PIPES: + value = rdev->num_z_pipes; + break; default: DRM_DEBUG("Invalid request %d\n", info->request); return -EINVAL; @@ -141,19 +144,42 @@ void radeon_driver_preclose_kms(struct drm_device *dev, */ u32 radeon_get_vblank_counter_kms(struct drm_device *dev, int crtc) { - /* FIXME: implement */ - return 0; + struct radeon_device *rdev = dev->dev_private; + + if (crtc < 0 || crtc > 1) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + return radeon_get_vblank_counter(rdev, crtc); } int radeon_enable_vblank_kms(struct drm_device *dev, int crtc) { - /* FIXME: implement */ - return 0; + struct radeon_device *rdev = dev->dev_private; + + if (crtc < 0 || crtc > 1) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return -EINVAL; + } + + rdev->irq.crtc_vblank_int[crtc] = true; + + return radeon_irq_set(rdev); } void radeon_disable_vblank_kms(struct drm_device *dev, int crtc) { - /* FIXME: implement */ + struct radeon_device *rdev = dev->dev_private; + + if (crtc < 0 || crtc > 1) { + DRM_ERROR("Invalid crtc %d\n", crtc); + return; + } + + rdev->irq.crtc_vblank_int[crtc] = false; + + radeon_irq_set(rdev); } @@ -295,5 +321,6 @@ struct drm_ioctl_desc radeon_ioctls_kms[] = { DRM_IOCTL_DEF(DRM_RADEON_INFO, radeon_info_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_RADEON_GEM_SET_TILING, radeon_gem_set_tiling_ioctl, DRM_AUTH), DRM_IOCTL_DEF(DRM_RADEON_GEM_GET_TILING, radeon_gem_get_tiling_ioctl, DRM_AUTH), + DRM_IOCTL_DEF(DRM_RADEON_GEM_BUSY, radeon_gem_busy_ioctl, DRM_AUTH), }; int radeon_max_kms_ioctl = DRM_ARRAY_SIZE(radeon_ioctls_kms); diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c index 7d06dc98a42..0da72f18fd3 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c @@ -310,10 +310,13 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) RADEON_CRTC_DISP_REQ_EN_B)); WREG32_P(RADEON_CRTC_EXT_CNTL, 0, ~mask); } + drm_vblank_post_modeset(dev, radeon_crtc->crtc_id); + radeon_crtc_load_lut(crtc); break; case DRM_MODE_DPMS_STANDBY: case DRM_MODE_DPMS_SUSPEND: case DRM_MODE_DPMS_OFF: + drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id); if (radeon_crtc->crtc_id) WREG32_P(RADEON_CRTC2_GEN_CNTL, mask, ~mask); else { @@ -323,10 +326,6 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode) } break; } - - if (mode != DRM_MODE_DPMS_OFF) { - radeon_crtc_load_lut(crtc); - } } /* properly set crtc bpp when using atombios */ diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c index 34d0f58eb94..9322675ef6d 100644 --- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c +++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c @@ -1066,6 +1066,7 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t switch (radeon_encoder->encoder_id) { case ENCODER_OBJECT_ID_INTERNAL_LVDS: + encoder->possible_crtcs = 0x1; drm_encoder_init(dev, encoder, &radeon_legacy_lvds_enc_funcs, DRM_MODE_ENCODER_LVDS); drm_encoder_helper_add(encoder, &radeon_legacy_lvds_helper_funcs); if (rdev->is_atom_bios) diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c index e98cae3bf4a..b85fb83d7ae 100644 --- a/drivers/gpu/drm/radeon/radeon_object.c +++ b/drivers/gpu/drm/radeon/radeon_object.c @@ -316,6 +316,25 @@ int radeon_object_wait(struct radeon_object *robj) return r; } +int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement) +{ + int r = 0; + + r = radeon_object_reserve(robj, true); + if (unlikely(r != 0)) { + DRM_ERROR("radeon: failed to reserve object for waiting.\n"); + return r; + } + spin_lock(&robj->tobj.lock); + *cur_placement = robj->tobj.mem.mem_type; + if (robj->tobj.sync_obj) { + r = ttm_bo_wait(&robj->tobj, true, true, true); + } + spin_unlock(&robj->tobj.lock); + radeon_object_unreserve(robj); + return r; +} + int radeon_object_evict_vram(struct radeon_device *rdev) { if (rdev->flags & RADEON_IS_IGP) { diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h index e1b61857446..4df43f62c67 100644 --- a/drivers/gpu/drm/radeon/radeon_reg.h +++ b/drivers/gpu/drm/radeon/radeon_reg.h @@ -982,12 +982,15 @@ # define RS400_TMDS2_PLLRST (1 << 1) #define RADEON_GEN_INT_CNTL 0x0040 +# define RADEON_CRTC_VBLANK_MASK (1 << 0) +# define RADEON_CRTC2_VBLANK_MASK (1 << 9) # define RADEON_SW_INT_ENABLE (1 << 25) #define RADEON_GEN_INT_STATUS 0x0044 -# define RADEON_VSYNC_INT_AK (1 << 2) -# define RADEON_VSYNC_INT (1 << 2) -# define RADEON_VSYNC2_INT_AK (1 << 6) -# define RADEON_VSYNC2_INT (1 << 6) +# define AVIVO_DISPLAY_INT_STATUS (1 << 0) +# define RADEON_CRTC_VBLANK_STAT (1 << 0) +# define RADEON_CRTC_VBLANK_STAT_ACK (1 << 0) +# define RADEON_CRTC2_VBLANK_STAT (1 << 9) +# define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9) # define RADEON_SW_INT_FIRE (1 << 26) # define RADEON_SW_INT_TEST (1 << 25) # define RADEON_SW_INT_TEST_ACK (1 << 25) @@ -2334,6 +2337,9 @@ # define RADEON_RE_WIDTH_SHIFT 0 # define RADEON_RE_HEIGHT_SHIFT 16 +#define RADEON_RB3D_ZPASS_DATA 0x3290 +#define RADEON_RB3D_ZPASS_ADDR 0x3294 + #define RADEON_SE_CNTL 0x1c4c # define RADEON_FFACE_CULL_CW (0 << 0) # define RADEON_FFACE_CULL_CCW (1 << 0) @@ -3568,4 +3574,6 @@ #define RADEON_SCRATCH_REG4 0x15f0 #define RADEON_SCRATCH_REG5 0x15f4 +#define RV530_GB_PIPE_SELECT2 0x4124 + #endif diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index 46645f3e032..2882f40d5ec 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -3081,6 +3081,9 @@ static int radeon_cp_getparam(struct drm_device *dev, void *data, struct drm_fil case RADEON_PARAM_NUM_GB_PIPES: value = dev_priv->num_gb_pipes; break; + case RADEON_PARAM_NUM_Z_PIPES: + value = dev_priv->num_z_pipes; + break; default: DRM_DEBUG("Invalid parameter %d\n", param->param); return -EINVAL; diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c index bbea6dee4a9..7e8ce983a90 100644 --- a/drivers/gpu/drm/radeon/rs600.c +++ b/drivers/gpu/drm/radeon/rs600.c @@ -240,6 +240,88 @@ void rs600_mc_fini(struct radeon_device *rdev) /* + * Interrupts + */ +int rs600_irq_set(struct radeon_device *rdev) +{ + uint32_t tmp = 0; + uint32_t mode_int = 0; + + if (rdev->irq.sw_int) { + tmp |= RADEON_SW_INT_ENABLE; + } + if (rdev->irq.crtc_vblank_int[0]) { + tmp |= AVIVO_DISPLAY_INT_STATUS; + mode_int |= AVIVO_D1MODE_INT_MASK; + } + if (rdev->irq.crtc_vblank_int[1]) { + tmp |= AVIVO_DISPLAY_INT_STATUS; + mode_int |= AVIVO_D2MODE_INT_MASK; + } + WREG32(RADEON_GEN_INT_CNTL, tmp); + WREG32(AVIVO_DxMODE_INT_MASK, mode_int); + return 0; +} + +static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_int) +{ + uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS); + uint32_t irq_mask = RADEON_SW_INT_TEST; + + if (irqs & AVIVO_DISPLAY_INT_STATUS) { + *r500_disp_int = RREG32(AVIVO_DISP_INTERRUPT_STATUS); + if (*r500_disp_int & AVIVO_D1_VBLANK_INTERRUPT) { + WREG32(AVIVO_D1MODE_VBLANK_STATUS, AVIVO_VBLANK_ACK); + } + if (*r500_disp_int & AVIVO_D2_VBLANK_INTERRUPT) { + WREG32(AVIVO_D2MODE_VBLANK_STATUS, AVIVO_VBLANK_ACK); + } + } else { + *r500_disp_int = 0; + } + + if (irqs) { + WREG32(RADEON_GEN_INT_STATUS, irqs); + } + return irqs & irq_mask; +} + +int rs600_irq_process(struct radeon_device *rdev) +{ + uint32_t status; + uint32_t r500_disp_int; + + status = rs600_irq_ack(rdev, &r500_disp_int); + if (!status && !r500_disp_int) { + return IRQ_NONE; + } + while (status || r500_disp_int) { + /* SW interrupt */ + if (status & RADEON_SW_INT_TEST) { + radeon_fence_process(rdev); + } + /* Vertical blank interrupts */ + if (r500_disp_int & AVIVO_D1_VBLANK_INTERRUPT) { + drm_handle_vblank(rdev->ddev, 0); + } + if (r500_disp_int & AVIVO_D2_VBLANK_INTERRUPT) { + drm_handle_vblank(rdev->ddev, 1); + } + status = rs600_irq_ack(rdev, &r500_disp_int); + } + return IRQ_HANDLED; +} + +u32 rs600_get_vblank_counter(struct radeon_device *rdev, int crtc) +{ + if (crtc == 0) + return RREG32(AVIVO_D1CRTC_FRAME_COUNT); + else + return RREG32(AVIVO_D2CRTC_FRAME_COUNT); +} + + +/* * Global GPU functions */ void rs600_disable_vga(struct radeon_device *rdev) diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c index 839595b0072..bc6b7c5339b 100644 --- a/drivers/gpu/drm/radeon/rs690.c +++ b/drivers/gpu/drm/radeon/rs690.c @@ -652,3 +652,68 @@ void rs690_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) WREG32(RS690_MC_DATA, v); WREG32(RS690_MC_INDEX, RS690_MC_INDEX_WR_ACK); } + +static const unsigned rs690_reg_safe_bm[219] = { + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0x17FF1FFF,0xFFFFFFFC,0xFFFFFFFF,0xFF30FFBF, + 0xFFFFFFF8,0xC3E6FFFF,0xFFFFF6DF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFF03F, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFEFCE,0xF00EBFFF,0x007C0000, + 0xF0000078,0xFF000009,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFF7FF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFC78,0xFFFFFFFF,0xFFFFFFFE,0xFFFFFFFF, + 0x38FF8F50,0xFFF88082,0xF000000C,0xFAE009FF, + 0x0000FFFF,0xFFFFFFFF,0xFFFFFFFF,0x00000000, + 0x00000000,0x0000C100,0x00000000,0x00000000, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x00000000,0xFFFF0000,0xFFFFFFFF,0xFF80FFFF, + 0x00000000,0x00000000,0x00000000,0x00000000, + 0x0003FC01,0xFFFFFFF8,0xFE800B19,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, + 0xFFFFFFFF,0xFFFFFFFF,0xFFFFFFFF, +}; + +int rs690_init(struct radeon_device *rdev) +{ + rdev->config.r300.reg_safe_bm = rs690_reg_safe_bm; + rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(rs690_reg_safe_bm); + return 0; +} diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c index fd8f3ca716e..31a7f668ae5 100644 --- a/drivers/gpu/drm/radeon/rv515.c +++ b/drivers/gpu/drm/radeon/rv515.c @@ -400,25 +400,6 @@ void rv515_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) WREG32(MC_IND_INDEX, 0); } -uint32_t rv515_pcie_rreg(struct radeon_device *rdev, uint32_t reg) -{ - uint32_t r; - - WREG32(PCIE_INDEX, ((reg) & 0x7ff)); - (void)RREG32(PCIE_INDEX); - r = RREG32(PCIE_DATA); - return r; -} - -void rv515_pcie_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v) -{ - WREG32(PCIE_INDEX, ((reg) & 0x7ff)); - (void)RREG32(PCIE_INDEX); - WREG32(PCIE_DATA, (v)); - (void)RREG32(PCIE_DATA); -} - - /* * Debugfs info */ diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index d258b02aef4..827da085813 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -674,7 +674,14 @@ omap_i2c_isr(int this_irq, void *dev_id) err = 0; complete: - omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); + /* + * Ack the stat in one go, but [R/X]DR and [R/X]RDY should be + * acked after the data operation is complete. + * Ref: TRM SWPU114Q Figure 18-31 + */ + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat & + ~(OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR | + OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); if (stat & OMAP_I2C_STAT_NACK) { err |= OMAP_I2C_STAT_NACK; @@ -687,6 +694,9 @@ complete: } if (stat & (OMAP_I2C_STAT_ARDY | OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { + omap_i2c_ack_stat(dev, stat & + (OMAP_I2C_STAT_RRDY | OMAP_I2C_STAT_RDR | + OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); omap_i2c_complete_cmd(dev, err); return IRQ_HANDLED; } @@ -774,7 +784,7 @@ complete: * memory to the I2C interface. */ - if (cpu_is_omap34xx()) { + if (dev->rev <= OMAP_I2C_REV_ON_3430) { while (!(stat & OMAP_I2C_STAT_XUDF)) { if (stat & (OMAP_I2C_STAT_NACK | OMAP_I2C_STAT_AL)) { omap_i2c_ack_stat(dev, stat & (OMAP_I2C_STAT_XRDY | OMAP_I2C_STAT_XDR)); diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 762e1e53088..049555777f6 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1134,35 +1134,44 @@ static int __exit i2c_pxa_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static int i2c_pxa_suspend_late(struct platform_device *dev, pm_message_t state) +static int i2c_pxa_suspend_noirq(struct device *dev) { - struct pxa_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct pxa_i2c *i2c = platform_get_drvdata(pdev); + clk_disable(i2c->clk); + return 0; } -static int i2c_pxa_resume_early(struct platform_device *dev) +static int i2c_pxa_resume_noirq(struct device *dev) { - struct pxa_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct pxa_i2c *i2c = platform_get_drvdata(pdev); clk_enable(i2c->clk); i2c_pxa_reset(i2c); return 0; } + +static struct dev_pm_ops i2c_pxa_dev_pm_ops = { + .suspend_noirq = i2c_pxa_suspend_noirq, + .resume_noirq = i2c_pxa_resume_noirq, +}; + +#define I2C_PXA_DEV_PM_OPS (&i2c_pxa_dev_pm_ops) #else -#define i2c_pxa_suspend_late NULL -#define i2c_pxa_resume_early NULL +#define I2C_PXA_DEV_PM_OPS NULL #endif static struct platform_driver i2c_pxa_driver = { .probe = i2c_pxa_probe, .remove = __exit_p(i2c_pxa_remove), - .suspend_late = i2c_pxa_suspend_late, - .resume_early = i2c_pxa_resume_early, .driver = { .name = "pxa2xx-i2c", .owner = THIS_MODULE, + .pm = I2C_PXA_DEV_PM_OPS, }, .id_table = i2c_pxa_id_table, }; diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 20bb0ceb027..96aafb91b69 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -946,17 +946,20 @@ static int s3c24xx_i2c_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int s3c24xx_i2c_suspend_late(struct platform_device *dev, - pm_message_t msg) +static int s3c24xx_i2c_suspend_noirq(struct device *dev) { - struct s3c24xx_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); + i2c->suspended = 1; + return 0; } -static int s3c24xx_i2c_resume(struct platform_device *dev) +static int s3c24xx_i2c_resume(struct device *dev) { - struct s3c24xx_i2c *i2c = platform_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev); + struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev); i2c->suspended = 0; s3c24xx_i2c_init(i2c); @@ -964,9 +967,14 @@ static int s3c24xx_i2c_resume(struct platform_device *dev) return 0; } +static struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = { + .suspend_noirq = s3c24xx_i2c_suspend_noirq, + .resume = s3c24xx_i2c_resume, +}; + +#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops) #else -#define s3c24xx_i2c_suspend_late NULL -#define s3c24xx_i2c_resume NULL +#define S3C24XX_DEV_PM_OPS NULL #endif /* device driver for platform bus bits */ @@ -985,12 +993,11 @@ MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); static struct platform_driver s3c24xx_i2c_driver = { .probe = s3c24xx_i2c_probe, .remove = s3c24xx_i2c_remove, - .suspend_late = s3c24xx_i2c_suspend_late, - .resume = s3c24xx_i2c_resume, .id_table = s3c24xx_driver_ids, .driver = { .owner = THIS_MODULE, .name = "s3c-i2c", + .pm = S3C24XX_DEV_PM_OPS, }, }; diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 820487d0d5c..86a9d4e8147 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -28,6 +28,7 @@ #include <linux/interrupt.h> #include <linux/i2c.h> #include <linux/err.h> +#include <linux/pm_runtime.h> #include <linux/clk.h> #include <linux/io.h> @@ -165,7 +166,8 @@ static void activate_ch(struct sh_mobile_i2c_data *pd) u_int32_t denom; u_int32_t tmp; - /* Make sure the clock is enabled */ + /* Wake up device and enable clock */ + pm_runtime_get_sync(pd->dev); clk_enable(pd->clk); /* Get clock rate after clock is enabled */ @@ -213,8 +215,9 @@ static void deactivate_ch(struct sh_mobile_i2c_data *pd) /* Disable channel */ iowrite8(ioread8(ICCR(pd)) & ~ICCR_ICE, ICCR(pd)); - /* Disable clock */ + /* Disable clock and mark device as idle */ clk_disable(pd->clk); + pm_runtime_put_sync(pd->dev); } static unsigned char i2c_op(struct sh_mobile_i2c_data *pd, @@ -572,6 +575,19 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) goto err_irq; } + /* Enable Runtime PM for this device. + * + * Also tell the Runtime PM core to ignore children + * for this device since it is valid for us to suspend + * this I2C master driver even though the slave devices + * on the I2C bus may not be suspended. + * + * The state of the I2C hardware bus is unaffected by + * the Runtime PM state. + */ + pm_suspend_ignore_children(&dev->dev, true); + pm_runtime_enable(&dev->dev); + /* setup the private data */ adap = &pd->adap; i2c_set_adapdata(adap, pd); @@ -614,14 +630,33 @@ static int sh_mobile_i2c_remove(struct platform_device *dev) iounmap(pd->reg); sh_mobile_i2c_hook_irqs(dev, 0); clk_put(pd->clk); + pm_runtime_disable(&dev->dev); kfree(pd); return 0; } +static int sh_mobile_i2c_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static struct dev_pm_ops sh_mobile_i2c_dev_pm_ops = { + .runtime_suspend = sh_mobile_i2c_runtime_nop, + .runtime_resume = sh_mobile_i2c_runtime_nop, +}; + static struct platform_driver sh_mobile_i2c_driver = { .driver = { .name = "i2c-sh_mobile", .owner = THIS_MODULE, + .pm = &sh_mobile_i2c_dev_pm_ops, }, .probe = sh_mobile_i2c_probe, .remove = sh_mobile_i2c_remove, diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c index 182e711318b..d2728a28a8d 100644 --- a/drivers/i2c/busses/i2c-stu300.c +++ b/drivers/i2c/busses/i2c-stu300.c @@ -117,7 +117,8 @@ enum stu300_error { STU300_ERROR_NONE = 0, STU300_ERROR_ACKNOWLEDGE_FAILURE, STU300_ERROR_BUS_ERROR, - STU300_ERROR_ARBITRATION_LOST + STU300_ERROR_ARBITRATION_LOST, + STU300_ERROR_UNKNOWN }; /* timeout waiting for the controller to respond */ @@ -127,7 +128,7 @@ enum stu300_error { * The number of address send athemps tried before giving up. * If the first one failes it seems like 5 to 8 attempts are required. */ -#define NUM_ADDR_RESEND_ATTEMPTS 10 +#define NUM_ADDR_RESEND_ATTEMPTS 12 /* I2C clock speed, in Hz 0-400kHz*/ static unsigned int scl_frequency = 100000; @@ -149,6 +150,7 @@ module_param(scl_frequency, uint, 0644); * @msg_index: index of current message * @msg_len: length of current message */ + struct stu300_dev { struct platform_device *pdev; struct i2c_adapter adapter; @@ -188,6 +190,27 @@ static inline u32 stu300_r8(void __iomem *address) return readl(address) & 0x000000FFU; } +static void stu300_irq_enable(struct stu300_dev *dev) +{ + u32 val; + val = stu300_r8(dev->virtbase + I2C_CR); + val |= I2C_CR_INTERRUPT_ENABLE; + /* Twice paranoia (possible HW glitch) */ + stu300_wr8(val, dev->virtbase + I2C_CR); + stu300_wr8(val, dev->virtbase + I2C_CR); +} + +static void stu300_irq_disable(struct stu300_dev *dev) +{ + u32 val; + val = stu300_r8(dev->virtbase + I2C_CR); + val &= ~I2C_CR_INTERRUPT_ENABLE; + /* Twice paranoia (possible HW glitch) */ + stu300_wr8(val, dev->virtbase + I2C_CR); + stu300_wr8(val, dev->virtbase + I2C_CR); +} + + /* * Tells whether a certain event or events occurred in * response to a command. The events represent states in @@ -196,9 +219,10 @@ static inline u32 stu300_r8(void __iomem *address) * documentation and can only be treated as abstract state * machine states. * - * @ret 0 = event has not occurred, any other value means - * the event occurred. + * @ret 0 = event has not occurred or unknown error, any + * other value means the correct event occurred or an error. */ + static int stu300_event_occurred(struct stu300_dev *dev, enum stu300_event mr_event) { u32 status1; @@ -206,11 +230,28 @@ static int stu300_event_occurred(struct stu300_dev *dev, /* What event happened? */ status1 = stu300_r8(dev->virtbase + I2C_SR1); + if (!(status1 & I2C_SR1_EVF_IND)) /* No event at all */ return 0; + status2 = stu300_r8(dev->virtbase + I2C_SR2); + /* Block any multiple interrupts */ + stu300_irq_disable(dev); + + /* Check for errors first */ + if (status2 & I2C_SR2_AF_IND) { + dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE; + return 1; + } else if (status2 & I2C_SR2_BERR_IND) { + dev->cmd_err = STU300_ERROR_BUS_ERROR; + return 1; + } else if (status2 & I2C_SR2_ARLO_IND) { + dev->cmd_err = STU300_ERROR_ARBITRATION_LOST; + return 1; + } + switch (mr_event) { case STU300_EVENT_1: if (status1 & I2C_SR1_ADSL_IND) @@ -221,10 +262,6 @@ static int stu300_event_occurred(struct stu300_dev *dev, case STU300_EVENT_7: case STU300_EVENT_8: if (status1 & I2C_SR1_BTF_IND) { - if (status2 & I2C_SR2_AF_IND) - dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE; - else if (status2 & I2C_SR2_BERR_IND) - dev->cmd_err = STU300_ERROR_BUS_ERROR; return 1; } break; @@ -240,8 +277,6 @@ static int stu300_event_occurred(struct stu300_dev *dev, case STU300_EVENT_6: if (status2 & I2C_SR2_ENDAD_IND) { /* First check for any errors */ - if (status2 & I2C_SR2_AF_IND) - dev->cmd_err = STU300_ERROR_ACKNOWLEDGE_FAILURE; return 1; } break; @@ -252,8 +287,15 @@ static int stu300_event_occurred(struct stu300_dev *dev, default: break; } - if (status2 & I2C_SR2_ARLO_IND) - dev->cmd_err = STU300_ERROR_ARBITRATION_LOST; + /* If we get here, we're on thin ice. + * Here we are in a status where we have + * gotten a response that does not match + * what we requested. + */ + dev->cmd_err = STU300_ERROR_UNKNOWN; + dev_err(&dev->pdev->dev, + "Unhandled interrupt! %d sr1: 0x%x sr2: 0x%x\n", + mr_event, status1, status2); return 0; } @@ -262,21 +304,20 @@ static irqreturn_t stu300_irh(int irq, void *data) struct stu300_dev *dev = data; int res; + /* Just make sure that the block is clocked */ + clk_enable(dev->clk); + /* See if this was what we were waiting for */ spin_lock(&dev->cmd_issue_lock); - if (dev->cmd_event != STU300_EVENT_NONE) { - res = stu300_event_occurred(dev, dev->cmd_event); - if (res || dev->cmd_err != STU300_ERROR_NONE) { - u32 val; - - complete(&dev->cmd_complete); - /* Block any multiple interrupts */ - val = stu300_r8(dev->virtbase + I2C_CR); - val &= ~I2C_CR_INTERRUPT_ENABLE; - stu300_wr8(val, dev->virtbase + I2C_CR); - } - } + + res = stu300_event_occurred(dev, dev->cmd_event); + if (res || dev->cmd_err != STU300_ERROR_NONE) + complete(&dev->cmd_complete); + spin_unlock(&dev->cmd_issue_lock); + + clk_disable(dev->clk); + return IRQ_HANDLED; } @@ -308,7 +349,6 @@ static int stu300_start_and_await_event(struct stu300_dev *dev, stu300_wr8(cr_value, dev->virtbase + I2C_CR); ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, STU300_TIMEOUT); - if (ret < 0) { dev_err(&dev->pdev->dev, "wait_for_completion_interruptible_timeout() " @@ -342,7 +382,6 @@ static int stu300_await_event(struct stu300_dev *dev, enum stu300_event mr_event) { int ret; - u32 val; if (unlikely(irqs_disabled())) { /* TODO: implement polling for this case if need be. */ @@ -354,36 +393,18 @@ static int stu300_await_event(struct stu300_dev *dev, /* Is it already here? */ spin_lock_irq(&dev->cmd_issue_lock); dev->cmd_err = STU300_ERROR_NONE; - if (stu300_event_occurred(dev, mr_event)) { - spin_unlock_irq(&dev->cmd_issue_lock); - goto exit_await_check_err; - } - init_completion(&dev->cmd_complete); - dev->cmd_err = STU300_ERROR_NONE; dev->cmd_event = mr_event; - /* Turn on the I2C interrupt for current operation */ - val = stu300_r8(dev->virtbase + I2C_CR); - val |= I2C_CR_INTERRUPT_ENABLE; - stu300_wr8(val, dev->virtbase + I2C_CR); - - /* Twice paranoia (possible HW glitch) */ - stu300_wr8(val, dev->virtbase + I2C_CR); + init_completion(&dev->cmd_complete); - /* Check again: is it already here? */ - if (unlikely(stu300_event_occurred(dev, mr_event))) { - /* Disable IRQ again. */ - val &= ~I2C_CR_INTERRUPT_ENABLE; - stu300_wr8(val, dev->virtbase + I2C_CR); - spin_unlock_irq(&dev->cmd_issue_lock); - goto exit_await_check_err; - } + /* Turn on the I2C interrupt for current operation */ + stu300_irq_enable(dev); /* Unlock the command block and wait for the event to occur */ spin_unlock_irq(&dev->cmd_issue_lock); + ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, STU300_TIMEOUT); - if (ret < 0) { dev_err(&dev->pdev->dev, "wait_for_completion_interruptible_timeout()" @@ -401,7 +422,6 @@ static int stu300_await_event(struct stu300_dev *dev, return -ETIMEDOUT; } - exit_await_check_err: if (dev->cmd_err != STU300_ERROR_NONE) { if (mr_event != STU300_EVENT_6) { dev_err(&dev->pdev->dev, "controller " @@ -457,18 +477,19 @@ struct stu300_clkset { }; static const struct stu300_clkset stu300_clktable[] = { - { 0, 0xFFU }, - { 2500000, I2C_OAR2_FR_25_10MHZ }, - { 10000000, I2C_OAR2_FR_10_1667MHZ }, - { 16670000, I2C_OAR2_FR_1667_2667MHZ }, - { 26670000, I2C_OAR2_FR_2667_40MHZ }, - { 40000000, I2C_OAR2_FR_40_5333MHZ }, - { 53330000, I2C_OAR2_FR_5333_66MHZ }, - { 66000000, I2C_OAR2_FR_66_80MHZ }, - { 80000000, I2C_OAR2_FR_80_100MHZ }, + { 0, 0xFFU }, + { 2500000, I2C_OAR2_FR_25_10MHZ }, + { 10000000, I2C_OAR2_FR_10_1667MHZ }, + { 16670000, I2C_OAR2_FR_1667_2667MHZ }, + { 26670000, I2C_OAR2_FR_2667_40MHZ }, + { 40000000, I2C_OAR2_FR_40_5333MHZ }, + { 53330000, I2C_OAR2_FR_5333_66MHZ }, + { 66000000, I2C_OAR2_FR_66_80MHZ }, + { 80000000, I2C_OAR2_FR_80_100MHZ }, { 100000000, 0xFFU }, }; + static int stu300_set_clk(struct stu300_dev *dev, unsigned long clkrate) { @@ -494,10 +515,10 @@ static int stu300_set_clk(struct stu300_dev *dev, unsigned long clkrate) if (dev->speed > 100000) /* Fast Mode I2C */ - val = ((clkrate/dev->speed)-9)/3; + val = ((clkrate/dev->speed) - 9)/3 + 1; else /* Standard Mode I2C */ - val = ((clkrate/dev->speed)-7)/2; + val = ((clkrate/dev->speed) - 7)/2 + 1; /* According to spec the divider must be > 2 */ if (val < 0x002) { @@ -557,6 +578,7 @@ static int stu300_init_hw(struct stu300_dev *dev) */ clkrate = clk_get_rate(dev->clk); ret = stu300_set_clk(dev, clkrate); + if (ret) return ret; /* @@ -641,7 +663,6 @@ static int stu300_xfer_msg(struct i2c_adapter *adap, int attempts = 0; struct stu300_dev *dev = i2c_get_adapdata(adap); - clk_enable(dev->clk); /* Remove this if (0) to trace each and every message. */ @@ -715,14 +736,15 @@ static int stu300_xfer_msg(struct i2c_adapter *adap, if (attempts < NUM_ADDR_RESEND_ATTEMPTS && attempts > 0) { dev_dbg(&dev->pdev->dev, "managed to get address " - "through after %d attempts\n", attempts); + "through after %d attempts\n", attempts); } else if (attempts == NUM_ADDR_RESEND_ATTEMPTS) { dev_dbg(&dev->pdev->dev, "I give up, tried %d times " - "to resend address.\n", - NUM_ADDR_RESEND_ATTEMPTS); + "to resend address.\n", + NUM_ADDR_RESEND_ATTEMPTS); goto exit_disable; } + if (msg->flags & I2C_M_RD) { /* READ: we read the actual bytes one at a time */ for (i = 0; i < msg->len; i++) { @@ -804,8 +826,10 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, { int ret = -1; int i; + struct stu300_dev *dev = i2c_get_adapdata(adap); dev->msg_len = num; + for (i = 0; i < num; i++) { /* * Another driver appears to send stop for each message, @@ -817,6 +841,7 @@ static int stu300_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, dev->msg_index = i; ret = stu300_xfer_msg(adap, &msgs[i], (i == (num - 1))); + if (ret != 0) { num = ret; break; @@ -845,6 +870,7 @@ stu300_probe(struct platform_device *pdev) struct resource *res; int bus_nr; int ret = 0; + char clk_name[] = "I2C0"; dev = kzalloc(sizeof(struct stu300_dev), GFP_KERNEL); if (!dev) { @@ -854,7 +880,8 @@ stu300_probe(struct platform_device *pdev) } bus_nr = pdev->id; - dev->clk = clk_get(&pdev->dev, NULL); + clk_name[3] += (char)bus_nr; + dev->clk = clk_get(&pdev->dev, clk_name); if (IS_ERR(dev->clk)) { ret = PTR_ERR(dev->clk); dev_err(&pdev->dev, "could not retrieve i2c bus clock\n"); diff --git a/drivers/md/dm-log-userspace-transfer.c b/drivers/md/dm-log-userspace-transfer.c index 0ca1ee768a1..8ce74d95ae4 100644 --- a/drivers/md/dm-log-userspace-transfer.c +++ b/drivers/md/dm-log-userspace-transfer.c @@ -108,7 +108,7 @@ static int fill_pkg(struct cn_msg *msg, struct dm_ulog_request *tfr) *(pkg->data_size) = 0; } else if (tfr->data_size > *(pkg->data_size)) { DMERR("Insufficient space to receive package [%u] " - "(%u vs %lu)", tfr->request_type, + "(%u vs %zu)", tfr->request_type, tfr->data_size, *(pkg->data_size)); *(pkg->data_size) = 0; diff --git a/drivers/md/md.c b/drivers/md/md.c index 103f2d33fa8..9dd872000ce 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -4364,6 +4364,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open) if (mode == 1) set_disk_ro(disk, 1); clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery); + err = 0; } out: mutex_unlock(&mddev->open_mutex); diff --git a/drivers/media/common/tuners/qt1010.c b/drivers/media/common/tuners/qt1010.c index 825aa1412e6..9f5dba244cb 100644 --- a/drivers/media/common/tuners/qt1010.c +++ b/drivers/media/common/tuners/qt1010.c @@ -64,24 +64,22 @@ static int qt1010_writereg(struct qt1010_priv *priv, u8 reg, u8 val) /* dump all registers */ static void qt1010_dump_regs(struct qt1010_priv *priv) { - char buf[52], buf2[4]; u8 reg, val; for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - printk("%s\n", buf); - sprintf(buf, "%02x: ", reg); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); } if (qt1010_readreg(priv, reg, &val) == 0) - sprintf(buf2, "%02x ", val); + printk(KERN_CONT " %02x", val); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + printk(KERN_CONT " --"); if (reg == 0x2f) break; } - printk("%s\n", buf); + printk(KERN_CONT "\n"); } static int qt1010_set_params(struct dvb_frontend *fe, diff --git a/drivers/media/common/tuners/tuner-xc2028.c b/drivers/media/common/tuners/tuner-xc2028.c index aa20ce8cc66..f270e605da8 100644 --- a/drivers/media/common/tuners/tuner-xc2028.c +++ b/drivers/media/common/tuners/tuner-xc2028.c @@ -1119,8 +1119,8 @@ static int xc2028_sleep(struct dvb_frontend *fe) struct xc2028_data *priv = fe->tuner_priv; int rc = 0; - /* Avoid firmware reload on slow devices */ - if (no_poweroff) + /* Avoid firmware reload on slow devices or if PM disabled */ + if (no_poweroff || priv->ctrl.disable_power_mgmt) return 0; tuner_dbg("Putting xc2028/3028 into poweroff mode.\n"); diff --git a/drivers/media/common/tuners/tuner-xc2028.h b/drivers/media/common/tuners/tuner-xc2028.h index 19de7928a74..a90c35d50ad 100644 --- a/drivers/media/common/tuners/tuner-xc2028.h +++ b/drivers/media/common/tuners/tuner-xc2028.h @@ -38,6 +38,7 @@ struct xc2028_ctrl { unsigned int input1:1; unsigned int vhfbw7:1; unsigned int uhfbw8:1; + unsigned int disable_power_mgmt:1; unsigned int demod; enum firmware_type type:2; }; diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 4cb31e7c13c..26690dfb326 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -81,7 +81,6 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) switch (req->cmd) { case GET_CONFIG: - case BOOT: case READ_MEMORY: case RECONNECT_USB: case GET_IR_CODE: @@ -100,6 +99,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) case WRITE_VIRTUAL_MEMORY: case COPY_FIRMWARE: case DOWNLOAD_FIRMWARE: + case BOOT: break; default: err("unknown command:%d", req->cmd); diff --git a/drivers/media/dvb/frontends/cx22700.c b/drivers/media/dvb/frontends/cx22700.c index ace5cb17165..fbd838eca26 100644 --- a/drivers/media/dvb/frontends/cx22700.c +++ b/drivers/media/dvb/frontends/cx22700.c @@ -380,7 +380,7 @@ struct dvb_frontend* cx22700_attach(const struct cx22700_config* config, struct cx22700_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx22700_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx22700_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c index 5d1abe34bdd..00b5c7e91d5 100644 --- a/drivers/media/dvb/frontends/cx22702.c +++ b/drivers/media/dvb/frontends/cx22702.c @@ -580,7 +580,7 @@ struct dvb_frontend *cx22702_attach(const struct cx22702_config *config, struct cx22702_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx22702_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx22702_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index 87ae29db024..ffbcfabd83f 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -598,7 +598,7 @@ struct dvb_frontend* cx24110_attach(const struct cx24110_config* config, int ret; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct cx24110_state), GFP_KERNEL); + state = kzalloc(sizeof(struct cx24110_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.c b/drivers/media/dvb/frontends/dvb_dummy_fe.c index db8a937cc63..a7fc7e53a55 100644 --- a/drivers/media/dvb/frontends/dvb_dummy_fe.c +++ b/drivers/media/dvb/frontends/dvb_dummy_fe.c @@ -117,7 +117,7 @@ struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ @@ -137,7 +137,7 @@ struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ @@ -157,7 +157,7 @@ struct dvb_frontend *dvb_dummy_fe_qam_attach(void) struct dvb_dummy_fe_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); + state = kzalloc(sizeof(struct dvb_dummy_fe_state), GFP_KERNEL); if (state == NULL) goto error; /* create dvb_frontend */ diff --git a/drivers/media/dvb/frontends/l64781.c b/drivers/media/dvb/frontends/l64781.c index e1e70e9e0cb..3051b64aa17 100644 --- a/drivers/media/dvb/frontends/l64781.c +++ b/drivers/media/dvb/frontends/l64781.c @@ -501,7 +501,7 @@ struct dvb_frontend* l64781_attach(const struct l64781_config* config, { .addr = config->demod_address, .flags = I2C_M_RD, .buf = b1, .len = 1 } }; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct l64781_state), GFP_KERNEL); + state = kzalloc(sizeof(struct l64781_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/lgs8gl5.c b/drivers/media/dvb/frontends/lgs8gl5.c index 855852fddf2..bb37ed289a0 100644 --- a/drivers/media/dvb/frontends/lgs8gl5.c +++ b/drivers/media/dvb/frontends/lgs8gl5.c @@ -387,7 +387,7 @@ lgs8gl5_attach(const struct lgs8gl5_config *config, struct i2c_adapter *i2c) dprintk("%s\n", __func__); /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); + state = kzalloc(sizeof(struct lgs8gl5_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/mt312.c b/drivers/media/dvb/frontends/mt312.c index a621f727935..f69daaac78c 100644 --- a/drivers/media/dvb/frontends/mt312.c +++ b/drivers/media/dvb/frontends/mt312.c @@ -782,7 +782,7 @@ struct dvb_frontend *mt312_attach(const struct mt312_config *config, struct mt312_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct mt312_state), GFP_KERNEL); + state = kzalloc(sizeof(struct mt312_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/nxt6000.c b/drivers/media/dvb/frontends/nxt6000.c index 0eef22dbf8a..a763ec756f7 100644 --- a/drivers/media/dvb/frontends/nxt6000.c +++ b/drivers/media/dvb/frontends/nxt6000.c @@ -545,7 +545,7 @@ struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config, struct nxt6000_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct nxt6000_state), GFP_KERNEL); + state = kzalloc(sizeof(struct nxt6000_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/or51132.c b/drivers/media/dvb/frontends/or51132.c index 8133ea3cddd..38e67accb8c 100644 --- a/drivers/media/dvb/frontends/or51132.c +++ b/drivers/media/dvb/frontends/or51132.c @@ -562,7 +562,7 @@ struct dvb_frontend* or51132_attach(const struct or51132_config* config, struct or51132_state* state = NULL; /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct or51132_state), GFP_KERNEL); + state = kzalloc(sizeof(struct or51132_state), GFP_KERNEL); if (state == NULL) return NULL; diff --git a/drivers/media/dvb/frontends/or51211.c b/drivers/media/dvb/frontends/or51211.c index 16cf2fdd5d7..c709ce6771c 100644 --- a/drivers/media/dvb/frontends/or51211.c +++ b/drivers/media/dvb/frontends/or51211.c @@ -527,7 +527,7 @@ struct dvb_frontend* or51211_attach(const struct or51211_config* config, struct or51211_state* state = NULL; /* Allocate memory for the internal state */ - state = kmalloc(sizeof(struct or51211_state), GFP_KERNEL); + state = kzalloc(sizeof(struct or51211_state), GFP_KERNEL); if (state == NULL) return NULL; diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c index 3e08d985d6e..fb301151842 100644 --- a/drivers/media/dvb/frontends/s5h1409.c +++ b/drivers/media/dvb/frontends/s5h1409.c @@ -796,7 +796,7 @@ struct dvb_frontend *s5h1409_attach(const struct s5h1409_config *config, u16 reg; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct s5h1409_state), GFP_KERNEL); + state = kzalloc(sizeof(struct s5h1409_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/s5h1411.c b/drivers/media/dvb/frontends/s5h1411.c index 66e2dd6d6fe..d8adf1e3201 100644 --- a/drivers/media/dvb/frontends/s5h1411.c +++ b/drivers/media/dvb/frontends/s5h1411.c @@ -844,7 +844,7 @@ struct dvb_frontend *s5h1411_attach(const struct s5h1411_config *config, u16 reg; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct s5h1411_state), GFP_KERNEL); + state = kzalloc(sizeof(struct s5h1411_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/si21xx.c b/drivers/media/dvb/frontends/si21xx.c index 0bd16af8a6c..9552a22ccff 100644 --- a/drivers/media/dvb/frontends/si21xx.c +++ b/drivers/media/dvb/frontends/si21xx.c @@ -928,7 +928,7 @@ struct dvb_frontend *si21xx_attach(const struct si21xx_config *config, dprintk("%s\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct si21xx_state), GFP_KERNEL); + state = kzalloc(sizeof(struct si21xx_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/sp8870.c b/drivers/media/dvb/frontends/sp8870.c index 1c9a9b4051b..b85eb60a893 100644 --- a/drivers/media/dvb/frontends/sp8870.c +++ b/drivers/media/dvb/frontends/sp8870.c @@ -557,7 +557,7 @@ struct dvb_frontend* sp8870_attach(const struct sp8870_config* config, struct sp8870_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct sp8870_state), GFP_KERNEL); + state = kzalloc(sizeof(struct sp8870_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/sp887x.c b/drivers/media/dvb/frontends/sp887x.c index 559509ab4da..4a7c3d84260 100644 --- a/drivers/media/dvb/frontends/sp887x.c +++ b/drivers/media/dvb/frontends/sp887x.c @@ -557,7 +557,7 @@ struct dvb_frontend* sp887x_attach(const struct sp887x_config* config, struct sp887x_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct sp887x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct sp887x_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/stv0288.c b/drivers/media/dvb/frontends/stv0288.c index ff1194de34c..2930a5d6768 100644 --- a/drivers/media/dvb/frontends/stv0288.c +++ b/drivers/media/dvb/frontends/stv0288.c @@ -570,7 +570,7 @@ struct dvb_frontend *stv0288_attach(const struct stv0288_config *config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0288_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0288_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c index 62caf802ed9..4fd7479bb62 100644 --- a/drivers/media/dvb/frontends/stv0297.c +++ b/drivers/media/dvb/frontends/stv0297.c @@ -663,7 +663,7 @@ struct dvb_frontend *stv0297_attach(const struct stv0297_config *config, struct stv0297_state *state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0297_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0297_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c index 6c1cb1973c6..96887446972 100644 --- a/drivers/media/dvb/frontends/stv0299.c +++ b/drivers/media/dvb/frontends/stv0299.c @@ -667,7 +667,7 @@ struct dvb_frontend* stv0299_attach(const struct stv0299_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct stv0299_state), GFP_KERNEL); + state = kzalloc(sizeof(struct stv0299_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c index f648fdb64bb..f5d7b3277a2 100644 --- a/drivers/media/dvb/frontends/tda10021.c +++ b/drivers/media/dvb/frontends/tda10021.c @@ -413,7 +413,7 @@ struct dvb_frontend* tda10021_attach(const struct tda1002x_config* config, u8 id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10021_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10021_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/tda10048.c b/drivers/media/dvb/frontends/tda10048.c index cc8862ce4aa..4e2a7c8b2f6 100644 --- a/drivers/media/dvb/frontends/tda10048.c +++ b/drivers/media/dvb/frontends/tda10048.c @@ -1095,7 +1095,7 @@ struct dvb_frontend *tda10048_attach(const struct tda10048_config *config, dprintk(1, "%s()\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10048_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10048_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c index 4981cef8b44..f2a8abe0a24 100644 --- a/drivers/media/dvb/frontends/tda1004x.c +++ b/drivers/media/dvb/frontends/tda1004x.c @@ -1269,7 +1269,7 @@ struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); if (!state) { printk(KERN_ERR "Can't alocate memory for tda10045 state\n"); return NULL; @@ -1339,7 +1339,7 @@ struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config, int id; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda1004x_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda1004x_state), GFP_KERNEL); if (!state) { printk(KERN_ERR "Can't alocate memory for tda10046 state\n"); return NULL; diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c index a17ce3c4ad8..f2c8faac6f3 100644 --- a/drivers/media/dvb/frontends/tda10086.c +++ b/drivers/media/dvb/frontends/tda10086.c @@ -745,7 +745,7 @@ struct dvb_frontend* tda10086_attach(const struct tda10086_config* config, dprintk ("%s\n", __func__); /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda10086_state), GFP_KERNEL); if (!state) return NULL; diff --git a/drivers/media/dvb/frontends/tda8083.c b/drivers/media/dvb/frontends/tda8083.c index 5b843b2e67e..9369f7442f2 100644 --- a/drivers/media/dvb/frontends/tda8083.c +++ b/drivers/media/dvb/frontends/tda8083.c @@ -417,7 +417,7 @@ struct dvb_frontend* tda8083_attach(const struct tda8083_config* config, struct tda8083_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct tda8083_state), GFP_KERNEL); + state = kzalloc(sizeof(struct tda8083_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/ves1820.c b/drivers/media/dvb/frontends/ves1820.c index a184597f1d9..6e78e486551 100644 --- a/drivers/media/dvb/frontends/ves1820.c +++ b/drivers/media/dvb/frontends/ves1820.c @@ -374,7 +374,7 @@ struct dvb_frontend* ves1820_attach(const struct ves1820_config* config, struct ves1820_state* state = NULL; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct ves1820_state), GFP_KERNEL); + state = kzalloc(sizeof(struct ves1820_state), GFP_KERNEL); if (state == NULL) goto error; diff --git a/drivers/media/dvb/frontends/ves1x93.c b/drivers/media/dvb/frontends/ves1x93.c index bd558960bd8..8d7854c2fb0 100644 --- a/drivers/media/dvb/frontends/ves1x93.c +++ b/drivers/media/dvb/frontends/ves1x93.c @@ -456,7 +456,7 @@ struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config, u8 identity; /* allocate memory for the internal state */ - state = kmalloc(sizeof(struct ves1x93_state), GFP_KERNEL); + state = kzalloc(sizeof(struct ves1x93_state), GFP_KERNEL); if (state == NULL) goto error; /* setup the state */ diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index 148b6f7f6cb..66f5c1fb307 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -98,7 +98,6 @@ static int zl10353_read_register(struct zl10353_state *state, u8 reg) static void zl10353_dump_regs(struct dvb_frontend *fe) { struct zl10353_state *state = fe->demodulator_priv; - char buf[52], buf2[4]; int ret; u8 reg; @@ -106,19 +105,18 @@ static void zl10353_dump_regs(struct dvb_frontend *fe) for (reg = 0; ; reg++) { if (reg % 16 == 0) { if (reg) - printk(KERN_DEBUG "%s\n", buf); - sprintf(buf, "%02x: ", reg); + printk(KERN_CONT "\n"); + printk(KERN_DEBUG "%02x:", reg); } ret = zl10353_read_register(state, reg); if (ret >= 0) - sprintf(buf2, "%02x ", (u8)ret); + printk(KERN_CONT " %02x", (u8)ret); else - strcpy(buf2, "-- "); - strcat(buf, buf2); + printk(KERN_CONT " --"); if (reg == 0xff) break; } - printk(KERN_DEBUG "%s\n", buf); + printk(KERN_CONT "\n"); } static void zl10353_calc_nominal_rate(struct dvb_frontend *fe, diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig index dd863f26167..88847d1dcbb 100644 --- a/drivers/media/dvb/siano/Kconfig +++ b/drivers/media/dvb/siano/Kconfig @@ -4,7 +4,7 @@ config DVB_SIANO_SMS1XXX tristate "Siano SMS1XXX USB dongle support" - depends on DVB_CORE && USB + depends on DVB_CORE && USB && INPUT ---help--- Choose Y here if you have a USB dongle with a SMS1XXX chipset. diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index d8b15d583bd..0420e2885e7 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -116,99 +116,21 @@ static inline void sms_gpio_assign_11xx_default_led_config( int sms_board_event(struct smscore_device_t *coredev, enum SMS_BOARD_EVENTS gevent) { - int board_id = smscore_get_board_id(coredev); - struct sms_board *board = sms_get_board(board_id); struct smscore_gpio_config MyGpioConfig; sms_gpio_assign_11xx_default_led_config(&MyGpioConfig); switch (gevent) { case BOARD_EVENT_POWER_INIT: /* including hotplug */ - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - /* set I/O and turn off all LEDs */ - smscore_gpio_configure(coredev, - board->board_cfg.leds_power, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 0); - smscore_gpio_configure(coredev, board->board_cfg.led0, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 0); - smscore_gpio_configure(coredev, board->board_cfg.led1, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - /* set I/O and turn off LNA */ - smscore_gpio_configure(coredev, - board->board_cfg.foreign_lna0_ctrl, - &MyGpioConfig); - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 0); - break; - } break; /* BOARD_EVENT_BIND */ case BOARD_EVENT_POWER_SUSPEND: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 0); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 0); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 0); - break; - } break; /* BOARD_EVENT_POWER_SUSPEND */ case BOARD_EVENT_POWER_RESUME: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 1); - break; - } break; /* BOARD_EVENT_POWER_RESUME */ case BOARD_EVENT_BIND: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.leds_power, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led0, 1); - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: - case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: - smscore_gpio_set_level(coredev, - board->board_cfg.foreign_lna0_ctrl, - 1); - break; - } break; /* BOARD_EVENT_BIND */ case BOARD_EVENT_SCAN_PROG: @@ -218,20 +140,8 @@ int sms_board_event(struct smscore_device_t *coredev, case BOARD_EVENT_EMERGENCY_WARNING_SIGNAL: break; /* BOARD_EVENT_EMERGENCY_WARNING_SIGNAL */ case BOARD_EVENT_FE_LOCK: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 1); - break; - } break; /* BOARD_EVENT_FE_LOCK */ case BOARD_EVENT_FE_UNLOCK: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - } break; /* BOARD_EVENT_FE_UNLOCK */ case BOARD_EVENT_DEMOD_LOCK: break; /* BOARD_EVENT_DEMOD_LOCK */ @@ -248,20 +158,8 @@ int sms_board_event(struct smscore_device_t *coredev, case BOARD_EVENT_RECEPTION_LOST_0: break; /* BOARD_EVENT_RECEPTION_LOST_0 */ case BOARD_EVENT_MULTIPLEX_OK: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 1); - break; - } break; /* BOARD_EVENT_MULTIPLEX_OK */ case BOARD_EVENT_MULTIPLEX_ERRORS: - switch (board_id) { - case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: - smscore_gpio_set_level(coredev, - board->board_cfg.led1, 0); - break; - } break; /* BOARD_EVENT_MULTIPLEX_ERRORS */ default: diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index a246903c334..bd9ab9d0d12 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -816,7 +816,7 @@ int smscore_set_device_mode(struct smscore_device_t *coredev, int mode) sms_debug("set device mode to %d", mode); if (coredev->device_flags & SMS_DEVICE_FAMILY2) { - if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_RAW_TUNER) { + if (mode < DEVICE_MODE_DVBT || mode >= DEVICE_MODE_RAW_TUNER) { sms_err("invalid mode specified %d", mode); return -EINVAL; } diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 84b6fc15519..dcf9fa9264b 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -920,6 +920,8 @@ source "drivers/media/video/pwc/Kconfig" config USB_ZR364XX tristate "USB ZR364XX Camera support" depends on VIDEO_V4L2 + select VIDEOBUF_GEN + select VIDEOBUF_VMALLOC ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index 10dbd4a11b3..9e39bc5f7b0 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -992,7 +992,7 @@ static int accept_bwqcam(struct parport *port) if (parport[0] && strncmp(parport[0], "auto", 4) != 0) { /* user gave parport parameters */ - for(n=0; parport[n] && n<MAX_CAMS; n++){ + for (n = 0; n < MAX_CAMS && parport[n]; n++) { char *ep; unsigned long r; r = simple_strtoul(parport[n], &ep, 0); diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c index 5136df19833..93f0dae0135 100644 --- a/drivers/media/video/cx18/cx18-controls.c +++ b/drivers/media/video/cx18/cx18-controls.c @@ -20,6 +20,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ +#include <linux/kernel.h> #include "cx18-driver.h" #include "cx18-cards.h" @@ -317,7 +318,7 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) idx = p.audio_properties & 0x03; /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ - if (idx < sizeof(freqs)) + if (idx < ARRAY_SIZE(freqs)) cx18_call_all(cx, audio, s_clock_freq, freqs[idx]); return err; } diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index e0cf21e0b1b..1a1048b18f7 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -1715,6 +1715,8 @@ static struct video_device cx23885_mpeg_template = { .fops = &mpeg_fops, .ioctl_ops = &mpeg_ioctl_ops, .minor = -1, + .tvnorms = CX23885_NORMS, + .current_norm = V4L2_STD_NTSC_M, }; void cx23885_417_unregister(struct cx23885_dev *dev) diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index a5cc1c1fc2d..39465301ec9 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -3003,6 +3003,14 @@ 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_GENIATECH_X8000_MT: + /* FIXME: For this board, the xc3028 never recovers after being + powered down (the reset GPIO probably is not set properly). + We don't have access to the hardware so we cannot determine + which GPIO is used for xc3028, so just disable power xc3028 + power management for now */ + ctl->disable_power_mgmt = 1; + break; case CX88_BOARD_WINFAST_TV2000_XP_GLOBAL: case CX88_BOARD_PROLINK_PV_GLOBAL_XTREME: case CX88_BOARD_PROLINK_PV_8000GT: diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index c44e8760021..e237b507659 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -501,6 +501,7 @@ static struct zl10353_config cx88_pinnacle_hybrid_pctv = { static struct zl10353_config cx88_geniatech_x8000_mt = { .demod_address = (0x1e >> 1), .no_tuner = 1, + .disable_i2c_gate_ctrl = 1, }; static struct s5h1411_config dvico_fusionhdtv7_config = { diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c index da4e3912cd3..7172dcf2a4f 100644 --- a/drivers/media/video/cx88/cx88-mpeg.c +++ b/drivers/media/video/cx88/cx88-mpeg.c @@ -116,6 +116,10 @@ static int cx8802_start_dma(struct cx8802_dev *dev, udelay(100); break; case CX88_BOARD_HAUPPAUGE_HVR1300: + /* Enable MPEG parallel IO and video signal pins */ + cx_write(MO_PINMUX_IO, 0x88); + cx_write(TS_SOP_STAT, 0); + cx_write(TS_VALERR_CNTRL, 0); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: /* Enable MPEG parallel IO and video signal pins */ diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 320f1f60276..ed281f56594 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -218,7 +218,7 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { struct em28xx_board em28xx_boards[] = { [EM2750_BOARD_UNKNOWN] = { .name = "EM2710/EM2750/EM2751 webcam grabber", - .xclk = EM28XX_XCLK_FREQUENCY_48MHZ, + .xclk = EM28XX_XCLK_FREQUENCY_20MHZ, .tuner_type = TUNER_ABSENT, .is_webcam = 1, .input = { { @@ -622,22 +622,27 @@ struct em28xx_board em28xx_boards[] = { }, [EM2861_BOARD_PLEXTOR_PX_TV100U] = { .name = "Plextor ConvertX PX-TV100U", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_TNF_5335MF, + .xclk = EM28XX_XCLK_I2S_MSB_TIMING | + EM28XX_XCLK_FREQUENCY_12MHZ, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_TVP5150, + .has_msp34xx = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_COMPOSITE1, .vmux = TVP5150_COMPOSITE1, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, }, { .type = EM28XX_VMUX_SVIDEO, .vmux = TVP5150_SVIDEO, .amux = EM28XX_AMUX_LINE_IN, + .gpio = pinnacle_hybrid_pro_analog, } }, }, @@ -1544,6 +1549,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2750_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2800), .driver_info = EM2800_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2710), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2820), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2821), @@ -1761,6 +1768,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) __be16 version_be; u16 version; + /* Micron sensor detection */ dev->i2c_client.addr = 0xba >> 1; cmd = 0; i2c_master_send(&dev->i2c_client, &cmd, 1); @@ -1769,15 +1777,27 @@ static int em28xx_hint_sensor(struct em28xx *dev) return -EINVAL; version = be16_to_cpu(version_be); - switch (version) { - case 0x8243: /* mt9v011 640x480 1.3 Mpix sensor */ + case 0x8232: /* mt9v011 640x480 1.3 Mpix sensor */ + case 0x8243: /* mt9v011 rev B 640x480 1.3 Mpix sensor */ dev->model = EM2820_BOARD_SILVERCREST_WEBCAM; + em28xx_set_model(dev); + sensor_name = "mt9v011"; dev->em28xx_sensor = EM28XX_MT9V011; dev->sensor_xres = 640; dev->sensor_yres = 480; - dev->sensor_xtal = 6300000; + /* + * FIXME: mt9v011 uses I2S speed as xtal clk - at least with + * the Silvercrest cam I have here for testing - for higher + * resolutions, a high clock cause horizontal artifacts, so we + * need to use a lower xclk frequency. + * Yet, it would be possible to adjust xclk depending on the + * desired resolution, since this affects directly the + * frame rate. + */ + dev->board.xclk = EM28XX_XCLK_FREQUENCY_4_3MHZ; + dev->sensor_xtal = 4300000; /* probably means GRGB 16 bit bayer */ dev->vinmode = 0x0d; @@ -1786,6 +1806,8 @@ static int em28xx_hint_sensor(struct em28xx *dev) break; case 0x8431: dev->model = EM2750_BOARD_UNKNOWN; + em28xx_set_model(dev); + sensor_name = "mt9m001"; dev->em28xx_sensor = EM28XX_MT9M001; em28xx_initialize_mt9m001(dev); @@ -1802,6 +1824,9 @@ static int em28xx_hint_sensor(struct em28xx *dev) return -EINVAL; } + /* Setup webcam defaults */ + em28xx_pre_card_setup(dev); + em28xx_errdev("Sensor is %s, using model %s entry.\n", sensor_name, em28xx_boards[dev->model].name); @@ -1813,60 +1838,6 @@ static int em28xx_hint_sensor(struct em28xx *dev) */ void em28xx_pre_card_setup(struct em28xx *dev) { - int rc; - - em28xx_set_model(dev); - - em28xx_info("Identified as %s (card=%d)\n", - dev->board.name, dev->model); - - /* Set the default GPO/GPIO for legacy devices */ - dev->reg_gpo_num = EM2880_R04_GPO; - dev->reg_gpio_num = EM28XX_R08_GPIO; - - dev->wait_after_write = 5; - - /* Based on the Chip ID, set the device configuration */ - rc = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); - if (rc > 0) { - dev->chip_id = rc; - - switch (dev->chip_id) { - case CHIP_ID_EM2750: - em28xx_info("chip ID is em2750\n"); - break; - case CHIP_ID_EM2820: - em28xx_info("chip ID is em2710 or em2820\n"); - break; - case CHIP_ID_EM2840: - em28xx_info("chip ID is em2840\n"); - break; - case CHIP_ID_EM2860: - em28xx_info("chip ID is em2860\n"); - break; - case CHIP_ID_EM2870: - em28xx_info("chip ID is em2870\n"); - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2874: - em28xx_info("chip ID is em2874\n"); - dev->reg_gpio_num = EM2874_R80_GPIO; - dev->wait_after_write = 0; - break; - case CHIP_ID_EM2883: - em28xx_info("chip ID is em2882/em2883\n"); - dev->wait_after_write = 0; - break; - default: - em28xx_info("em28xx chip ID = %d\n", dev->chip_id); - } - } - - /* Prepopulate cached GPO register content */ - rc = em28xx_read_reg(dev, dev->reg_gpo_num); - if (rc >= 0) - dev->reg_gpo = rc; - /* Set the initial XCLK and I2C clock values based on the board definition */ em28xx_write_reg(dev, EM28XX_R0F_XCLK, dev->board.xclk & 0x7f); @@ -1876,9 +1847,8 @@ void em28xx_pre_card_setup(struct em28xx *dev) /* request some modules */ switch (dev->model) { case EM2861_BOARD_PLEXTOR_PX_TV100U: - /* FIXME guess */ - /* Turn on analog audio output */ - em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd); + /* Sets the msp34xx I2S speed */ + dev->i2s_speed = 2048000; break; case EM2861_BOARD_KWORLD_PVRTV_300U: case EM2880_BOARD_KWORLD_DVB_305U: @@ -2216,7 +2186,20 @@ void em28xx_register_i2c_ir(struct em28xx *dev) void em28xx_card_setup(struct em28xx *dev) { - em28xx_set_model(dev); + /* + * If the device can be a webcam, seek for a sensor. + * If sensor is not found, then it isn't a webcam. + */ + if (dev->board.is_webcam) { + if (em28xx_hint_sensor(dev) < 0) + dev->board.is_webcam = 0; + else + dev->progressive = 1; + } else + em28xx_set_model(dev); + + em28xx_info("Identified as %s (card=%d)\n", + dev->board.name, dev->model); dev->tuner_type = em28xx_boards[dev->model].tuner_type; if (em28xx_boards[dev->model].tuner_addr) @@ -2290,10 +2273,6 @@ void em28xx_card_setup(struct em28xx *dev) em28xx_gpio_set(dev, dev->board.tuner_gpio); em28xx_set_mode(dev, EM28XX_ANALOG_MODE); break; - case EM2820_BOARD_SILVERCREST_WEBCAM: - /* FIXME: need to document the registers bellow */ - em28xx_write_reg(dev, 0x0d, 0x42); - em28xx_write_reg(dev, 0x13, 0x08); } if (dev->board.has_snapshot_button) @@ -2433,7 +2412,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, int minor) { struct em28xx *dev = *devhandle; - int retval = -ENOMEM; + int retval; int errCode; dev->udev = udev; @@ -2450,6 +2429,58 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->em28xx_read_reg_req = em28xx_read_reg_req; dev->board.is_em2800 = em28xx_boards[dev->model].is_em2800; + em28xx_set_model(dev); + + /* Set the default GPO/GPIO for legacy devices */ + dev->reg_gpo_num = EM2880_R04_GPO; + dev->reg_gpio_num = EM28XX_R08_GPIO; + + dev->wait_after_write = 5; + + /* Based on the Chip ID, set the device configuration */ + retval = em28xx_read_reg(dev, EM28XX_R0A_CHIPID); + if (retval > 0) { + dev->chip_id = retval; + + switch (dev->chip_id) { + case CHIP_ID_EM2710: + em28xx_info("chip ID is em2710\n"); + break; + case CHIP_ID_EM2750: + em28xx_info("chip ID is em2750\n"); + break; + case CHIP_ID_EM2820: + em28xx_info("chip ID is em2820 (or em2710)\n"); + break; + case CHIP_ID_EM2840: + em28xx_info("chip ID is em2840\n"); + break; + case CHIP_ID_EM2860: + em28xx_info("chip ID is em2860\n"); + break; + case CHIP_ID_EM2870: + em28xx_info("chip ID is em2870\n"); + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2874: + em28xx_info("chip ID is em2874\n"); + dev->reg_gpio_num = EM2874_R80_GPIO; + dev->wait_after_write = 0; + break; + case CHIP_ID_EM2883: + em28xx_info("chip ID is em2882/em2883\n"); + dev->wait_after_write = 0; + break; + default: + em28xx_info("em28xx chip ID = %d\n", dev->chip_id); + } + } + + /* Prepopulate cached GPO register content */ + retval = em28xx_read_reg(dev, dev->reg_gpo_num); + if (retval >= 0) + dev->reg_gpo = retval; + em28xx_pre_card_setup(dev); if (!dev->board.is_em2800) { @@ -2484,14 +2515,6 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->vinmode = 0x10; dev->vinctl = 0x11; - /* - * If the device can be a webcam, seek for a sensor. - * If sensor is not found, then it isn't a webcam. - */ - if (dev->board.is_webcam) - if (em28xx_hint_sensor(dev) < 0) - dev->board.is_webcam = 0; - /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 5b78e199abd..98e140b5d95 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -632,6 +632,9 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } + if (dev->board.is_webcam) + rc = em28xx_write_reg(dev, 0x13, 0x0c); + /* enable video capture */ rc = em28xx_write_reg(dev, 0x48, 0x00); @@ -720,7 +723,10 @@ int em28xx_resolution_set(struct em28xx *dev) { int width, height; width = norm_maxw(dev); - height = norm_maxh(dev) >> 1; + height = norm_maxh(dev); + + if (!dev->progressive) + height >>= norm_maxh(dev); em28xx_set_outfmt(dev); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index cf0ac7f2a30..d603575431b 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -478,7 +478,6 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_KWORLD_DVB_310U: - case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_with_xc3028, &dev->i2c_adap); @@ -488,6 +487,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, &dev->i2c_adap); diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index a2676d63cfd..6bf84bd787d 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -176,7 +176,8 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { - CHIP_ID_EM2820 = 18, /* Also used by em2710 */ + CHIP_ID_EM2710 = 17, + CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ CHIP_ID_EM2840 = 20, CHIP_ID_EM2750 = 33, CHIP_ID_EM2860 = 34, diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index ff37b4c15f4..ab079d9256c 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -194,15 +194,24 @@ static void em28xx_copy_video(struct em28xx *dev, startread = p; remain = len; - /* Interlaces frame */ - if (buf->top_field) + if (dev->progressive) fieldstart = outp; - else - fieldstart = outp + bytesperline; + else { + /* Interlaces two half frames */ + if (buf->top_field) + fieldstart = outp; + else + fieldstart = outp + bytesperline; + } linesdone = dma_q->pos / bytesperline; currlinedone = dma_q->pos % bytesperline; - offset = linesdone * bytesperline * 2 + currlinedone; + + if (dev->progressive) + offset = linesdone * bytesperline + currlinedone; + else + offset = linesdone * bytesperline * 2 + currlinedone; + startwrite = fieldstart + offset; lencopy = bytesperline - currlinedone; lencopy = lencopy > remain ? remain : lencopy; @@ -376,7 +385,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], len, (p[2] & 1) ? "odd" : "even"); - if (!(p[2] & 1)) { + if (dev->progressive || !(p[2] & 1)) { if (buf != NULL) buffer_filled(dev, dma_q, buf); get_next_buf(dma_q, &buf); @@ -689,7 +698,10 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* FIXME: TOP? NONE? BOTTOM? ALTENATE? */ - f->fmt.pix.field = dev->interlaced ? + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; mutex_unlock(&dev->lock); @@ -753,7 +765,11 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3; f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height; f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - f->fmt.pix.field = V4L2_FIELD_INTERLACED; + if (dev->progressive) + f->fmt.pix.field = V4L2_FIELD_NONE; + else + f->fmt.pix.field = dev->interlaced ? + V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; return 0; } @@ -846,6 +862,41 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) return 0; } +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc = 0; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (dev->board.is_webcam) + rc = v4l2_device_call_until_err(&dev->v4l2_dev, 0, + video, g_parm, p); + else + v4l2_video_std_frame_period(dev->norm, + &p->parm.capture.timeperframe); + + return rc; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *p) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + + if (!dev->board.is_webcam) + return -EINVAL; + + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return v4l2_device_call_until_err(&dev->v4l2_dev, 0, video, s_parm, p); +} + static const char *iname[] = { [EM28XX_VMUX_COMPOSITE1] = "Composite1", [EM28XX_VMUX_COMPOSITE2] = "Composite2", @@ -1624,6 +1675,7 @@ static int em28xx_v4l2_open(struct file *filp) struct em28xx *dev; enum v4l2_buf_type fh_type; struct em28xx_fh *fh; + enum v4l2_field field; dev = em28xx_get_device(minor, &fh_type, &radio); @@ -1665,8 +1717,13 @@ static int em28xx_v4l2_open(struct file *filp) dev->users++; + if (dev->progressive) + field = V4L2_FIELD_NONE; + else + field = V4L2_FIELD_INTERLACED; + videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED, + NULL, &dev->slock, fh->type, field, sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); @@ -1885,6 +1942,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_qbuf = vidioc_qbuf, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_s_std = vidioc_s_std, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 45bd513f62d..8c2dc38bca9 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -484,6 +484,9 @@ struct em28xx { int sensor_xres, sensor_yres; int sensor_xtal; + /* Allows progressive (e. g. non-interlaced) mode */ + int progressive; + /* Vinmode/Vinctl used at the driver */ int vinmode, vinctl; diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index ccd47f57f42..d678765cbba 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -1220,6 +1220,8 @@ static const struct video_device hdpvr_video_template = { V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I | V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_60, + .current_norm = V4L2_STD_NTSC | V4L2_STD_PAL_M | + V4L2_STD_PAL_60, }; int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent, diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c index a3b77ed3f08..4a9c8ce0ecb 100644 --- a/drivers/media/video/ivtv/ivtv-controls.c +++ b/drivers/media/video/ivtv/ivtv-controls.c @@ -17,6 +17,7 @@ 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 "ivtv-driver.h" #include "ivtv-cards.h" @@ -281,7 +282,7 @@ int ivtv_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c) idx = p.audio_properties & 0x03; /* The audio clock of the digitizer must match the codec sample rate otherwise you get some very strange effects. */ - if (idx < sizeof(freqs)) + if (idx < ARRAY_SIZE(freqs)) ivtv_call_all(itv, audio, s_clock_freq, freqs[idx]); return err; } diff --git a/drivers/media/video/mt9v011.c b/drivers/media/video/mt9v011.c index b2260de645f..cc85f77a570 100644 --- a/drivers/media/video/mt9v011.c +++ b/drivers/media/video/mt9v011.c @@ -52,13 +52,34 @@ static struct v4l2_queryctrl mt9v011_qctrl[] = { .step = 1, .default_value = 0, .flags = 0, - }, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + .flags = 0, + }, { + } }; struct mt9v011 { struct v4l2_subdev sd; unsigned width, height; unsigned xtal; + unsigned hflip:1; + unsigned vflip:1; u16 global_gain, red_bal, blue_bal; }; @@ -131,7 +152,6 @@ static const struct i2c_reg_value mt9v011_init_default[] = { { R0A_MT9V011_CLK_SPEED, 0x0000 }, { R1E_MT9V011_DIGITAL_ZOOM, 0x0000 }, - { R20_MT9V011_READ_MODE, 0x1000 }, { R07_MT9V011_OUT_CTRL, 0x0002 }, /* chip enable */ }; @@ -156,7 +176,7 @@ static void set_balance(struct v4l2_subdev *sd) mt9v011_write(sd, R2D_MT9V011_RED_GAIN, red_gain); } -static void calc_fps(struct v4l2_subdev *sd) +static void calc_fps(struct v4l2_subdev *sd, u32 *numerator, u32 *denominator) { struct mt9v011 *core = to_mt9v011(sd); unsigned height, width, hblank, vblank, speed; @@ -179,6 +199,51 @@ static void calc_fps(struct v4l2_subdev *sd) v4l2_dbg(1, debug, sd, "Programmed to %u.%03u fps (%d pixel clcks)\n", tmp / 1000, tmp % 1000, t_time); + + if (numerator && denominator) { + *numerator = 1000; + *denominator = (u32)frames_per_ms; + } +} + +static u16 calc_speed(struct v4l2_subdev *sd, u32 numerator, u32 denominator) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned height, width, hblank, vblank; + unsigned row_time, line_time; + u64 t_time, speed; + + /* Avoid bogus calculus */ + if (!numerator || !denominator) + return 0; + + height = mt9v011_read(sd, R03_MT9V011_HEIGHT); + width = mt9v011_read(sd, R04_MT9V011_WIDTH); + hblank = mt9v011_read(sd, R05_MT9V011_HBLANK); + vblank = mt9v011_read(sd, R06_MT9V011_VBLANK); + + row_time = width + 113 + hblank; + line_time = height + vblank + 1; + + t_time = core->xtal * ((u64)numerator); + /* round to the closest value */ + t_time += denominator / 2; + do_div(t_time, denominator); + + speed = t_time; + do_div(speed, row_time * line_time); + + /* Avoid having a negative value for speed */ + if (speed < 2) + speed = 0; + else + speed -= 2; + + /* Avoid speed overflow */ + if (speed > 15) + return 15; + + return (u16)speed; } static void set_res(struct v4l2_subdev *sd) @@ -207,9 +272,23 @@ static void set_res(struct v4l2_subdev *sd) mt9v011_write(sd, R03_MT9V011_HEIGHT, core->height); mt9v011_write(sd, R06_MT9V011_VBLANK, 508 - core->height); - calc_fps(sd); + calc_fps(sd, NULL, NULL); }; +static void set_read_mode(struct v4l2_subdev *sd) +{ + struct mt9v011 *core = to_mt9v011(sd); + unsigned mode = 0x1000; + + if (core->hflip) + mode |= 0x4000; + + if (core->vflip) + mode |= 0x8000; + + mt9v011_write(sd, R20_MT9V011_READ_MODE, mode); +} + static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) { int i; @@ -220,6 +299,7 @@ static int mt9v011_reset(struct v4l2_subdev *sd, u32 val) set_balance(sd); set_res(sd); + set_read_mode(sd); return 0; }; @@ -240,6 +320,12 @@ static int mt9v011_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_BLUE_BALANCE: ctrl->value = core->blue_bal; return 0; + case V4L2_CID_HFLIP: + ctrl->value = core->hflip ? 1 : 0; + return 0; + case V4L2_CID_VFLIP: + ctrl->value = core->vflip ? 1 : 0; + return 0; } return -EINVAL; } @@ -288,6 +374,14 @@ static int mt9v011_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_BLUE_BALANCE: core->blue_bal = ctrl->value; break; + case V4L2_CID_HFLIP: + core->hflip = ctrl->value; + set_read_mode(sd); + return 0; + case V4L2_CID_VFLIP: + core->vflip = ctrl->value; + set_read_mode(sd); + return 0; default: return -EINVAL; } @@ -322,6 +416,44 @@ static int mt9v011_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) return 0; } +static int mt9v011_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + memset(cp, 0, sizeof(struct v4l2_captureparm)); + cp->capability = V4L2_CAP_TIMEPERFRAME; + calc_fps(sd, + &cp->timeperframe.numerator, + &cp->timeperframe.denominator); + + return 0; +} + +static int mt9v011_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms) +{ + struct v4l2_captureparm *cp = &parms->parm.capture; + struct v4l2_fract *tpf = &cp->timeperframe; + u16 speed; + + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cp->extendedmode != 0) + return -EINVAL; + + speed = calc_speed(sd, tpf->numerator, tpf->denominator); + + mt9v011_write(sd, R0A_MT9V011_CLK_SPEED, speed); + v4l2_dbg(1, debug, sd, "Setting speed to %d\n", speed); + + /* Recalculate and update fps info */ + calc_fps(sd, &tpf->numerator, &tpf->denominator); + + return 0; +} + static int mt9v011_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) { struct v4l2_pix_format *pix = &fmt->fmt.pix; @@ -393,10 +525,13 @@ static int mt9v011_s_register(struct v4l2_subdev *sd, static int mt9v011_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip) { + u16 version; struct i2c_client *client = v4l2_get_subdevdata(sd); + version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_MT9V011, - MT9V011_VERSION); + version); } static const struct v4l2_subdev_core_ops mt9v011_core_ops = { @@ -416,6 +551,8 @@ static const struct v4l2_subdev_video_ops mt9v011_video_ops = { .enum_fmt = mt9v011_enum_fmt, .try_fmt = mt9v011_try_fmt, .s_fmt = mt9v011_s_fmt, + .g_parm = mt9v011_g_parm, + .s_parm = mt9v011_s_parm, }; static const struct v4l2_subdev_ops mt9v011_ops = { @@ -449,8 +586,9 @@ static int mt9v011_probe(struct i2c_client *c, /* Check if the sensor is really a MT9V011 */ version = mt9v011_read(sd, R00_MT9V011_CHIP_VERSION); - if (version != MT9V011_VERSION) { - v4l2_info(sd, "*** unknown micron chip detected (0x%04x.\n", + if ((version != MT9V011_VERSION) && + (version != MT9V011_REV_B_VERSION)) { + v4l2_info(sd, "*** unknown micron chip detected (0x%04x).\n", version); kfree(core); return -EINVAL; @@ -461,8 +599,8 @@ static int mt9v011_probe(struct i2c_client *c, core->height = 480; core->xtal = 27000000; /* Hz */ - v4l_info(c, "chip found @ 0x%02x (%s)\n", - c->addr << 1, c->adapter->name); + v4l_info(c, "chip found @ 0x%02x (%s - chip version 0x%04x)\n", + c->addr << 1, c->adapter->name, version); return 0; } diff --git a/drivers/media/video/mt9v011.h b/drivers/media/video/mt9v011.h index 9e443ee3055..3350fd6083c 100644 --- a/drivers/media/video/mt9v011.h +++ b/drivers/media/video/mt9v011.h @@ -30,6 +30,7 @@ #define R35_MT9V011_GLOBAL_GAIN 0x35 #define RF1_MT9V011_CHIP_ENABLE 0xf1 -#define MT9V011_VERSION 0x8243 +#define MT9V011_VERSION 0x8232 +#define MT9V011_REV_B_VERSION 0x8243 #endif diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 2d075205bdf..736c31d2319 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -234,6 +234,7 @@ static int mx1_camera_setup_dma(struct mx1_camera_dev *pcdev) return ret; } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -241,13 +242,10 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct mx1_camera_dev *pcdev = ici->priv; struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); - unsigned long flags; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); - spin_lock_irqsave(&pcdev->lock, flags); - list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_ACTIVE; @@ -264,8 +262,6 @@ static void mx1_videobuf_queue(struct videobuf_queue *vq, __raw_writel(temp, pcdev->base + CSICR1); } } - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void mx1_videobuf_release(struct videobuf_queue *vq, diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index e605c076ed8..9770cb7932c 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -332,7 +332,10 @@ static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc) } } -/* Called with .vb_lock held */ +/* + * Called with .vb_lock mutex held and + * under spinlock_irqsave(&mx3_cam->lock, ...) + */ static void mx3_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -346,7 +349,8 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, struct idmac_video_param *video = &ichan->params.video; const struct soc_camera_data_format *data_fmt = icd->current_fmt; dma_cookie_t cookie; - unsigned long flags; + + BUG_ON(!irqs_disabled()); /* This is the configuration of one sg-element */ video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc); @@ -359,8 +363,6 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, memset((void *)vb->baddr, 0xaa, vb->bsize); #endif - spin_lock_irqsave(&mx3_cam->lock, flags); - list_add_tail(&vb->queue, &mx3_cam->capture); if (!mx3_cam->active) { @@ -370,24 +372,23 @@ static void mx3_videobuf_queue(struct videobuf_queue *vq, vb->state = VIDEOBUF_QUEUED; } - spin_unlock_irqrestore(&mx3_cam->lock, flags); + spin_unlock_irq(&mx3_cam->lock); cookie = txd->tx_submit(txd); dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg)); + + spin_lock_irq(&mx3_cam->lock); + if (cookie >= 0) return; /* Submit error */ vb->state = VIDEOBUF_PREPARED; - spin_lock_irqsave(&mx3_cam->lock, flags); - list_del_init(&vb->queue); if (mx3_cam->active == buf) mx3_cam->active = NULL; - - spin_unlock_irqrestore(&mx3_cam->lock, flags); } /* Called with .vb_lock held */ diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 46e0d8ad880..016bb45ba0c 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -612,6 +612,7 @@ static void pxa_camera_stop_capture(struct pxa_camera_dev *pcdev) dev_dbg(pcdev->soc_host.dev, "%s\n", __func__); } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { @@ -619,13 +620,10 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct pxa_camera_dev *pcdev = ici->priv; struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); - unsigned long flags; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d active=%p\n", __func__, vb, vb->baddr, vb->bsize, pcdev->active); - spin_lock_irqsave(&pcdev->lock, flags); - list_add_tail(&vb->queue, &pcdev->capture); vb->state = VIDEOBUF_ACTIVE; @@ -633,8 +631,6 @@ static void pxa_videobuf_queue(struct videobuf_queue *vq, if (!pcdev->active) pxa_camera_start_capture(pcdev); - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void pxa_videobuf_release(struct videobuf_queue *vq, @@ -1579,6 +1575,7 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->mclk = 20000000; } + pcdev->soc_host.dev = &pdev->dev; pcdev->mclk_divisor = mclk_get_divisor(pcdev); INIT_LIST_HEAD(&pcdev->capture); @@ -1644,7 +1641,6 @@ static int __devinit pxa_camera_probe(struct platform_device *pdev) pcdev->soc_host.drv_name = PXA_CAM_DRV_NAME; pcdev->soc_host.ops = &pxa_soc_camera_host_ops; pcdev->soc_host.priv = pcdev; - pcdev->soc_host.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; err = soc_camera_host_register(&pcdev->soc_host); diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 06861b782b9..6eebe3ef97d 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -3331,8 +3331,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200100, }, }, - [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { - .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid", + [SAA7134_BOARD_HAUPPAUGE_HVR1150] = { + .name = "Hauppauge WinTV-HVR1150 ATSC/QAM-Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -3363,8 +3363,8 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0800100, /* GPIO 23 HI for FM */ }, }, - [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = { - .name = "Hauppauge WinTV-HVR1110r3 DVB-T/Hybrid", + [SAA7134_BOARD_HAUPPAUGE_HVR1120] = { + .name = "Hauppauge WinTV-HVR1120 DVB-T/Hybrid", .audio_clock = 0x00187de7, .tuner_type = TUNER_PHILIPS_TDA8290, .radio_type = UNSET, @@ -5862,31 +5862,31 @@ struct pci_device_id saa7134_pci_tbl[] = { .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6706, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6707, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6708, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1150, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x6709, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, .subvendor = 0x0070, .subdevice = 0x670a, - .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3, + .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120, },{ .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7133, @@ -6363,8 +6363,8 @@ static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev, switch (command) { case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */ switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg); break; default: @@ -6384,8 +6384,8 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev, int ret; switch (dev->board) { + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* tda8290 + tda18271 */ ret = saa7134_tda8290_18271_callback(dev, command, arg); break; @@ -6427,7 +6427,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) switch (tv.model) { case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ - case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ + case 67201: /* WinTV-HVR1150 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */ case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ @@ -6435,7 +6435,7 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data) case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */ - case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ + case 67651: /* WinTV-HVR1150 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */ break; default: @@ -6625,8 +6625,8 @@ int saa7134_board_init1(struct saa7134_dev *dev) saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00); break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: /* GPIO 26 high for digital, low for analog */ saa7134_set_gpio(dev, 26, 0); msleep(1); @@ -6891,8 +6891,8 @@ int saa7134_board_init2(struct saa7134_dev *dev) dev->name, saa7134_boards[dev->board].name); } break; + case SAA7134_BOARD_HAUPPAUGE_HVR1150: case SAA7134_BOARD_HAUPPAUGE_HVR1120: - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: hauppauge_eeprom(dev, dev->eedata+0x80); break; case SAA7134_BOARD_HAUPPAUGE_HVR1110: diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index 31930f26ffc..98f3efd1e94 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -1119,7 +1119,7 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_2) < 0) goto dettach_frontend; break; - case SAA7134_BOARD_HAUPPAUGE_HVR1110R3: + case SAA7134_BOARD_HAUPPAUGE_HVR1120: fe0->dvb.frontend = dvb_attach(tda10048_attach, &hcw_tda10048_config, &dev->i2c_adap); @@ -1147,7 +1147,7 @@ static int dvb_init(struct saa7134_dev *dev) &tda827x_cfg_1) < 0) goto dettach_frontend; break; - case SAA7134_BOARD_HAUPPAUGE_HVR1120: + case SAA7134_BOARD_HAUPPAUGE_HVR1150: fe0->dvb.frontend = dvb_attach(lgdt3305_attach, &hcw_lgdt3305_config, &dev->i2c_adap); diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index 82268848f26..fb564f14887 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -278,8 +278,8 @@ struct saa7134_format { #define SAA7134_BOARD_ASUSTeK_TIGER 152 #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153 #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154 -#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155 -#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156 +#define SAA7134_BOARD_HAUPPAUGE_HVR1150 155 +#define SAA7134_BOARD_HAUPPAUGE_HVR1120 156 #define SAA7134_BOARD_AVERMEDIA_STUDIO_507UA 157 #define SAA7134_BOARD_AVERMEDIA_CARDBUS_501 158 #define SAA7134_BOARD_BEHOLD_505RDS 159 diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 0db88a53d92..61c47b82408 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -30,7 +30,7 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/videodev2.h> -#include <linux/clk.h> +#include <linux/pm_runtime.h> #include <media/v4l2-common.h> #include <media/v4l2-dev.h> @@ -86,7 +86,6 @@ struct sh_mobile_ceu_dev { unsigned int irq; void __iomem *base; - struct clk *clk; unsigned long video_limit; /* lock used to protect videobuf */ @@ -282,27 +281,24 @@ out: return ret; } +/* Called under spinlock_irqsave(&pcdev->lock, ...) */ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct soc_camera_device *icd = vq->priv_data; struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - unsigned long flags; dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %zd\n", __func__, vb, vb->baddr, vb->bsize); vb->state = VIDEOBUF_QUEUED; - spin_lock_irqsave(&pcdev->lock, flags); list_add_tail(&vb->queue, &pcdev->capture); if (!pcdev->active) { pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } - - spin_unlock_irqrestore(&pcdev->lock, flags); } static void sh_mobile_ceu_videobuf_release(struct videobuf_queue *vq, @@ -364,7 +360,7 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) if (ret) goto err; - clk_enable(pcdev->clk); + pm_runtime_get_sync(ici->dev); ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ while (ceu_read(pcdev, CSTSR) & 1) @@ -398,7 +394,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) } spin_unlock_irqrestore(&pcdev->lock, flags); - clk_disable(pcdev->clk); + pm_runtime_put_sync(ici->dev); icd->ops->release(icd); @@ -801,7 +797,6 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) struct sh_mobile_ceu_dev *pcdev; struct resource *res; void __iomem *base; - char clk_name[8]; unsigned int irq; int err = 0; @@ -865,13 +860,9 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) goto exit_release_mem; } - snprintf(clk_name, sizeof(clk_name), "ceu%d", pdev->id); - pcdev->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(pcdev->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - err = PTR_ERR(pcdev->clk); - goto exit_free_irq; - } + pm_suspend_ignore_children(&pdev->dev, true); + pm_runtime_enable(&pdev->dev); + pm_runtime_resume(&pdev->dev); pcdev->ici.priv = pcdev; pcdev->ici.dev = &pdev->dev; @@ -881,12 +872,10 @@ static int sh_mobile_ceu_probe(struct platform_device *pdev) err = soc_camera_host_register(&pcdev->ici); if (err) - goto exit_free_clk; + goto exit_free_irq; return 0; -exit_free_clk: - clk_put(pcdev->clk); exit_free_irq: free_irq(pcdev->irq, pcdev); exit_release_mem: @@ -907,7 +896,6 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev) struct sh_mobile_ceu_dev, ici); soc_camera_host_unregister(soc_host); - clk_put(pcdev->clk); free_irq(pcdev->irq, pcdev); if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) dma_release_declared_memory(&pdev->dev); @@ -916,9 +904,27 @@ static int sh_mobile_ceu_remove(struct platform_device *pdev) return 0; } +static int sh_mobile_ceu_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * This driver re-initializes all registers after + * pm_runtime_get_sync() anyway so there is no need + * to save and restore registers here. + */ + return 0; +} + +static struct dev_pm_ops sh_mobile_ceu_dev_pm_ops = { + .runtime_suspend = sh_mobile_ceu_runtime_nop, + .runtime_resume = sh_mobile_ceu_runtime_nop, +}; + static struct platform_driver sh_mobile_ceu_driver = { .driver = { .name = "sh_mobile_ceu", + .pm = &sh_mobile_ceu_dev_pm_ops, }, .probe = sh_mobile_ceu_probe, .remove = sh_mobile_ceu_remove, diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index 4d6785e6345..b154bd961e3 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -1050,8 +1050,8 @@ static int stk_setup_format(struct stk_camera *dev) depth = 1; else depth = 2; - while (stk_sizes[i].m != dev->vsettings.mode - && i < ARRAY_SIZE(stk_sizes)) + while (i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode) i++; if (i == ARRAY_SIZE(stk_sizes)) { STK_ERROR("Something is broken in %s\n", __func__); diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 89927b7aec2..04b47832fa0 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -1845,11 +1845,29 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STREAM_NO_FID }, - /* ViMicro */ - { .match_flags = USB_DEVICE_ID_MATCH_VENDOR + /* ViMicro Vega */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x332d, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0ac8, + .idProduct = 0x3410, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_FIX_BANDWIDTH }, + /* ViMicro Venus - Minoru3D */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, .idVendor = 0x0ac8, - .idProduct = 0x0000, + .idProduct = 0x3420, .bInterfaceClass = USB_CLASS_VIDEO, .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c index f152a990386..1ca6dff7361 100644 --- a/drivers/media/video/uvc/uvc_status.c +++ b/drivers/media/video/uvc/uvc_status.c @@ -145,8 +145,8 @@ static void uvc_status_complete(struct urb *urb) break; default: - uvc_printk(KERN_INFO, "unknown event type %u.\n", - dev->status[0]); + uvc_trace(UVC_TRACE_STATUS, "Unknown status event " + "type %u.\n", dev->status[0]); break; } } diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index be64a502ea2..f2afc4e0837 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -1081,8 +1081,10 @@ static long __video_do_ioctl(struct file *file, /* Calls the specific handler */ if (ops->vidioc_g_std) ret = ops->vidioc_g_std(file, fh, id); - else + else if (vfd->current_norm) *id = vfd->current_norm; + else + ret = -EINVAL; if (!ret) dbgarg(cmd, "std=0x%08Lx\n", (long long unsigned)*id); @@ -1553,12 +1555,19 @@ static long __video_do_ioctl(struct file *file, break; ret = ops->vidioc_g_parm(file, fh, p); } else { + v4l2_std_id std = vfd->current_norm; + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - v4l2_video_std_frame_period(vfd->current_norm, - &p->parm.capture.timeperframe); ret = 0; + if (ops->vidioc_g_std) + ret = ops->vidioc_g_std(file, fh, &std); + else if (std == 0) + ret = -EINVAL; + if (ret == 0) + v4l2_video_std_frame_period(std, + &p->parm.capture.timeperframe); } dbgarg(cmd, "type=%d\n", p->type); diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c index c2041685094..45675889850 100644 --- a/drivers/net/3c59x.c +++ b/drivers/net/3c59x.c @@ -235,6 +235,7 @@ enum vortex_chips { CH_3C900B_FL, CH_3C905_1, CH_3C905_2, + CH_3C905B_TX, CH_3C905B_1, CH_3C905B_2, @@ -307,6 +308,8 @@ static struct vortex_chip_info { PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, }, {"3c905 Boomerang 100baseT4", PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, }, + {"3C905B-TX Fast Etherlink XL PCI", + PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, }, {"3c905B Cyclone 100baseTx", PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, }, @@ -389,6 +392,7 @@ static struct pci_device_id vortex_pci_tbl[] = { { 0x10B7, 0x900A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C900B_FL }, { 0x10B7, 0x9050, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_1 }, { 0x10B7, 0x9051, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905_2 }, + { 0x10B7, 0x9054, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_TX }, { 0x10B7, 0x9055, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_1 }, { 0x10B7, 0x9058, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CH_3C905B_2 }, diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c index 50efde11ea6..d0dbbf39349 100644 --- a/drivers/net/8139cp.c +++ b/drivers/net/8139cp.c @@ -515,7 +515,7 @@ rx_status_loop: dma_addr_t mapping; struct sk_buff *skb, *new_skb; struct cp_desc *desc; - unsigned buflen; + const unsigned buflen = cp->rx_buf_sz; skb = cp->rx_skb[rx_tail]; BUG_ON(!skb); @@ -549,8 +549,7 @@ rx_status_loop: pr_debug("%s: rx slot %d status 0x%x len %d\n", dev->name, rx_tail, status, len); - buflen = cp->rx_buf_sz + NET_IP_ALIGN; - new_skb = netdev_alloc_skb(dev, buflen); + new_skb = netdev_alloc_skb(dev, buflen + NET_IP_ALIGN); if (!new_skb) { dev->stats.rx_dropped++; goto rx_next; diff --git a/drivers/net/atl1c/atl1c_ethtool.c b/drivers/net/atl1c/atl1c_ethtool.c index 607007d75b6..00d11b480af 100644 --- a/drivers/net/atl1c/atl1c_ethtool.c +++ b/drivers/net/atl1c/atl1c_ethtool.c @@ -232,11 +232,11 @@ static void atl1c_get_drvinfo(struct net_device *netdev, { struct atl1c_adapter *adapter = netdev_priv(netdev); - strncpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); - strncpy(drvinfo->version, atl1c_driver_version, + strlcpy(drvinfo->driver, atl1c_driver_name, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, atl1c_driver_version, sizeof(drvinfo->version)); - strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); - strncpy(drvinfo->bus_info, pci_name(adapter->pdev), + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->n_stats = 0; drvinfo->testinfo_len = 0; diff --git a/drivers/net/atlx/atl1.c b/drivers/net/atlx/atl1.c index 94d7325caf4..8bca12f7139 100644 --- a/drivers/net/atlx/atl1.c +++ b/drivers/net/atlx/atl1.c @@ -3378,11 +3378,11 @@ static void atl1_get_drvinfo(struct net_device *netdev, { struct atl1_adapter *adapter = netdev_priv(netdev); - strncpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver)); - strncpy(drvinfo->version, ATLX_DRIVER_VERSION, + strlcpy(drvinfo->driver, ATLX_DRIVER_NAME, sizeof(drvinfo->driver)); + strlcpy(drvinfo->version, ATLX_DRIVER_VERSION, sizeof(drvinfo->version)); - strncpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); - strncpy(drvinfo->bus_info, pci_name(adapter->pdev), + strlcpy(drvinfo->fw_version, "N/A", sizeof(drvinfo->fw_version)); + strlcpy(drvinfo->bus_info, pci_name(adapter->pdev), sizeof(drvinfo->bus_info)); drvinfo->eedump_len = ATL1_EEDUMP_LEN; } diff --git a/drivers/net/b44.c b/drivers/net/b44.c index 36d4d377ec2..bafca672ea7 100644 --- a/drivers/net/b44.c +++ b/drivers/net/b44.c @@ -952,9 +952,10 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) int rc = NETDEV_TX_OK; dma_addr_t mapping; u32 len, entry, ctrl; + unsigned long flags; len = skb->len; - spin_lock_irq(&bp->lock); + spin_lock_irqsave(&bp->lock, flags); /* This is a hard error, log it. */ if (unlikely(TX_BUFFS_AVAIL(bp) < 1)) { @@ -1027,7 +1028,7 @@ static int b44_start_xmit(struct sk_buff *skb, struct net_device *dev) dev->trans_start = jiffies; out_unlock: - spin_unlock_irq(&bp->lock); + spin_unlock_irqrestore(&bp->lock, flags); return rc; diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index b70cc99962f..06b901152d4 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -399,9 +399,11 @@ static int bnx2_unregister_cnic(struct net_device *dev) struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; struct cnic_eth_dev *cp = &bp->cnic_eth_dev; + mutex_lock(&bp->cnic_lock); cp->drv_state = 0; bnapi->cnic_present = 0; rcu_assign_pointer(bp->cnic_ops, NULL); + mutex_unlock(&bp->cnic_lock); synchronize_rcu(); return 0; } @@ -429,13 +431,13 @@ bnx2_cnic_stop(struct bnx2 *bp) struct cnic_ops *c_ops; struct cnic_ctl_info info; - rcu_read_lock(); - c_ops = rcu_dereference(bp->cnic_ops); + mutex_lock(&bp->cnic_lock); + c_ops = bp->cnic_ops; if (c_ops) { info.cmd = CNIC_CTL_STOP_CMD; c_ops->cnic_ctl(bp->cnic_data, &info); } - rcu_read_unlock(); + mutex_unlock(&bp->cnic_lock); } static void @@ -444,8 +446,8 @@ bnx2_cnic_start(struct bnx2 *bp) struct cnic_ops *c_ops; struct cnic_ctl_info info; - rcu_read_lock(); - c_ops = rcu_dereference(bp->cnic_ops); + mutex_lock(&bp->cnic_lock); + c_ops = bp->cnic_ops; if (c_ops) { if (!(bp->flags & BNX2_FLAG_USING_MSIX)) { struct bnx2_napi *bnapi = &bp->bnx2_napi[0]; @@ -455,7 +457,7 @@ bnx2_cnic_start(struct bnx2 *bp) info.cmd = CNIC_CTL_START_CMD; c_ops->cnic_ctl(bp->cnic_data, &info); } - rcu_read_unlock(); + mutex_unlock(&bp->cnic_lock); } #else @@ -7663,6 +7665,9 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev) spin_lock_init(&bp->phy_lock); spin_lock_init(&bp->indirect_lock); +#ifdef BCM_CNIC + mutex_init(&bp->cnic_lock); +#endif INIT_WORK(&bp->reset_task, bnx2_reset_task); dev->base_addr = dev->mem_start = pci_resource_start(pdev, 0); diff --git a/drivers/net/bnx2.h b/drivers/net/bnx2.h index f1edfaa9e56..a4f12fd0ecd 100644 --- a/drivers/net/bnx2.h +++ b/drivers/net/bnx2.h @@ -6902,6 +6902,7 @@ struct bnx2 { u32 idle_chk_status_idx; #ifdef BCM_CNIC + struct mutex cnic_lock; struct cnic_eth_dev cnic_eth_dev; #endif diff --git a/drivers/net/can/dev.c b/drivers/net/can/dev.c index 9e4283aff82..e1a4f821423 100644 --- a/drivers/net/can/dev.c +++ b/drivers/net/can/dev.c @@ -611,11 +611,18 @@ nla_put_failure: return -EMSGSIZE; } +static int can_newlink(struct net_device *dev, + struct nlattr *tb[], struct nlattr *data[]) +{ + return -EOPNOTSUPP; +} + static struct rtnl_link_ops can_link_ops __read_mostly = { .kind = "can", .maxtype = IFLA_CAN_MAX, .policy = can_policy, .setup = can_setup, + .newlink = can_newlink, .changelink = can_changelink, .fill_info = can_fill_info, .fill_xstats = can_fill_xstats, diff --git a/drivers/net/cnic.c b/drivers/net/cnic.c index 4869d77cbe9..74c342959b7 100644 --- a/drivers/net/cnic.c +++ b/drivers/net/cnic.c @@ -138,6 +138,16 @@ static struct cnic_dev *cnic_from_netdev(struct net_device *netdev) return NULL; } +static inline void ulp_get(struct cnic_ulp_ops *ulp_ops) +{ + atomic_inc(&ulp_ops->ref_count); +} + +static inline void ulp_put(struct cnic_ulp_ops *ulp_ops) +{ + atomic_dec(&ulp_ops->ref_count); +} + static void cnic_ctx_wr(struct cnic_dev *dev, u32 cid_addr, u32 off, u32 val) { struct cnic_local *cp = dev->cnic_priv; @@ -358,6 +368,7 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops) } read_unlock(&cnic_dev_lock); + atomic_set(&ulp_ops->ref_count, 0); rcu_assign_pointer(cnic_ulp_tbl[ulp_type], ulp_ops); mutex_unlock(&cnic_lock); @@ -379,6 +390,8 @@ int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops) int cnic_unregister_driver(int ulp_type) { struct cnic_dev *dev; + struct cnic_ulp_ops *ulp_ops; + int i = 0; if (ulp_type >= MAX_CNIC_ULP_TYPE) { printk(KERN_ERR PFX "cnic_unregister_driver: Bad type %d\n", @@ -386,7 +399,8 @@ int cnic_unregister_driver(int ulp_type) return -EINVAL; } mutex_lock(&cnic_lock); - if (!cnic_ulp_tbl[ulp_type]) { + ulp_ops = cnic_ulp_tbl[ulp_type]; + if (!ulp_ops) { printk(KERN_ERR PFX "cnic_unregister_driver: Type %d has not " "been registered\n", ulp_type); goto out_unlock; @@ -411,6 +425,14 @@ int cnic_unregister_driver(int ulp_type) mutex_unlock(&cnic_lock); synchronize_rcu(); + while ((atomic_read(&ulp_ops->ref_count) != 0) && (i < 20)) { + msleep(100); + i++; + } + + if (atomic_read(&ulp_ops->ref_count) != 0) + printk(KERN_WARNING PFX "%s: Failed waiting for ref count to go" + " to zero.\n", dev->netdev->name); return 0; out_unlock: @@ -466,6 +488,7 @@ EXPORT_SYMBOL(cnic_register_driver); static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) { struct cnic_local *cp = dev->cnic_priv; + int i = 0; if (ulp_type >= MAX_CNIC_ULP_TYPE) { printk(KERN_ERR PFX "cnic_unregister_device: Bad type %d\n", @@ -486,6 +509,15 @@ static int cnic_unregister_device(struct cnic_dev *dev, int ulp_type) synchronize_rcu(); + while (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type]) && + i < 20) { + msleep(100); + i++; + } + if (test_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[ulp_type])) + printk(KERN_WARNING PFX "%s: Failed waiting for ULP up call" + " to complete.\n", dev->netdev->name); + return 0; } EXPORT_SYMBOL(cnic_unregister_driver); @@ -1076,18 +1108,23 @@ static void cnic_ulp_stop(struct cnic_dev *dev) if (cp->cnic_uinfo) cnic_send_nlmsg(cp, ISCSI_KEVENT_IF_DOWN, NULL); - rcu_read_lock(); for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { struct cnic_ulp_ops *ulp_ops; - ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); - if (!ulp_ops) + mutex_lock(&cnic_lock); + ulp_ops = cp->ulp_ops[if_type]; + if (!ulp_ops) { + mutex_unlock(&cnic_lock); continue; + } + set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); + mutex_unlock(&cnic_lock); if (test_and_clear_bit(ULP_F_START, &cp->ulp_flags[if_type])) ulp_ops->cnic_stop(cp->ulp_handle[if_type]); + + clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); } - rcu_read_unlock(); } static void cnic_ulp_start(struct cnic_dev *dev) @@ -1095,18 +1132,23 @@ static void cnic_ulp_start(struct cnic_dev *dev) struct cnic_local *cp = dev->cnic_priv; int if_type; - rcu_read_lock(); for (if_type = 0; if_type < MAX_CNIC_ULP_TYPE; if_type++) { struct cnic_ulp_ops *ulp_ops; - ulp_ops = rcu_dereference(cp->ulp_ops[if_type]); - if (!ulp_ops || !ulp_ops->cnic_start) + mutex_lock(&cnic_lock); + ulp_ops = cp->ulp_ops[if_type]; + if (!ulp_ops || !ulp_ops->cnic_start) { + mutex_unlock(&cnic_lock); continue; + } + set_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); + mutex_unlock(&cnic_lock); if (!test_and_set_bit(ULP_F_START, &cp->ulp_flags[if_type])) ulp_ops->cnic_start(cp->ulp_handle[if_type]); + + clear_bit(ULP_F_CALL_PENDING, &cp->ulp_flags[if_type]); } - rcu_read_unlock(); } static int cnic_ctl(void *data, struct cnic_ctl_info *info) @@ -1116,22 +1158,18 @@ static int cnic_ctl(void *data, struct cnic_ctl_info *info) switch (info->cmd) { case CNIC_CTL_STOP_CMD: cnic_hold(dev); - mutex_lock(&cnic_lock); cnic_ulp_stop(dev); cnic_stop_hw(dev); - mutex_unlock(&cnic_lock); cnic_put(dev); break; case CNIC_CTL_START_CMD: cnic_hold(dev); - mutex_lock(&cnic_lock); if (!cnic_start_hw(dev)) cnic_ulp_start(dev); - mutex_unlock(&cnic_lock); cnic_put(dev); break; default: @@ -1145,19 +1183,23 @@ static void cnic_ulp_init(struct cnic_dev *dev) int i; struct cnic_local *cp = dev->cnic_priv; - rcu_read_lock(); for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) { struct cnic_ulp_ops *ulp_ops; - ulp_ops = rcu_dereference(cnic_ulp_tbl[i]); - if (!ulp_ops || !ulp_ops->cnic_init) + mutex_lock(&cnic_lock); + ulp_ops = cnic_ulp_tbl[i]; + if (!ulp_ops || !ulp_ops->cnic_init) { + mutex_unlock(&cnic_lock); continue; + } + ulp_get(ulp_ops); + mutex_unlock(&cnic_lock); if (!test_and_set_bit(ULP_F_INIT, &cp->ulp_flags[i])) ulp_ops->cnic_init(dev); + ulp_put(ulp_ops); } - rcu_read_unlock(); } static void cnic_ulp_exit(struct cnic_dev *dev) @@ -1165,19 +1207,23 @@ static void cnic_ulp_exit(struct cnic_dev *dev) int i; struct cnic_local *cp = dev->cnic_priv; - rcu_read_lock(); for (i = 0; i < MAX_CNIC_ULP_TYPE_EXT; i++) { struct cnic_ulp_ops *ulp_ops; - ulp_ops = rcu_dereference(cnic_ulp_tbl[i]); - if (!ulp_ops || !ulp_ops->cnic_exit) + mutex_lock(&cnic_lock); + ulp_ops = cnic_ulp_tbl[i]; + if (!ulp_ops || !ulp_ops->cnic_exit) { + mutex_unlock(&cnic_lock); continue; + } + ulp_get(ulp_ops); + mutex_unlock(&cnic_lock); if (test_and_clear_bit(ULP_F_INIT, &cp->ulp_flags[i])) ulp_ops->cnic_exit(dev); + ulp_put(ulp_ops); } - rcu_read_unlock(); } static int cnic_cm_offload_pg(struct cnic_sock *csk) @@ -2393,21 +2439,45 @@ static int cnic_start_bnx2_hw(struct cnic_dev *dev) return 0; } -static int cnic_start_hw(struct cnic_dev *dev) +static int cnic_register_netdev(struct cnic_dev *dev) { struct cnic_local *cp = dev->cnic_priv; struct cnic_eth_dev *ethdev = cp->ethdev; int err; - if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) - return -EALREADY; + if (!ethdev) + return -ENODEV; + + if (ethdev->drv_state & CNIC_DRV_STATE_REGD) + return 0; err = ethdev->drv_register_cnic(dev->netdev, cp->cnic_ops, dev); - if (err) { + if (err) printk(KERN_ERR PFX "%s: register_cnic failed\n", dev->netdev->name); - goto err2; - } + + return err; +} + +static void cnic_unregister_netdev(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + + if (!ethdev) + return; + + ethdev->drv_unregister_cnic(dev->netdev); +} + +static int cnic_start_hw(struct cnic_dev *dev) +{ + struct cnic_local *cp = dev->cnic_priv; + struct cnic_eth_dev *ethdev = cp->ethdev; + int err; + + if (test_bit(CNIC_F_CNIC_UP, &dev->flags)) + return -EALREADY; dev->regview = ethdev->io_base; cp->chip_id = ethdev->chip_id; @@ -2438,18 +2508,13 @@ static int cnic_start_hw(struct cnic_dev *dev) return 0; err1: - ethdev->drv_unregister_cnic(dev->netdev); cp->free_resc(dev); pci_dev_put(dev->pcidev); -err2: return err; } static void cnic_stop_bnx2_hw(struct cnic_dev *dev) { - struct cnic_local *cp = dev->cnic_priv; - struct cnic_eth_dev *ethdev = cp->ethdev; - cnic_disable_bnx2_int_sync(dev); cnic_reg_wr_ind(dev, BNX2_CP_SCRATCH + 0x20, 0); @@ -2461,8 +2526,6 @@ static void cnic_stop_bnx2_hw(struct cnic_dev *dev) cnic_setup_5709_context(dev, 0); cnic_free_irq(dev); - ethdev->drv_unregister_cnic(dev->netdev); - cnic_free_resc(dev); } @@ -2543,7 +2606,7 @@ static struct cnic_dev *init_bnx2_cnic(struct net_device *dev) probe = symbol_get(bnx2_cnic_probe); if (probe) { ethdev = (*probe)(dev); - symbol_put_addr(probe); + symbol_put(bnx2_cnic_probe); } if (!ethdev) return NULL; @@ -2646,10 +2709,12 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event, else if (event == NETDEV_UNREGISTER) cnic_ulp_exit(dev); else if (event == NETDEV_UP) { - mutex_lock(&cnic_lock); + if (cnic_register_netdev(dev) != 0) { + cnic_put(dev); + goto done; + } if (!cnic_start_hw(dev)) cnic_ulp_start(dev); - mutex_unlock(&cnic_lock); } rcu_read_lock(); @@ -2668,10 +2733,9 @@ static int cnic_netdev_event(struct notifier_block *this, unsigned long event, rcu_read_unlock(); if (event == NETDEV_GOING_DOWN) { - mutex_lock(&cnic_lock); cnic_ulp_stop(dev); cnic_stop_hw(dev); - mutex_unlock(&cnic_lock); + cnic_unregister_netdev(dev); } else if (event == NETDEV_UNREGISTER) { write_lock(&cnic_dev_lock); list_del_init(&dev->list); @@ -2703,6 +2767,7 @@ static void cnic_release(void) } cnic_ulp_exit(dev); + cnic_unregister_netdev(dev); list_del_init(&dev->list); cnic_free_dev(dev); } diff --git a/drivers/net/cnic.h b/drivers/net/cnic.h index 5192d4a9df5..a94b302bb46 100644 --- a/drivers/net/cnic.h +++ b/drivers/net/cnic.h @@ -176,6 +176,7 @@ struct cnic_local { unsigned long ulp_flags[MAX_CNIC_ULP_TYPE]; #define ULP_F_INIT 0 #define ULP_F_START 1 +#define ULP_F_CALL_PENDING 2 struct cnic_ulp_ops *ulp_ops[MAX_CNIC_ULP_TYPE]; /* protected by ulp_lock */ diff --git a/drivers/net/cnic_if.h b/drivers/net/cnic_if.h index d1bce27ee99..a49235739ee 100644 --- a/drivers/net/cnic_if.h +++ b/drivers/net/cnic_if.h @@ -290,6 +290,7 @@ struct cnic_ulp_ops { void (*iscsi_nl_send_msg)(struct cnic_dev *dev, u32 msg_type, char *data, u16 data_size); struct module *owner; + atomic_t ref_count; }; extern int cnic_register_driver(int ulp_type, struct cnic_ulp_ops *ulp_ops); diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c index d56c7473144..99df2abf82a 100644 --- a/drivers/net/e1000e/ich8lan.c +++ b/drivers/net/e1000e/ich8lan.c @@ -338,10 +338,7 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) { struct e1000_nvm_info *nvm = &hw->nvm; struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan; - union ich8_hws_flash_status hsfsts; - u32 gfpreg; - u32 sector_base_addr; - u32 sector_end_addr; + u32 gfpreg, sector_base_addr, sector_end_addr; u16 i; /* Can't read flash registers if the register set isn't mapped. */ @@ -375,20 +372,6 @@ static s32 e1000_init_nvm_params_ich8lan(struct e1000_hw *hw) /* Adjust to word count */ nvm->flash_bank_size /= sizeof(u16); - /* - * Make sure the flash bank size does not overwrite the 4k - * sector ranges. We may have 64k allotted to us but we only care - * about the first 2 4k sectors. Therefore, if we have anything less - * than 64k set in the HSFSTS register, we will reduce the bank size - * down to 4k and let the rest remain unused. If berasesz == 3, then - * we are working in 64k mode. Otherwise we are not. - */ - if (nvm->flash_bank_size > E1000_ICH8_SHADOW_RAM_WORDS) { - hsfsts.regval = er16flash(ICH_FLASH_HSFSTS); - if (hsfsts.hsf_status.berasesz != 3) - nvm->flash_bank_size = E1000_ICH8_SHADOW_RAM_WORDS; - } - nvm->word_size = E1000_ICH8_SHADOW_RAM_WORDS; /* Clear shadow ram */ @@ -594,8 +577,8 @@ static DEFINE_MUTEX(nvm_mutex); **/ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) { - u32 extcnf_ctrl; - u32 timeout = PHY_CFG_TIMEOUT; + u32 extcnf_ctrl, timeout = PHY_CFG_TIMEOUT; + s32 ret_val = 0; might_sleep(); @@ -603,28 +586,46 @@ static s32 e1000_acquire_swflag_ich8lan(struct e1000_hw *hw) while (timeout) { extcnf_ctrl = er32(EXTCNF_CTRL); + if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)) + break; - if (!(extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG)) { - extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; - ew32(EXTCNF_CTRL, extcnf_ctrl); + mdelay(1); + timeout--; + } + + if (!timeout) { + hw_dbg(hw, "SW/FW/HW has locked the resource for too long.\n"); + ret_val = -E1000_ERR_CONFIG; + goto out; + } + + timeout = PHY_CFG_TIMEOUT * 2; + + extcnf_ctrl |= E1000_EXTCNF_CTRL_SWFLAG; + ew32(EXTCNF_CTRL, extcnf_ctrl); + + while (timeout) { + extcnf_ctrl = er32(EXTCNF_CTRL); + if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) + break; - extcnf_ctrl = er32(EXTCNF_CTRL); - if (extcnf_ctrl & E1000_EXTCNF_CTRL_SWFLAG) - break; - } mdelay(1); timeout--; } if (!timeout) { - hw_dbg(hw, "FW or HW has locked the resource for too long.\n"); + hw_dbg(hw, "Failed to acquire the semaphore.\n"); extcnf_ctrl &= ~E1000_EXTCNF_CTRL_SWFLAG; ew32(EXTCNF_CTRL, extcnf_ctrl); - mutex_unlock(&nvm_mutex); - return -E1000_ERR_CONFIG; + ret_val = -E1000_ERR_CONFIG; + goto out; } - return 0; +out: + if (ret_val) + mutex_unlock(&nvm_mutex); + + return ret_val; } /** @@ -1306,7 +1307,7 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, struct e1000_nvm_info *nvm = &hw->nvm; struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan; u32 act_offset; - s32 ret_val; + s32 ret_val = 0; u32 bank = 0; u16 i, word; @@ -1321,12 +1322,15 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, goto out; ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); - if (ret_val) - goto release; + if (ret_val) { + hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n"); + bank = 0; + } act_offset = (bank) ? nvm->flash_bank_size : 0; act_offset += offset; + ret_val = 0; for (i = 0; i < words; i++) { if ((dev_spec->shadow_ram) && (dev_spec->shadow_ram[offset+i].modified)) { @@ -1341,7 +1345,6 @@ static s32 e1000_read_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, } } -release: e1000_release_swflag_ich8lan(hw); out: @@ -1592,7 +1595,6 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, { struct e1000_nvm_info *nvm = &hw->nvm; struct e1000_dev_spec_ich8lan *dev_spec = &hw->dev_spec.ich8lan; - s32 ret_val; u16 i; if ((offset >= nvm->word_size) || (words > nvm->word_size - offset) || @@ -1601,17 +1603,11 @@ static s32 e1000_write_nvm_ich8lan(struct e1000_hw *hw, u16 offset, u16 words, return -E1000_ERR_NVM; } - ret_val = e1000_acquire_swflag_ich8lan(hw); - if (ret_val) - return ret_val; - for (i = 0; i < words; i++) { dev_spec->shadow_ram[offset+i].modified = 1; dev_spec->shadow_ram[offset+i].value = data[i]; } - e1000_release_swflag_ich8lan(hw); - return 0; } @@ -1652,8 +1648,8 @@ static s32 e1000_update_nvm_checksum_ich8lan(struct e1000_hw *hw) */ ret_val = e1000_valid_nvm_bank_detect_ich8lan(hw, &bank); if (ret_val) { - e1000_release_swflag_ich8lan(hw); - goto out; + hw_dbg(hw, "Could not detect valid bank, assuming bank 0\n"); + bank = 0; } if (bank == 0) { @@ -2039,12 +2035,8 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) iteration = 1; break; case 2: - if (hw->mac.type == e1000_ich9lan) { - sector_size = ICH_FLASH_SEG_SIZE_8K; - iteration = flash_bank_size / ICH_FLASH_SEG_SIZE_8K; - } else { - return -E1000_ERR_NVM; - } + sector_size = ICH_FLASH_SEG_SIZE_8K; + iteration = 1; break; case 3: sector_size = ICH_FLASH_SEG_SIZE_64K; @@ -2056,7 +2048,7 @@ static s32 e1000_erase_flash_bank_ich8lan(struct e1000_hw *hw, u32 bank) /* Start with the base address, then add the sector offset. */ flash_linear_addr = hw->nvm.flash_base_addr; - flash_linear_addr += (bank) ? (sector_size * iteration) : 0; + flash_linear_addr += (bank) ? flash_bank_size : 0; for (j = 0; j < iteration ; j++) { do { diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index 63415bb6f48..fa92a683aef 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -4538,8 +4538,7 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) /* Allow time for pending master requests to run */ e1000e_disable_pcie_master(&adapter->hw); - if ((adapter->flags2 & FLAG2_HAS_PHY_WAKEUP) && - !(hw->mac.ops.check_mng_mode(hw))) { + if (adapter->flags2 & FLAG2_HAS_PHY_WAKEUP) { /* enable wakeup by the PHY */ retval = e1000_init_phy_wakeup(adapter, wufc); if (retval) @@ -4557,7 +4556,8 @@ static int __e1000_shutdown(struct pci_dev *pdev, bool *enable_wake) *enable_wake = !!wufc; /* make sure adapter isn't asleep if manageability is enabled */ - if (adapter->flags & FLAG_MNG_PT_ENABLED) + if ((adapter->flags & FLAG_MNG_PT_ENABLED) || + (hw->mac.ops.check_mng_mode(hw))) *enable_wake = true; if (adapter->hw.phy.type == e1000_phy_igp_3) @@ -4670,14 +4670,6 @@ static int e1000_resume(struct pci_dev *pdev) return err; } - /* AER (Advanced Error Reporting) hooks */ - err = pci_enable_pcie_error_reporting(pdev); - if (err) { - dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed " - "0x%x\n", err); - /* non-fatal, continue */ - } - pci_set_master(pdev); pci_enable_wake(pdev, PCI_D3hot, 0); @@ -4990,6 +4982,14 @@ static int __devinit e1000_probe(struct pci_dev *pdev, if (err) goto err_pci_reg; + /* AER (Advanced Error Reporting) hooks */ + err = pci_enable_pcie_error_reporting(pdev); + if (err) { + dev_err(&pdev->dev, "pci_enable_pcie_error_reporting failed " + "0x%x\n", err); + /* non-fatal, continue */ + } + pci_set_master(pdev); /* PCI config space info */ err = pci_save_state(pdev); diff --git a/drivers/net/fec.c b/drivers/net/fec.c index d4b98074b1b..c9fd82d3a80 100644 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -285,6 +285,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) { struct fec_enet_private *fep = netdev_priv(dev); struct bufdesc *bdp; + void *bufaddr; unsigned short status; unsigned long flags; @@ -312,7 +313,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) status &= ~BD_ENET_TX_STATS; /* Set buffer length and buffer pointer */ - bdp->cbd_bufaddr = __pa(skb->data); + bufaddr = skb->data; bdp->cbd_datlen = skb->len; /* @@ -320,11 +321,11 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) * 4-byte boundaries. Use bounce buffers to copy data * and get it aligned. Ugh. */ - if (bdp->cbd_bufaddr & FEC_ALIGNMENT) { + if (((unsigned long) bufaddr) & FEC_ALIGNMENT) { unsigned int index; index = bdp - fep->tx_bd_base; memcpy(fep->tx_bounce[index], (void *)skb->data, skb->len); - bdp->cbd_bufaddr = __pa(fep->tx_bounce[index]); + bufaddr = fep->tx_bounce[index]; } /* Save skb pointer */ @@ -336,7 +337,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Push the data cache so the CPM does not get stale memory * data. */ - bdp->cbd_bufaddr = dma_map_single(&dev->dev, skb->data, + bdp->cbd_bufaddr = dma_map_single(&dev->dev, bufaddr, FEC_ENET_TX_FRSIZE, DMA_TO_DEVICE); /* Send it on its way. Tell FEC it's ready, interrupt when done, diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c index f8ffcbf0bc3..e212f2c5448 100644 --- a/drivers/net/gianfar.c +++ b/drivers/net/gianfar.c @@ -936,6 +936,7 @@ int startup_gfar(struct net_device *dev) struct gfar __iomem *regs = priv->regs; int err = 0; u32 rctrl = 0; + u32 tctrl = 0; u32 attrs = 0; gfar_write(®s->imask, IMASK_INIT_CLEAR); @@ -1111,11 +1112,19 @@ int startup_gfar(struct net_device *dev) rctrl |= RCTRL_PADDING(priv->padding); } + /* keep vlan related bits if it's enabled */ + if (priv->vlgrp) { + rctrl |= RCTRL_VLEX | RCTRL_PRSDEP_INIT; + tctrl |= TCTRL_VLINS; + } + /* Init rctrl based on our settings */ gfar_write(&priv->regs->rctrl, rctrl); if (dev->features & NETIF_F_IP_CSUM) - gfar_write(&priv->regs->tctrl, TCTRL_INIT_CSUM); + tctrl |= TCTRL_INIT_CSUM; + + gfar_write(&priv->regs->tctrl, tctrl); /* Set the extraction length and index */ attrs = ATTRELI_EL(priv->rx_stash_size) | @@ -1450,7 +1459,6 @@ static void gfar_vlan_rx_register(struct net_device *dev, /* Enable VLAN tag extraction */ tempval = gfar_read(&priv->regs->rctrl); - tempval |= RCTRL_VLEX; tempval |= (RCTRL_VLEX | RCTRL_PRSDEP_INIT); gfar_write(&priv->regs->rctrl, tempval); } else { diff --git a/drivers/net/irda/w83977af_ir.c b/drivers/net/irda/w83977af_ir.c index d0883835b0c..fe4f2b2bff9 100644 --- a/drivers/net/irda/w83977af_ir.c +++ b/drivers/net/irda/w83977af_ir.c @@ -115,7 +115,7 @@ static int __init w83977af_init(void) IRDA_DEBUG(0, "%s()\n", __func__ ); - for (i=0; (io[i] < 2000) && (i < ARRAY_SIZE(dev_self)); i++) { + for (i=0; i < ARRAY_SIZE(dev_self) && io[i] < 2000; i++) { if (w83977af_open(i, io[i], irq[i], dma[i]) == 0) return 0; } diff --git a/drivers/net/ixgbe/ixgbe.h b/drivers/net/ixgbe/ixgbe.h index e11d83d5852..2c4dc8221dc 100644 --- a/drivers/net/ixgbe/ixgbe.h +++ b/drivers/net/ixgbe/ixgbe.h @@ -136,6 +136,8 @@ struct ixgbe_ring { u8 queue_index; /* needed for multiqueue queue management */ +#define IXGBE_RING_RX_PS_ENABLED (u8)(1) + u8 flags; /* per ring feature flags */ u16 head; u16 tail; diff --git a/drivers/net/ixgbe/ixgbe_ethtool.c b/drivers/net/ixgbe/ixgbe_ethtool.c index 79144e950a3..dff8dfac7ed 100644 --- a/drivers/net/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ixgbe/ixgbe_ethtool.c @@ -1948,6 +1948,7 @@ static int ixgbe_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) { struct ixgbe_adapter *adapter = netdev_priv(netdev); + struct ixgbe_q_vector *q_vector; int i; if (ec->tx_max_coalesced_frames_irq) @@ -1982,14 +1983,24 @@ static int ixgbe_set_coalesce(struct net_device *netdev, adapter->itr_setting = 0; } - for (i = 0; i < adapter->num_msix_vectors - NON_Q_VECTORS; i++) { - struct ixgbe_q_vector *q_vector = adapter->q_vector[i]; - if (q_vector->txr_count && !q_vector->rxr_count) - /* tx vector gets half the rate */ - q_vector->eitr = (adapter->eitr_param >> 1); - else - /* rx only or mixed */ - q_vector->eitr = adapter->eitr_param; + /* MSI/MSIx Interrupt Mode */ + if (adapter->flags & + (IXGBE_FLAG_MSIX_ENABLED | IXGBE_FLAG_MSI_ENABLED)) { + int num_vectors = adapter->num_msix_vectors - NON_Q_VECTORS; + for (i = 0; i < num_vectors; i++) { + q_vector = adapter->q_vector[i]; + if (q_vector->txr_count && !q_vector->rxr_count) + /* tx vector gets half the rate */ + q_vector->eitr = (adapter->eitr_param >> 1); + else + /* rx only or mixed */ + q_vector->eitr = adapter->eitr_param; + ixgbe_write_eitr(q_vector); + } + /* Legacy Interrupt Mode */ + } else { + q_vector = adapter->q_vector[0]; + q_vector->eitr = adapter->eitr_param; ixgbe_write_eitr(q_vector); } diff --git a/drivers/net/ixgbe/ixgbe_fcoe.c b/drivers/net/ixgbe/ixgbe_fcoe.c index fa9f24e2368..28cf104e36c 100644 --- a/drivers/net/ixgbe/ixgbe_fcoe.c +++ b/drivers/net/ixgbe/ixgbe_fcoe.c @@ -336,7 +336,7 @@ int ixgbe_fcoe_ddp(struct ixgbe_adapter *adapter, /* return 0 to bypass going to ULD for DDPed data */ if (fcstat == IXGBE_RXDADV_STAT_FCSTAT_DDP) rc = 0; - else + else if (ddp->len) rc = ddp->len; } diff --git a/drivers/net/ixgbe/ixgbe_main.c b/drivers/net/ixgbe/ixgbe_main.c index 110c65ab5cb..77b0381a2b5 100644 --- a/drivers/net/ixgbe/ixgbe_main.c +++ b/drivers/net/ixgbe/ixgbe_main.c @@ -492,12 +492,12 @@ static void ixgbe_receive_skb(struct ixgbe_q_vector *q_vector, skb_record_rx_queue(skb, ring->queue_index); if (!(adapter->flags & IXGBE_FLAG_IN_NETPOLL)) { - if (adapter->vlgrp && is_vlan && (tag != 0)) + if (adapter->vlgrp && is_vlan && (tag & VLAN_VID_MASK)) vlan_gro_receive(napi, adapter->vlgrp, tag, skb); else napi_gro_receive(napi, skb); } else { - if (adapter->vlgrp && is_vlan && (tag != 0)) + if (adapter->vlgrp && is_vlan && (tag & VLAN_VID_MASK)) vlan_hwaccel_rx(skb, adapter->vlgrp, tag); else netif_rx(skb); @@ -585,7 +585,7 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter, rx_desc = IXGBE_RX_DESC_ADV(*rx_ring, i); if (!bi->page_dma && - (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED)) { + (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED)) { if (!bi->page) { bi->page = alloc_page(GFP_ATOMIC); if (!bi->page) { @@ -629,7 +629,7 @@ static void ixgbe_alloc_rx_buffers(struct ixgbe_adapter *adapter, } /* Refresh the desc even if buffer_addrs didn't change because * each write-back erases this info. */ - if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) { + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { rx_desc->read.pkt_addr = cpu_to_le64(bi->page_dma); rx_desc->read.hdr_addr = cpu_to_le64(bi->dma); } else { @@ -726,7 +726,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, break; (*work_done)++; - if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) { + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { hdr_info = le16_to_cpu(ixgbe_get_hdr_info(rx_desc)); len = (hdr_info & IXGBE_RXDADV_HDRBUFLEN_MASK) >> IXGBE_RXDADV_HDRBUFLEN_SHIFT; @@ -798,7 +798,7 @@ static bool ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, rx_ring->stats.packets++; rx_ring->stats.bytes += skb->len; } else { - if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) { + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { rx_buffer_info->skb = next_buffer->skb; rx_buffer_info->dma = next_buffer->dma; next_buffer->skb = skb; @@ -1898,46 +1898,19 @@ static void ixgbe_configure_tx(struct ixgbe_adapter *adapter) #define IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT 2 -static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter, int index) +static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter, + struct ixgbe_ring *rx_ring) { - struct ixgbe_ring *rx_ring; u32 srrctl; - int queue0 = 0; - unsigned long mask; + int index; struct ixgbe_ring_feature *feature = adapter->ring_feature; - if (adapter->hw.mac.type == ixgbe_mac_82599EB) { - if (adapter->flags & IXGBE_FLAG_DCB_ENABLED) { - int dcb_i = feature[RING_F_DCB].indices; - if (dcb_i == 8) - queue0 = index >> 4; - else if (dcb_i == 4) - queue0 = index >> 5; - else - dev_err(&adapter->pdev->dev, "Invalid DCB " - "configuration\n"); -#ifdef IXGBE_FCOE - if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) { - struct ixgbe_ring_feature *f; - - rx_ring = &adapter->rx_ring[queue0]; - f = &adapter->ring_feature[RING_F_FCOE]; - if ((queue0 == 0) && (index > rx_ring->reg_idx)) - queue0 = f->mask + index - - rx_ring->reg_idx - 1; - } -#endif /* IXGBE_FCOE */ - } else { - queue0 = index; - } - } else { + index = rx_ring->reg_idx; + if (adapter->hw.mac.type == ixgbe_mac_82598EB) { + unsigned long mask; mask = (unsigned long) feature[RING_F_RSS].mask; - queue0 = index & mask; index = index & mask; } - - rx_ring = &adapter->rx_ring[queue0]; - srrctl = IXGBE_READ_REG(&adapter->hw, IXGBE_SRRCTL(index)); srrctl &= ~IXGBE_SRRCTL_BSIZEHDR_MASK; @@ -1946,7 +1919,7 @@ static void ixgbe_configure_srrctl(struct ixgbe_adapter *adapter, int index) srrctl |= (IXGBE_RX_HDR_SIZE << IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT) & IXGBE_SRRCTL_BSIZEHDR_MASK; - if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) { + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { #if (PAGE_SIZE / 2) > IXGBE_MAX_RXBUFFER srrctl |= IXGBE_MAX_RXBUFFER >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; #else @@ -2002,6 +1975,7 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) { u64 rdba; struct ixgbe_hw *hw = &adapter->hw; + struct ixgbe_ring *rx_ring; struct net_device *netdev = adapter->netdev; int max_frame = netdev->mtu + ETH_HLEN + ETH_FCS_LEN; int i, j; @@ -2018,11 +1992,6 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) /* Decide whether to use packet split mode or not */ adapter->flags |= IXGBE_FLAG_RX_PS_ENABLED; -#ifdef IXGBE_FCOE - if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) - adapter->flags &= ~IXGBE_FLAG_RX_PS_ENABLED; -#endif /* IXGBE_FCOE */ - /* Set the RX buffer length according to the mode */ if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) { rx_buf_len = IXGBE_RX_HDR_SIZE; @@ -2070,29 +2039,35 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) * the Base and Length of the Rx Descriptor Ring */ for (i = 0; i < adapter->num_rx_queues; i++) { - rdba = adapter->rx_ring[i].dma; - j = adapter->rx_ring[i].reg_idx; + rx_ring = &adapter->rx_ring[i]; + rdba = rx_ring->dma; + j = rx_ring->reg_idx; IXGBE_WRITE_REG(hw, IXGBE_RDBAL(j), (rdba & DMA_BIT_MASK(32))); IXGBE_WRITE_REG(hw, IXGBE_RDBAH(j), (rdba >> 32)); IXGBE_WRITE_REG(hw, IXGBE_RDLEN(j), rdlen); IXGBE_WRITE_REG(hw, IXGBE_RDH(j), 0); IXGBE_WRITE_REG(hw, IXGBE_RDT(j), 0); - adapter->rx_ring[i].head = IXGBE_RDH(j); - adapter->rx_ring[i].tail = IXGBE_RDT(j); - adapter->rx_ring[i].rx_buf_len = rx_buf_len; + rx_ring->head = IXGBE_RDH(j); + rx_ring->tail = IXGBE_RDT(j); + rx_ring->rx_buf_len = rx_buf_len; + + if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) + rx_ring->flags |= IXGBE_RING_RX_PS_ENABLED; #ifdef IXGBE_FCOE if (adapter->flags & IXGBE_FLAG_FCOE_ENABLED) { struct ixgbe_ring_feature *f; f = &adapter->ring_feature[RING_F_FCOE]; - if ((rx_buf_len < IXGBE_FCOE_JUMBO_FRAME_SIZE) && - (i >= f->mask) && (i < f->mask + f->indices)) - adapter->rx_ring[i].rx_buf_len = - IXGBE_FCOE_JUMBO_FRAME_SIZE; + if ((i >= f->mask) && (i < f->mask + f->indices)) { + rx_ring->flags &= ~IXGBE_RING_RX_PS_ENABLED; + if (rx_buf_len < IXGBE_FCOE_JUMBO_FRAME_SIZE) + rx_ring->rx_buf_len = + IXGBE_FCOE_JUMBO_FRAME_SIZE; + } } #endif /* IXGBE_FCOE */ - ixgbe_configure_srrctl(adapter, j); + ixgbe_configure_srrctl(adapter, rx_ring); } if (hw->mac.type == ixgbe_mac_82598EB) { @@ -2168,7 +2143,8 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) if (adapter->flags2 & IXGBE_FLAG2_RSC_ENABLED) { /* Enable 82599 HW-RSC */ for (i = 0; i < adapter->num_rx_queues; i++) { - j = adapter->rx_ring[i].reg_idx; + rx_ring = &adapter->rx_ring[i]; + j = rx_ring->reg_idx; rscctrl = IXGBE_READ_REG(hw, IXGBE_RSCCTL(j)); rscctrl |= IXGBE_RSCCTL_RSCEN; /* @@ -2176,7 +2152,7 @@ static void ixgbe_configure_rx(struct ixgbe_adapter *adapter) * total size of max desc * buf_len is not greater * than 65535 */ - if (adapter->flags & IXGBE_FLAG_RX_PS_ENABLED) { + if (rx_ring->flags & IXGBE_RING_RX_PS_ENABLED) { #if (MAX_SKB_FRAGS > 16) rscctrl |= IXGBE_RSCCTL_MAXDESC_16; #elif (MAX_SKB_FRAGS > 8) diff --git a/drivers/net/mlx4/en_rx.c b/drivers/net/mlx4/en_rx.c index 91bdfdfd431..3ac0404d0d1 100644 --- a/drivers/net/mlx4/en_rx.c +++ b/drivers/net/mlx4/en_rx.c @@ -506,8 +506,9 @@ static int mlx4_en_complete_rx_desc(struct mlx4_en_priv *priv, PCI_DMA_FROMDEVICE); } /* Adjust size of last fragment to match actual length */ - skb_frags_rx[nr - 1].size = length - - priv->frag_info[nr - 1].frag_prefix_size; + if (nr > 0) + skb_frags_rx[nr - 1].size = length - + priv->frag_info[nr - 1].frag_prefix_size; return nr; fail: diff --git a/drivers/net/netxen/netxen_nic.h b/drivers/net/netxen/netxen_nic.h index f86e05047d1..a9c1fcca5e7 100644 --- a/drivers/net/netxen/netxen_nic.h +++ b/drivers/net/netxen/netxen_nic.h @@ -1254,7 +1254,7 @@ struct netxen_adapter { u8 mc_enabled; u8 max_mc_count; u8 rss_supported; - u8 resv2; + u8 link_changed; u32 resv3; u8 has_link_events; diff --git a/drivers/net/netxen/netxen_nic_init.c b/drivers/net/netxen/netxen_nic_init.c index 7acf204e38c..5d3343ef3d8 100644 --- a/drivers/net/netxen/netxen_nic_init.c +++ b/drivers/net/netxen/netxen_nic_init.c @@ -184,13 +184,6 @@ void netxen_free_sw_resources(struct netxen_adapter *adapter) kfree(recv_ctx->rds_rings); skip_rds: - if (recv_ctx->sds_rings == NULL) - goto skip_sds; - - for(ring = 0; ring < adapter->max_sds_rings; ring++) - recv_ctx->sds_rings[ring].consumer = 0; - -skip_sds: if (adapter->tx_ring == NULL) return; diff --git a/drivers/net/netxen/netxen_nic_main.c b/drivers/net/netxen/netxen_nic_main.c index 3cd8cfcf627..28f270f5ac7 100644 --- a/drivers/net/netxen/netxen_nic_main.c +++ b/drivers/net/netxen/netxen_nic_main.c @@ -94,10 +94,6 @@ static struct pci_device_id netxen_pci_tbl[] __devinitdata = { MODULE_DEVICE_TABLE(pci, netxen_pci_tbl); -static struct workqueue_struct *netxen_workq; -#define SCHEDULE_WORK(tp) queue_work(netxen_workq, tp) -#define FLUSH_SCHEDULED_WORK() flush_workqueue(netxen_workq) - static void netxen_watchdog(unsigned long); static uint32_t crb_cmd_producer[4] = { @@ -171,6 +167,8 @@ netxen_free_sds_rings(struct netxen_recv_context *recv_ctx) { if (recv_ctx->sds_rings != NULL) kfree(recv_ctx->sds_rings); + + recv_ctx->sds_rings = NULL; } static int @@ -193,6 +191,21 @@ netxen_napi_add(struct netxen_adapter *adapter, struct net_device *netdev) } static void +netxen_napi_del(struct netxen_adapter *adapter) +{ + int ring; + struct nx_host_sds_ring *sds_ring; + struct netxen_recv_context *recv_ctx = &adapter->recv_ctx; + + for (ring = 0; ring < adapter->max_sds_rings; ring++) { + sds_ring = &recv_ctx->sds_rings[ring]; + netif_napi_del(&sds_ring->napi); + } + + netxen_free_sds_rings(&adapter->recv_ctx); +} + +static void netxen_napi_enable(struct netxen_adapter *adapter) { int ring; @@ -260,7 +273,7 @@ nx_update_dma_mask(struct netxen_adapter *adapter) change = 0; shift = NXRD32(adapter, CRB_DMA_SHIFT); - if (shift >= 32) + if (shift > 32) return 0; if (NX_IS_REVISION_P3(adapter->ahw.revision_id) && (shift > 9)) @@ -272,7 +285,7 @@ nx_update_dma_mask(struct netxen_adapter *adapter) old_mask = pdev->dma_mask; old_cmask = pdev->dev.coherent_dma_mask; - mask = (1ULL<<(32+shift)) - 1; + mask = DMA_BIT_MASK(32+shift); err = pci_set_dma_mask(pdev, mask); if (err) @@ -880,7 +893,6 @@ netxen_nic_down(struct netxen_adapter *adapter, struct net_device *netdev) spin_unlock(&adapter->tx_clean_lock); del_timer_sync(&adapter->watchdog_timer); - FLUSH_SCHEDULED_WORK(); } @@ -894,10 +906,12 @@ netxen_nic_attach(struct netxen_adapter *adapter) struct nx_host_tx_ring *tx_ring; err = netxen_init_firmware(adapter); - if (err != 0) { - printk(KERN_ERR "Failed to init firmware\n"); - return -EIO; - } + if (err) + return err; + + err = netxen_napi_add(adapter, netdev); + if (err) + return err; if (adapter->fw_major < 4) adapter->max_rds_rings = 3; @@ -961,6 +975,7 @@ netxen_nic_detach(struct netxen_adapter *adapter) netxen_free_hw_resources(adapter); netxen_release_rx_buffers(adapter); netxen_nic_free_irq(adapter); + netxen_napi_del(adapter); netxen_free_sw_resources(adapter); adapter->is_up = 0; @@ -1105,9 +1120,6 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) netdev->irq = adapter->msix_entries[0].vector; - if (netxen_napi_add(adapter, netdev)) - goto err_out_disable_msi; - init_timer(&adapter->watchdog_timer); adapter->watchdog_timer.function = &netxen_watchdog; adapter->watchdog_timer.data = (unsigned long)adapter; @@ -1177,6 +1189,9 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) unregister_netdev(netdev); + cancel_work_sync(&adapter->watchdog_task); + cancel_work_sync(&adapter->tx_timeout_task); + if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) { netxen_nic_detach(adapter); } @@ -1185,7 +1200,6 @@ static void __devexit netxen_nic_remove(struct pci_dev *pdev) netxen_free_adapter_offload(adapter); netxen_teardown_intr(adapter); - netxen_free_sds_rings(&adapter->recv_ctx); netxen_cleanup_pci_map(adapter); @@ -1211,6 +1225,9 @@ netxen_nic_suspend(struct pci_dev *pdev, pm_message_t state) if (netif_running(netdev)) netxen_nic_down(adapter, netdev); + cancel_work_sync(&adapter->watchdog_task); + cancel_work_sync(&adapter->tx_timeout_task); + if (adapter->is_up == NETXEN_ADAPTER_UP_MAGIC) netxen_nic_detach(adapter); @@ -1549,11 +1566,6 @@ static int netxen_nic_check_temp(struct netxen_adapter *adapter) "%s: Device temperature %d degrees C exceeds" " maximum allowed. Hardware has been shut down.\n", netdev->name, temp_val); - - netif_device_detach(netdev); - netxen_nic_down(adapter, netdev); - netxen_nic_detach(adapter); - rv = 1; } else if (temp_state == NX_TEMP_WARN) { if (adapter->temp == NX_TEMP_NORMAL) { @@ -1587,10 +1599,7 @@ void netxen_advert_link_change(struct netxen_adapter *adapter, int linkup) netif_carrier_off(netdev); netif_stop_queue(netdev); } - - if (!adapter->has_link_events) - netxen_nic_set_link_parameters(adapter); - + adapter->link_changed = !adapter->has_link_events; } else if (!adapter->ahw.linkup && linkup) { printk(KERN_INFO "%s: %s NIC Link is up\n", netxen_nic_driver_name, netdev->name); @@ -1599,9 +1608,7 @@ void netxen_advert_link_change(struct netxen_adapter *adapter, int linkup) netif_carrier_on(netdev); netif_wake_queue(netdev); } - - if (!adapter->has_link_events) - netxen_nic_set_link_parameters(adapter); + adapter->link_changed = !adapter->has_link_events; } } @@ -1628,11 +1635,36 @@ static void netxen_nic_handle_phy_intr(struct netxen_adapter *adapter) netxen_advert_link_change(adapter, linkup); } +static void netxen_nic_thermal_shutdown(struct netxen_adapter *adapter) +{ + struct net_device *netdev = adapter->netdev; + + netif_device_detach(netdev); + netxen_nic_down(adapter, netdev); + netxen_nic_detach(adapter); +} + static void netxen_watchdog(unsigned long v) { struct netxen_adapter *adapter = (struct netxen_adapter *)v; - SCHEDULE_WORK(&adapter->watchdog_task); + if (netxen_nic_check_temp(adapter)) + goto do_sched; + + if (!adapter->has_link_events) { + netxen_nic_handle_phy_intr(adapter); + + if (adapter->link_changed) + goto do_sched; + } + + if (netif_running(adapter->netdev)) + mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); + + return; + +do_sched: + schedule_work(&adapter->watchdog_task); } void netxen_watchdog_task(struct work_struct *work) @@ -1640,11 +1672,13 @@ void netxen_watchdog_task(struct work_struct *work) struct netxen_adapter *adapter = container_of(work, struct netxen_adapter, watchdog_task); - if (netxen_nic_check_temp(adapter)) + if (adapter->temp == NX_TEMP_PANIC) { + netxen_nic_thermal_shutdown(adapter); return; + } - if (!adapter->has_link_events) - netxen_nic_handle_phy_intr(adapter); + if (adapter->link_changed) + netxen_nic_set_link_parameters(adapter); if (netif_running(adapter->netdev)) mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ); @@ -1652,9 +1686,8 @@ void netxen_watchdog_task(struct work_struct *work) static void netxen_tx_timeout(struct net_device *netdev) { - struct netxen_adapter *adapter = (struct netxen_adapter *) - netdev_priv(netdev); - SCHEDULE_WORK(&adapter->tx_timeout_task); + struct netxen_adapter *adapter = netdev_priv(netdev); + schedule_work(&adapter->tx_timeout_task); } static void netxen_tx_timeout_task(struct work_struct *work) @@ -1811,9 +1844,6 @@ static int __init netxen_init_module(void) { printk(KERN_INFO "%s\n", netxen_nic_driver_string); - if ((netxen_workq = create_singlethread_workqueue("netxen")) == NULL) - return -ENOMEM; - return pci_register_driver(&netxen_driver); } @@ -1822,7 +1852,6 @@ module_init(netxen_init_module); static void __exit netxen_exit_module(void) { pci_unregister_driver(&netxen_driver); - destroy_workqueue(netxen_workq); } module_exit(netxen_exit_module); diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c index a646a445fda..23e1a0750fe 100644 --- a/drivers/net/pcnet32.c +++ b/drivers/net/pcnet32.c @@ -1839,7 +1839,7 @@ pcnet32_probe1(unsigned long ioaddr, int shared, struct pci_dev *pdev) lp->chip_version = chip_version; lp->msg_enable = pcnet32_debug; if ((cards_found >= MAX_UNITS) - || (options[cards_found] > sizeof(options_mapping))) + || (options[cards_found] >= sizeof(options_mapping))) lp->options = PCNET32_PORT_ASEL; else lp->options = options_mapping[options[cards_found]]; diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c index 99a63649f4f..4cf9a658875 100644 --- a/drivers/net/tulip/tulip_core.c +++ b/drivers/net/tulip/tulip_core.c @@ -652,8 +652,9 @@ tulip_start_xmit(struct sk_buff *skb, struct net_device *dev) int entry; u32 flag; dma_addr_t mapping; + unsigned long flags; - spin_lock_irq(&tp->lock); + spin_lock_irqsave(&tp->lock, flags); /* Calculate the next Tx descriptor entry. */ entry = tp->cur_tx % TX_RING_SIZE; @@ -688,7 +689,7 @@ tulip_start_xmit(struct sk_buff *skb, struct net_device *dev) /* Trigger an immediate transmit demand. */ iowrite32(0, tp->base_addr + CSR1); - spin_unlock_irq(&tp->lock); + spin_unlock_irqrestore(&tp->lock, flags); dev->trans_start = jiffies; diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 027f7aba26a..42b6c6319bc 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -1048,20 +1048,15 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) return err; } -static int tun_get_iff(struct net *net, struct file *file, struct ifreq *ifr) +static int tun_get_iff(struct net *net, struct tun_struct *tun, + struct ifreq *ifr) { - struct tun_struct *tun = tun_get(file); - - if (!tun) - return -EBADFD; - DBG(KERN_INFO "%s: tun_get_iff\n", tun->dev->name); strcpy(ifr->ifr_name, tun->dev->name); ifr->ifr_flags = tun_flags(tun); - tun_put(tun); return 0; } @@ -1105,8 +1100,8 @@ static int set_offload(struct net_device *dev, unsigned long arg) return 0; } -static int tun_chr_ioctl(struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) +static long tun_chr_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) { struct tun_file *tfile = file->private_data; struct tun_struct *tun; @@ -1128,34 +1123,32 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, (unsigned int __user*)argp); } + rtnl_lock(); + tun = __tun_get(tfile); if (cmd == TUNSETIFF && !tun) { - int err; - ifr.ifr_name[IFNAMSIZ-1] = '\0'; - rtnl_lock(); - err = tun_set_iff(tfile->net, file, &ifr); - rtnl_unlock(); + ret = tun_set_iff(tfile->net, file, &ifr); - if (err) - return err; + if (ret) + goto unlock; if (copy_to_user(argp, &ifr, sizeof(ifr))) - return -EFAULT; - return 0; + ret = -EFAULT; + goto unlock; } - + ret = -EBADFD; if (!tun) - return -EBADFD; + goto unlock; DBG(KERN_INFO "%s: tun_chr_ioctl cmd %d\n", tun->dev->name, cmd); ret = 0; switch (cmd) { case TUNGETIFF: - ret = tun_get_iff(current->nsproxy->net_ns, file, &ifr); + ret = tun_get_iff(current->nsproxy->net_ns, tun, &ifr); if (ret) break; @@ -1201,7 +1194,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, case TUNSETLINK: /* Only allow setting the type when the interface is down */ - rtnl_lock(); if (tun->dev->flags & IFF_UP) { DBG(KERN_INFO "%s: Linktype set failed because interface is up\n", tun->dev->name); @@ -1211,7 +1203,6 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, DBG(KERN_INFO "%s: linktype set to %d\n", tun->dev->name, tun->dev->type); ret = 0; } - rtnl_unlock(); break; #ifdef TUN_DEBUG @@ -1220,9 +1211,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; #endif case TUNSETOFFLOAD: - rtnl_lock(); ret = set_offload(tun->dev, arg); - rtnl_unlock(); break; case TUNSETTXFILTER: @@ -1230,9 +1219,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, ret = -EINVAL; if ((tun->flags & TUN_TYPE_MASK) != TUN_TAP_DEV) break; - rtnl_lock(); ret = update_filter(&tun->txflt, (void __user *)arg); - rtnl_unlock(); break; case SIOCGIFHWADDR: @@ -1248,9 +1235,7 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, DBG(KERN_DEBUG "%s: set hw address: %pM\n", tun->dev->name, ifr.ifr_hwaddr.sa_data); - rtnl_lock(); ret = dev_set_mac_address(tun->dev, &ifr.ifr_hwaddr); - rtnl_unlock(); break; case TUNGETSNDBUF: @@ -1273,7 +1258,10 @@ static int tun_chr_ioctl(struct inode *inode, struct file *file, break; }; - tun_put(tun); +unlock: + rtnl_unlock(); + if (tun) + tun_put(tun); return ret; } @@ -1361,7 +1349,7 @@ static const struct file_operations tun_fops = { .write = do_sync_write, .aio_write = tun_chr_aio_write, .poll = tun_chr_poll, - .ioctl = tun_chr_ioctl, + .unlocked_ioctl = tun_chr_ioctl, .open = tun_chr_open, .release = tun_chr_close, .fasync = tun_chr_fasync diff --git a/drivers/net/ucc_geth.c b/drivers/net/ucc_geth.c index 3b957e6412e..8a7b8c7bd78 100644 --- a/drivers/net/ucc_geth.c +++ b/drivers/net/ucc_geth.c @@ -3111,10 +3111,11 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) u8 __iomem *bd; /* BD pointer */ u32 bd_status; u8 txQ = 0; + unsigned long flags; ugeth_vdbg("%s: IN", __func__); - spin_lock_irq(&ugeth->lock); + spin_lock_irqsave(&ugeth->lock, flags); dev->stats.tx_bytes += skb->len; @@ -3171,7 +3172,7 @@ static int ucc_geth_start_xmit(struct sk_buff *skb, struct net_device *dev) uccf = ugeth->uccf; out_be16(uccf->p_utodr, UCC_FAST_TOD); #endif - spin_unlock_irq(&ugeth->lock); + spin_unlock_irqrestore(&ugeth->lock, flags); return 0; } diff --git a/drivers/net/usb/pegasus.h b/drivers/net/usb/pegasus.h index c7467823cd1..f968c834ff6 100644 --- a/drivers/net/usb/pegasus.h +++ b/drivers/net/usb/pegasus.h @@ -250,6 +250,8 @@ PEGASUS_DEV( "IO DATA USB ET/TX", VENDOR_IODATA, 0x0904, DEFAULT_GPIO_RESET ) PEGASUS_DEV( "IO DATA USB ET/TX-S", VENDOR_IODATA, 0x0913, DEFAULT_GPIO_RESET | PEGASUS_II ) +PEGASUS_DEV( "IO DATA USB ETX-US2", VENDOR_IODATA, 0x092a, + DEFAULT_GPIO_RESET | PEGASUS_II ) PEGASUS_DEV( "Kingston KNU101TX Ethernet", VENDOR_KINGSTON, 0x000a, DEFAULT_GPIO_RESET) PEGASUS_DEV( "LANEED USB Ethernet LD-USB/TX", VENDOR_LANEED, 0x4002, diff --git a/drivers/net/via-rhine.c b/drivers/net/via-rhine.c index 88c30a58b4b..934f7671650 100644 --- a/drivers/net/via-rhine.c +++ b/drivers/net/via-rhine.c @@ -1218,6 +1218,7 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev) struct rhine_private *rp = netdev_priv(dev); void __iomem *ioaddr = rp->base; unsigned entry; + unsigned long flags; /* Caution: the write order is important here, set the field with the "ownership" bits last. */ @@ -1261,7 +1262,7 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev) cpu_to_le32(TXDESC | (skb->len >= ETH_ZLEN ? skb->len : ETH_ZLEN)); /* lock eth irq */ - spin_lock_irq(&rp->lock); + spin_lock_irqsave(&rp->lock, flags); wmb(); rp->tx_ring[entry].tx_status = cpu_to_le32(DescOwn); wmb(); @@ -1280,7 +1281,7 @@ static int rhine_start_tx(struct sk_buff *skb, struct net_device *dev) dev->trans_start = jiffies; - spin_unlock_irq(&rp->lock); + spin_unlock_irqrestore(&rp->lock, flags); if (debug > 4) { printk(KERN_DEBUG "%s: Transmit frame #%d queued in slot %d.\n", diff --git a/drivers/net/via-velocity.c b/drivers/net/via-velocity.c index 3ba35956327..cee08a1e497 100644 --- a/drivers/net/via-velocity.c +++ b/drivers/net/via-velocity.c @@ -1778,7 +1778,7 @@ static void velocity_error(struct velocity_info *vptr, int status) * mode */ if (vptr->rev_id < REV_ID_VT3216_A0) { - if (vptr->mii_status | VELOCITY_DUPLEX_FULL) + if (vptr->mii_status & VELOCITY_DUPLEX_FULL) BYTE_REG_BITS_ON(TCR_TB2BDIS, ®s->TCR); else BYTE_REG_BITS_OFF(TCR_TB2BDIS, ®s->TCR); diff --git a/drivers/net/wireless/ath/ar9170/main.c b/drivers/net/wireless/ath/ar9170/main.c index 9d38cf60a0d..88c3d857386 100644 --- a/drivers/net/wireless/ath/ar9170/main.c +++ b/drivers/net/wireless/ath/ar9170/main.c @@ -1967,13 +1967,14 @@ static int ar9170_conf_tx(struct ieee80211_hw *hw, u16 queue, int ret; mutex_lock(&ar->mutex); - if ((param) && !(queue > __AR9170_NUM_TXQ)) { + if (queue < __AR9170_NUM_TXQ) { memcpy(&ar->edcf[ar9170_qos_hwmap[queue]], param, sizeof(*param)); ret = ar9170_set_qos(ar); - } else + } else { ret = -EINVAL; + } mutex_unlock(&ar->mutex); return ret; diff --git a/drivers/net/wireless/ath/ar9170/usb.c b/drivers/net/wireless/ath/ar9170/usb.c index 754b1f8d8da..007eb85fc67 100644 --- a/drivers/net/wireless/ath/ar9170/usb.c +++ b/drivers/net/wireless/ath/ar9170/usb.c @@ -598,11 +598,15 @@ static int ar9170_usb_request_firmware(struct ar9170_usb *aru) err = request_firmware(&aru->init_values, "ar9170-1.fw", &aru->udev->dev); + if (err) { + dev_err(&aru->udev->dev, "file with init values not found.\n"); + return err; + } err = request_firmware(&aru->firmware, "ar9170-2.fw", &aru->udev->dev); if (err) { release_firmware(aru->init_values); - dev_err(&aru->udev->dev, "file with init values not found.\n"); + dev_err(&aru->udev->dev, "firmware file not found.\n"); return err; } diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 44c29b3f672..6dcac73b4d2 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -6226,7 +6226,7 @@ static void ipw_add_scan_channels(struct ipw_priv *priv, }; u8 channel; - while (channel_index < IPW_SCAN_CHANNELS) { + while (channel_index < IPW_SCAN_CHANNELS - 1) { channel = priv->speed_scan[priv->speed_scan_pos]; if (channel == 0) { diff --git a/drivers/net/wireless/libertas/assoc.c b/drivers/net/wireless/libertas/assoc.c index d6997371c27..b9b37411903 100644 --- a/drivers/net/wireless/libertas/assoc.c +++ b/drivers/net/wireless/libertas/assoc.c @@ -1,7 +1,6 @@ /* Copyright (C) 2006, Red Hat, Inc. */ #include <linux/types.h> -#include <linux/kernel.h> #include <linux/etherdevice.h> #include <linux/ieee80211.h> #include <linux/if_arp.h> @@ -44,21 +43,21 @@ static int get_common_rates(struct lbs_private *priv, u16 *rates_size) { u8 *card_rates = lbs_bg_rates; + size_t num_card_rates = sizeof(lbs_bg_rates); int ret = 0, i, j; - u8 tmp[(ARRAY_SIZE(lbs_bg_rates) - 1) * (*rates_size - 1)]; + u8 tmp[30]; size_t tmp_size = 0; /* For each rate in card_rates that exists in rate1, copy to tmp */ - for (i = 0; i < ARRAY_SIZE(lbs_bg_rates) && card_rates[i]; i++) { - for (j = 0; j < *rates_size && rates[j]; j++) { + for (i = 0; card_rates[i] && (i < num_card_rates); i++) { + for (j = 0; rates[j] && (j < *rates_size); j++) { if (rates[j] == card_rates[i]) tmp[tmp_size++] = card_rates[i]; } } lbs_deb_hex(LBS_DEB_JOIN, "AP rates ", rates, *rates_size); - lbs_deb_hex(LBS_DEB_JOIN, "card rates ", card_rates, - ARRAY_SIZE(lbs_bg_rates)); + lbs_deb_hex(LBS_DEB_JOIN, "card rates ", card_rates, num_card_rates); lbs_deb_hex(LBS_DEB_JOIN, "common rates", tmp, tmp_size); lbs_deb_join("TX data rate 0x%02x\n", priv->cur_rate); @@ -70,7 +69,10 @@ static int get_common_rates(struct lbs_private *priv, lbs_pr_alert("Previously set fixed data rate %#x isn't " "compatible with the network.\n", priv->cur_rate); ret = -1; + goto done; } + ret = 0; + done: memset(rates, 0, *rates_size); *rates_size = min_t(int, tmp_size, *rates_size); @@ -320,7 +322,7 @@ static int lbs_associate(struct lbs_private *priv, rates = (struct mrvl_ie_rates_param_set *) pos; rates->header.type = cpu_to_le16(TLV_TYPE_RATES); memcpy(&rates->rates, &bss->rates, MAX_RATES); - tmplen = min_t(u16, ARRAY_SIZE(rates->rates), MAX_RATES); + tmplen = MAX_RATES; if (get_common_rates(priv, rates->rates, &tmplen)) { ret = -1; goto done; @@ -596,7 +598,7 @@ static int lbs_adhoc_join(struct lbs_private *priv, /* Copy Data rates from the rates recorded in scan response */ memset(cmd.bss.rates, 0, sizeof(cmd.bss.rates)); - ratesize = min_t(u16, ARRAY_SIZE(cmd.bss.rates), MAX_RATES); + ratesize = min_t(u16, sizeof(cmd.bss.rates), MAX_RATES); memcpy(cmd.bss.rates, bss->rates, ratesize); if (get_common_rates(priv, cmd.bss.rates, &ratesize)) { lbs_deb_join("ADHOC_JOIN: get_common_rates returned error.\n"); diff --git a/drivers/net/wireless/libertas/hostcmd.h b/drivers/net/wireless/libertas/hostcmd.h index 0a2e29140ad..c8a1998d474 100644 --- a/drivers/net/wireless/libertas/hostcmd.h +++ b/drivers/net/wireless/libertas/hostcmd.h @@ -56,8 +56,8 @@ struct rxpd { u8 bss_type; /* BSS number */ u8 bss_num; - } bss; - } u; + } __attribute__ ((packed)) bss; + } __attribute__ ((packed)) u; /* SNR */ u8 snr; diff --git a/drivers/net/wireless/mwl8k.c b/drivers/net/wireless/mwl8k.c index a263d5c84c0..83967afe082 100644 --- a/drivers/net/wireless/mwl8k.c +++ b/drivers/net/wireless/mwl8k.c @@ -261,7 +261,7 @@ struct mwl8k_vif { */ }; -#define MWL8K_VIF(_vif) (struct mwl8k_vif *)(&((_vif)->drv_priv)) +#define MWL8K_VIF(_vif) ((struct mwl8k_vif *)&((_vif)->drv_priv)) static const struct ieee80211_channel mwl8k_channels[] = { { .center_freq = 2412, .hw_value = 1, }, @@ -1012,6 +1012,8 @@ static int rxq_process(struct ieee80211_hw *hw, int index, int limit) rmb(); skb = rxq->rx_skb[rxq->rx_head]; + if (skb == NULL) + break; rxq->rx_skb[rxq->rx_head] = NULL; rxq->rx_head = (rxq->rx_head + 1) % MWL8K_RX_DESCS; @@ -1591,6 +1593,9 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) timeout = wait_for_completion_timeout(&cmd_wait, msecs_to_jiffies(MWL8K_CMD_TIMEOUT_MS)); + pci_unmap_single(priv->pdev, dma_addr, dma_size, + PCI_DMA_BIDIRECTIONAL); + result = &cmd->result; if (!timeout) { spin_lock_irq(&priv->fw_lock); @@ -1610,8 +1615,6 @@ static int mwl8k_post_cmd(struct ieee80211_hw *hw, struct mwl8k_cmd_pkt *cmd) *result); } - pci_unmap_single(priv->pdev, dma_addr, dma_size, - PCI_DMA_BIDIRECTIONAL); return rc; } @@ -1654,18 +1657,18 @@ static int mwl8k_cmd_get_hw_spec(struct ieee80211_hw *hw) memset(cmd->perm_addr, 0xff, sizeof(cmd->perm_addr)); cmd->ps_cookie = cpu_to_le32(priv->cookie_dma); cmd->rx_queue_ptr = cpu_to_le32(priv->rxq[0].rx_desc_dma); - cmd->num_tx_queues = MWL8K_TX_QUEUES; + cmd->num_tx_queues = cpu_to_le32(MWL8K_TX_QUEUES); for (i = 0; i < MWL8K_TX_QUEUES; i++) cmd->tx_queue_ptrs[i] = cpu_to_le32(priv->txq[i].tx_desc_dma); - cmd->num_tx_desc_per_queue = MWL8K_TX_DESCS; - cmd->total_rx_desc = MWL8K_RX_DESCS; + cmd->num_tx_desc_per_queue = cpu_to_le32(MWL8K_TX_DESCS); + cmd->total_rx_desc = cpu_to_le32(MWL8K_RX_DESCS); rc = mwl8k_post_cmd(hw, &cmd->header); if (!rc) { SET_IEEE80211_PERM_ADDR(hw, cmd->perm_addr); priv->num_mcaddrs = le16_to_cpu(cmd->num_mcaddrs); - priv->fw_rev = cmd->fw_rev; + priv->fw_rev = le32_to_cpu(cmd->fw_rev); priv->hw_rev = cmd->hw_rev; priv->region_code = le16_to_cpu(cmd->region_code); } @@ -3216,15 +3219,19 @@ static int mwl8k_configure_filter_wt(struct work_struct *wt) struct dev_addr_list *mclist = worker->mclist; struct mwl8k_priv *priv = hw->priv; - struct mwl8k_vif *mv_vif; int rc = 0; if (changed_flags & FIF_BCN_PRBRESP_PROMISC) { if (*total_flags & FIF_BCN_PRBRESP_PROMISC) rc = mwl8k_cmd_set_pre_scan(hw); else { - mv_vif = MWL8K_VIF(priv->vif); - rc = mwl8k_cmd_set_post_scan(hw, mv_vif->bssid); + u8 *bssid; + + bssid = "\x00\x00\x00\x00\x00\x00"; + if (priv->vif != NULL) + bssid = MWL8K_VIF(priv->vif)->bssid; + + rc = mwl8k_cmd_set_post_scan(hw, bssid); } } @@ -3726,6 +3733,8 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) ieee80211_stop_queues(hw); + ieee80211_unregister_hw(hw); + /* Remove tx reclaim tasklet */ tasklet_kill(&priv->tx_reclaim_task); @@ -3739,8 +3748,6 @@ static void __devexit mwl8k_remove(struct pci_dev *pdev) for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_reclaim(hw, i, 1); - ieee80211_unregister_hw(hw); - for (i = 0; i < MWL8K_TX_QUEUES; i++) mwl8k_txq_deinit(hw, i); diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index a498dde024e..49c9e2c1433 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -849,13 +849,15 @@ struct rt2x00_dev { static inline void rt2x00_rf_read(struct rt2x00_dev *rt2x00dev, const unsigned int word, u32 *data) { - *data = rt2x00dev->rf[word]; + BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); + *data = rt2x00dev->rf[word - 1]; } static inline void rt2x00_rf_write(struct rt2x00_dev *rt2x00dev, const unsigned int word, u32 data) { - rt2x00dev->rf[word] = data; + BUG_ON(word < 1 || word > rt2x00dev->ops->rf_size / sizeof(u32)); + rt2x00dev->rf[word - 1] = data; } /* diff --git a/drivers/net/zorro8390.c b/drivers/net/zorro8390.c index 37c84e3b8be..81c753a617a 100644 --- a/drivers/net/zorro8390.c +++ b/drivers/net/zorro8390.c @@ -120,6 +120,9 @@ static int __devinit zorro8390_init_one(struct zorro_dev *z, for (i = ARRAY_SIZE(cards)-1; i >= 0; i--) if (z->id == cards[i].id) break; + if (i < 0) + return -ENODEV; + board = z->resource.start; ioaddr = board+cards[i].offset; dev = alloc_ei_netdev(); diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index d76c4c85367..a7eb7277b10 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -508,7 +508,7 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev) return error; } - return pci_dev->state_saved ? pci_restore_state(pci_dev) : 0; + return pci_restore_state(pci_dev); } static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) @@ -575,7 +575,7 @@ static void pci_pm_complete(struct device *dev) static int pci_pm_suspend(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_SUSPEND); @@ -613,7 +613,7 @@ static int pci_pm_suspend(struct device *dev) static int pci_pm_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_SUSPEND); @@ -672,7 +672,7 @@ static int pci_pm_resume_noirq(struct device *dev) static int pci_pm_resume(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -711,7 +711,7 @@ static int pci_pm_resume(struct device *dev) static int pci_pm_freeze(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_FREEZE); @@ -780,7 +780,7 @@ static int pci_pm_thaw_noirq(struct device *dev) static int pci_pm_thaw(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; if (pci_has_legacy_pm_support(pci_dev)) @@ -799,7 +799,7 @@ static int pci_pm_thaw(struct device *dev) static int pci_pm_poweroff(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE); @@ -872,7 +872,7 @@ static int pci_pm_restore_noirq(struct device *dev) static int pci_pm_restore(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -910,7 +910,7 @@ static int pci_pm_restore(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -struct dev_pm_ops pci_dev_pm_ops = { +const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, .complete = pci_pm_complete, .suspend = pci_pm_suspend, diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index dbd0f947f49..7b70312181d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -846,6 +846,8 @@ pci_restore_state(struct pci_dev *dev) int i; u32 val; + if (!dev->state_saved) + return 0; /* PCI Express register must be restored first */ pci_restore_pcie_state(dev); diff --git a/drivers/rtc/rtc-ds1302.c b/drivers/rtc/rtc-ds1302.c index 18455662077..d490628b64d 100644 --- a/drivers/rtc/rtc-ds1302.c +++ b/drivers/rtc/rtc-ds1302.c @@ -1,26 +1,25 @@ /* * Dallas DS1302 RTC Support * - * Copyright (C) 2002 David McCullough - * Copyright (C) 2003 - 2007 Paul Mundt + * Copyright (C) 2002 David McCullough + * Copyright (C) 2003 - 2007 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public - * License version 2. See the file "COPYING" in the main directory of + * License version 2. See the file "COPYING" in the main directory of * this archive for more details. */ + #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/platform_device.h> -#include <linux/time.h> #include <linux/rtc.h> -#include <linux/spinlock.h> #include <linux/io.h> #include <linux/bcd.h> #include <asm/rtc.h> #define DRV_NAME "rtc-ds1302" -#define DRV_VERSION "0.1.0" +#define DRV_VERSION "0.1.1" #define RTC_CMD_READ 0x81 /* Read command */ #define RTC_CMD_WRITE 0x80 /* Write command */ @@ -47,11 +46,6 @@ #error "Add support for your platform" #endif -struct ds1302_rtc { - struct rtc_device *rtc_dev; - spinlock_t lock; -}; - static void ds1302_sendbits(unsigned int val) { int i; @@ -103,10 +97,6 @@ static void ds1302_writebyte(unsigned int addr, unsigned int val) static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm) { - struct ds1302_rtc *rtc = dev_get_drvdata(dev); - - spin_lock_irq(&rtc->lock); - tm->tm_sec = bcd2bin(ds1302_readbyte(RTC_ADDR_SEC)); tm->tm_min = bcd2bin(ds1302_readbyte(RTC_ADDR_MIN)); tm->tm_hour = bcd2bin(ds1302_readbyte(RTC_ADDR_HOUR)); @@ -118,26 +108,17 @@ static int ds1302_rtc_read_time(struct device *dev, struct rtc_time *tm) if (tm->tm_year < 70) tm->tm_year += 100; - spin_unlock_irq(&rtc->lock); - dev_dbg(dev, "%s: tm is secs=%d, mins=%d, hours=%d, " "mday=%d, mon=%d, year=%d, wday=%d\n", __func__, tm->tm_sec, tm->tm_min, tm->tm_hour, tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday); - if (rtc_valid_tm(tm) < 0) - dev_err(dev, "invalid date\n"); - - return 0; + return rtc_valid_tm(tm); } static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm) { - struct ds1302_rtc *rtc = dev_get_drvdata(dev); - - spin_lock_irq(&rtc->lock); - /* Stop RTC */ ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) | 0x80); @@ -152,8 +133,6 @@ static int ds1302_rtc_set_time(struct device *dev, struct rtc_time *tm) /* Start RTC */ ds1302_writebyte(RTC_ADDR_SEC, ds1302_readbyte(RTC_ADDR_SEC) & ~0x80); - spin_unlock_irq(&rtc->lock); - return 0; } @@ -170,9 +149,7 @@ static int ds1302_rtc_ioctl(struct device *dev, unsigned int cmd, if (copy_from_user(&tcs_val, (int __user *)arg, sizeof(int))) return -EFAULT; - spin_lock_irq(&rtc->lock); ds1302_writebyte(RTC_ADDR_TCR, (0xa0 | tcs_val * 0xf)); - spin_unlock_irq(&rtc->lock); return 0; } #endif @@ -187,10 +164,9 @@ static struct rtc_class_ops ds1302_rtc_ops = { .ioctl = ds1302_rtc_ioctl, }; -static int __devinit ds1302_rtc_probe(struct platform_device *pdev) +static int __init ds1302_rtc_probe(struct platform_device *pdev) { - struct ds1302_rtc *rtc; - int ret; + struct rtc_device *rtc; /* Reset */ set_dp(get_dp() & ~(RTC_RESET | RTC_IODATA | RTC_SCLK)); @@ -200,37 +176,23 @@ static int __devinit ds1302_rtc_probe(struct platform_device *pdev) if (ds1302_readbyte(RTC_ADDR_RAM0) != 0x42) return -ENODEV; - rtc = kzalloc(sizeof(struct ds1302_rtc), GFP_KERNEL); - if (unlikely(!rtc)) - return -ENOMEM; - - spin_lock_init(&rtc->lock); - rtc->rtc_dev = rtc_device_register("ds1302", &pdev->dev, + rtc = rtc_device_register("ds1302", &pdev->dev, &ds1302_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc->rtc_dev)) { - ret = PTR_ERR(rtc->rtc_dev); - goto out; - } + if (IS_ERR(rtc)) + return PTR_ERR(rtc); platform_set_drvdata(pdev, rtc); return 0; -out: - kfree(rtc); - return ret; } static int __devexit ds1302_rtc_remove(struct platform_device *pdev) { - struct ds1302_rtc *rtc = platform_get_drvdata(pdev); - - if (likely(rtc->rtc_dev)) - rtc_device_unregister(rtc->rtc_dev); + struct rtc_device *rtc = platform_get_drvdata(pdev); + rtc_device_unregister(rtc); platform_set_drvdata(pdev, NULL); - kfree(rtc); - return 0; } @@ -239,13 +201,12 @@ static struct platform_driver ds1302_platform_driver = { .name = DRV_NAME, .owner = THIS_MODULE, }, - .probe = ds1302_rtc_probe, - .remove = __devexit_p(ds1302_rtc_remove), + .remove = __exit_p(ds1302_rtc_remove), }; static int __init ds1302_rtc_init(void) { - return platform_driver_register(&ds1302_platform_driver); + return platform_driver_probe(&ds1302_platform_driver, ds1302_rtc_probe); } static void __exit ds1302_rtc_exit(void) diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c index d7310adb715..39a2fcd98c2 100644 --- a/drivers/rtc/rtc-sh.c +++ b/drivers/rtc/rtc-sh.c @@ -29,7 +29,7 @@ #include <asm/rtc.h> #define DRV_NAME "sh-rtc" -#define DRV_VERSION "0.2.2" +#define DRV_VERSION "0.2.3" #define RTC_REG(r) ((r) * rtc_reg_size) @@ -215,7 +215,7 @@ static irqreturn_t sh_rtc_shared(int irq, void *dev_id) return IRQ_RETVAL(ret); } -static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) +static int sh_rtc_irq_set_state(struct device *dev, int enable) { struct sh_rtc *rtc = dev_get_drvdata(dev); unsigned int tmp; @@ -225,17 +225,22 @@ static inline void sh_rtc_setpie(struct device *dev, unsigned int enable) tmp = readb(rtc->regbase + RCR2); if (enable) { + rtc->periodic_freq |= PF_KOU; tmp &= ~RCR2_PEF; /* Clear PES bit */ tmp |= (rtc->periodic_freq & ~PF_HP); /* Set PES2-0 */ - } else + } else { + rtc->periodic_freq &= ~PF_KOU; tmp &= ~(RCR2_PESMASK | RCR2_PEF); + } writeb(tmp, rtc->regbase + RCR2); spin_unlock_irq(&rtc->lock); + + return 0; } -static inline int sh_rtc_setfreq(struct device *dev, unsigned int freq) +static int sh_rtc_irq_set_freq(struct device *dev, int freq) { struct sh_rtc *rtc = dev_get_drvdata(dev); int tmp, ret = 0; @@ -346,10 +351,6 @@ static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) unsigned int ret = 0; switch (cmd) { - case RTC_PIE_OFF: - case RTC_PIE_ON: - sh_rtc_setpie(dev, cmd == RTC_PIE_ON); - break; case RTC_AIE_OFF: case RTC_AIE_ON: sh_rtc_setaie(dev, cmd == RTC_AIE_ON); @@ -362,13 +363,6 @@ static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) rtc->periodic_freq |= PF_OXS; sh_rtc_setcie(dev, 1); break; - case RTC_IRQP_READ: - ret = put_user(rtc->rtc_dev->irq_freq, - (unsigned long __user *)arg); - break; - case RTC_IRQP_SET: - ret = sh_rtc_setfreq(dev, arg); - break; default: ret = -ENOIOCTLCMD; } @@ -602,28 +596,6 @@ static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm) return 0; } -static int sh_rtc_irq_set_state(struct device *dev, int enabled) -{ - struct platform_device *pdev = to_platform_device(dev); - struct sh_rtc *rtc = platform_get_drvdata(pdev); - - if (enabled) { - rtc->periodic_freq |= PF_KOU; - return sh_rtc_ioctl(dev, RTC_PIE_ON, 0); - } else { - rtc->periodic_freq &= ~PF_KOU; - return sh_rtc_ioctl(dev, RTC_PIE_OFF, 0); - } -} - -static int sh_rtc_irq_set_freq(struct device *dev, int freq) -{ - if (!is_power_of_2(freq)) - return -EINVAL; - - return sh_rtc_ioctl(dev, RTC_IRQP_SET, freq); -} - static struct rtc_class_ops sh_rtc_ops = { .ioctl = sh_rtc_ioctl, .read_time = sh_rtc_read_time, @@ -635,7 +607,7 @@ static struct rtc_class_ops sh_rtc_ops = { .proc = sh_rtc_proc, }; -static int __devinit sh_rtc_probe(struct platform_device *pdev) +static int __init sh_rtc_probe(struct platform_device *pdev) { struct sh_rtc *rtc; struct resource *res; @@ -702,13 +674,6 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) clk_enable(rtc->clk); - rtc->rtc_dev = rtc_device_register("sh", &pdev->dev, - &sh_rtc_ops, THIS_MODULE); - if (IS_ERR(rtc->rtc_dev)) { - ret = PTR_ERR(rtc->rtc_dev); - goto err_unmap; - } - rtc->capabilities = RTC_DEF_CAPABILITIES; if (pdev->dev.platform_data) { struct sh_rtc_platform_info *pinfo = pdev->dev.platform_data; @@ -720,10 +685,6 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) rtc->capabilities |= pinfo->capabilities; } - rtc->rtc_dev->max_user_freq = 256; - - platform_set_drvdata(pdev, rtc); - if (rtc->carry_irq <= 0) { /* register shared periodic/carry/alarm irq */ ret = request_irq(rtc->periodic_irq, sh_rtc_shared, @@ -767,13 +728,26 @@ static int __devinit sh_rtc_probe(struct platform_device *pdev) } } + platform_set_drvdata(pdev, rtc); + /* everything disabled by default */ - rtc->periodic_freq = 0; - rtc->rtc_dev->irq_freq = 0; - sh_rtc_setpie(&pdev->dev, 0); + sh_rtc_irq_set_freq(&pdev->dev, 0); + sh_rtc_irq_set_state(&pdev->dev, 0); sh_rtc_setaie(&pdev->dev, 0); sh_rtc_setcie(&pdev->dev, 0); + rtc->rtc_dev = rtc_device_register("sh", &pdev->dev, + &sh_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc->rtc_dev)) { + ret = PTR_ERR(rtc->rtc_dev); + free_irq(rtc->periodic_irq, rtc); + free_irq(rtc->carry_irq, rtc); + free_irq(rtc->alarm_irq, rtc); + goto err_unmap; + } + + rtc->rtc_dev->max_user_freq = 256; + /* reset rtc to epoch 0 if time is invalid */ if (rtc_read_time(rtc->rtc_dev, &r) < 0) { rtc_time_to_tm(0, &r); @@ -795,14 +769,13 @@ err_badres: return ret; } -static int __devexit sh_rtc_remove(struct platform_device *pdev) +static int __exit sh_rtc_remove(struct platform_device *pdev) { struct sh_rtc *rtc = platform_get_drvdata(pdev); - if (likely(rtc->rtc_dev)) - rtc_device_unregister(rtc->rtc_dev); + rtc_device_unregister(rtc->rtc_dev); + sh_rtc_irq_set_state(&pdev->dev, 0); - sh_rtc_setpie(&pdev->dev, 0); sh_rtc_setaie(&pdev->dev, 0); sh_rtc_setcie(&pdev->dev, 0); @@ -813,9 +786,8 @@ static int __devexit sh_rtc_remove(struct platform_device *pdev) free_irq(rtc->alarm_irq, rtc); } - release_resource(rtc->res); - iounmap(rtc->regbase); + release_resource(rtc->res); clk_disable(rtc->clk); clk_put(rtc->clk); @@ -867,13 +839,12 @@ static struct platform_driver sh_rtc_platform_driver = { .owner = THIS_MODULE, .pm = &sh_rtc_dev_pm_ops, }, - .probe = sh_rtc_probe, - .remove = __devexit_p(sh_rtc_remove), + .remove = __exit_p(sh_rtc_remove), }; static int __init sh_rtc_init(void) { - return platform_driver_register(&sh_rtc_platform_driver); + return platform_driver_probe(&sh_rtc_platform_driver, sh_rtc_probe); } static void __exit sh_rtc_exit(void) diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c index f3da592f7bc..35a13867495 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.c +++ b/drivers/scsi/mpt2sas/mpt2sas_base.c @@ -119,6 +119,64 @@ _base_fault_reset_work(struct work_struct *work) spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); } +/** + * mpt2sas_base_start_watchdog - start the fault_reset_work_q + * @ioc: pointer to scsi command object + * Context: sleep. + * + * Return nothing. + */ +void +mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc) +{ + unsigned long flags; + + if (ioc->fault_reset_work_q) + return; + + /* initialize fault polling */ + INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); + snprintf(ioc->fault_reset_work_q_name, + sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id); + ioc->fault_reset_work_q = + create_singlethread_workqueue(ioc->fault_reset_work_q_name); + if (!ioc->fault_reset_work_q) { + printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n", + ioc->name, __func__, __LINE__); + return; + } + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + if (ioc->fault_reset_work_q) + queue_delayed_work(ioc->fault_reset_work_q, + &ioc->fault_reset_work, + msecs_to_jiffies(FAULT_POLLING_INTERVAL)); + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); +} + +/** + * mpt2sas_base_stop_watchdog - stop the fault_reset_work_q + * @ioc: pointer to scsi command object + * Context: sleep. + * + * Return nothing. + */ +void +mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc) +{ + unsigned long flags; + struct workqueue_struct *wq; + + spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); + wq = ioc->fault_reset_work_q; + ioc->fault_reset_work_q = NULL; + spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + if (wq) { + if (!cancel_delayed_work(&ioc->fault_reset_work)) + flush_workqueue(wq); + destroy_workqueue(wq); + } +} + #ifdef CONFIG_SCSI_MPT2SAS_LOGGING /** * _base_sas_ioc_info - verbose translation of the ioc status @@ -440,6 +498,10 @@ _base_sas_log_info(struct MPT2SAS_ADAPTER *ioc , u32 log_info) if (sas_loginfo.dw.bus_type != 3 /*SAS*/) return; + /* each nexus loss loginfo */ + if (log_info == 0x31170000) + return; + /* eat the loginfos associated with task aborts */ if (ioc->ignore_loginfos && (log_info == 30050000 || log_info == 0x31140000 || log_info == 0x31130000)) @@ -1109,7 +1171,6 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) } } - pci_set_drvdata(pdev, ioc->shost); _base_mask_interrupts(ioc); r = _base_enable_msix(ioc); if (r) @@ -1132,7 +1193,6 @@ mpt2sas_base_map_resources(struct MPT2SAS_ADAPTER *ioc) ioc->pci_irq = -1; pci_release_selected_regions(ioc->pdev, ioc->bars); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return r; } @@ -3191,7 +3251,6 @@ mpt2sas_base_free_resources(struct MPT2SAS_ADAPTER *ioc) ioc->chip_phys = 0; pci_release_selected_regions(ioc->pdev, ioc->bars); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); return; } @@ -3205,7 +3264,6 @@ int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) { int r, i; - unsigned long flags; dinitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, __func__)); @@ -3214,6 +3272,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) if (r) return r; + pci_set_drvdata(ioc->pdev, ioc->shost); r = _base_make_ioc_ready(ioc, CAN_SLEEP, SOFT_RESET); if (r) goto out_free_resources; @@ -3288,23 +3347,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) if (r) goto out_free_resources; - /* initialize fault polling */ - INIT_DELAYED_WORK(&ioc->fault_reset_work, _base_fault_reset_work); - snprintf(ioc->fault_reset_work_q_name, - sizeof(ioc->fault_reset_work_q_name), "poll_%d_status", ioc->id); - ioc->fault_reset_work_q = - create_singlethread_workqueue(ioc->fault_reset_work_q_name); - if (!ioc->fault_reset_work_q) { - printk(MPT2SAS_ERR_FMT "%s: failed (line=%d)\n", - ioc->name, __func__, __LINE__); - goto out_free_resources; - } - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - if (ioc->fault_reset_work_q) - queue_delayed_work(ioc->fault_reset_work_q, - &ioc->fault_reset_work, - msecs_to_jiffies(FAULT_POLLING_INTERVAL)); - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); + mpt2sas_base_start_watchdog(ioc); return 0; out_free_resources: @@ -3312,6 +3355,7 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) ioc->remove_host = 1; mpt2sas_base_free_resources(ioc); _base_release_memory_pools(ioc); + pci_set_drvdata(ioc->pdev, NULL); kfree(ioc->tm_cmds.reply); kfree(ioc->transport_cmds.reply); kfree(ioc->config_cmds.reply); @@ -3337,22 +3381,14 @@ mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc) void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc) { - unsigned long flags; - struct workqueue_struct *wq; dexitprintk(ioc, printk(MPT2SAS_DEBUG_FMT "%s\n", ioc->name, __func__)); - spin_lock_irqsave(&ioc->ioc_reset_in_progress_lock, flags); - wq = ioc->fault_reset_work_q; - ioc->fault_reset_work_q = NULL; - spin_unlock_irqrestore(&ioc->ioc_reset_in_progress_lock, flags); - if (!cancel_delayed_work(&ioc->fault_reset_work)) - flush_workqueue(wq); - destroy_workqueue(wq); - + mpt2sas_base_stop_watchdog(ioc); mpt2sas_base_free_resources(ioc); _base_release_memory_pools(ioc); + pci_set_drvdata(ioc->pdev, NULL); kfree(ioc->pfacts); kfree(ioc->ctl_cmds.reply); kfree(ioc->base_cmds.reply); diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.h b/drivers/scsi/mpt2sas/mpt2sas_base.h index 286c185fa9e..acdcff150a3 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_base.h +++ b/drivers/scsi/mpt2sas/mpt2sas_base.h @@ -69,10 +69,10 @@ #define MPT2SAS_DRIVER_NAME "mpt2sas" #define MPT2SAS_AUTHOR "LSI Corporation <DL-MPTFusionLinux@lsi.com>" #define MPT2SAS_DESCRIPTION "LSI MPT Fusion SAS 2.0 Device Driver" -#define MPT2SAS_DRIVER_VERSION "01.100.03.00" +#define MPT2SAS_DRIVER_VERSION "01.100.04.00" #define MPT2SAS_MAJOR_VERSION 01 #define MPT2SAS_MINOR_VERSION 100 -#define MPT2SAS_BUILD_VERSION 03 +#define MPT2SAS_BUILD_VERSION 04 #define MPT2SAS_RELEASE_VERSION 00 /* @@ -673,6 +673,8 @@ typedef void (*MPT_CALLBACK)(struct MPT2SAS_ADAPTER *ioc, u16 smid, u8 VF_ID, /* base shared API */ extern struct list_head mpt2sas_ioc_list; +void mpt2sas_base_start_watchdog(struct MPT2SAS_ADAPTER *ioc); +void mpt2sas_base_stop_watchdog(struct MPT2SAS_ADAPTER *ioc); int mpt2sas_base_attach(struct MPT2SAS_ADAPTER *ioc); void mpt2sas_base_detach(struct MPT2SAS_ADAPTER *ioc); diff --git a/drivers/scsi/mpt2sas/mpt2sas_config.c b/drivers/scsi/mpt2sas/mpt2sas_config.c index 58cfb97846f..6ddee161beb 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_config.c +++ b/drivers/scsi/mpt2sas/mpt2sas_config.c @@ -236,17 +236,25 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t Mpi2ConfigRequest_t *config_request; int r; u8 retry_count; - u8 issue_reset; + u8 issue_host_reset = 0; u16 wait_state_count; + mutex_lock(&ioc->config_cmds.mutex); if (ioc->config_cmds.status != MPT2_CMD_NOT_USED) { printk(MPT2SAS_ERR_FMT "%s: config_cmd in use\n", ioc->name, __func__); + mutex_unlock(&ioc->config_cmds.mutex); return -EAGAIN; } retry_count = 0; retry_config: + if (retry_count) { + if (retry_count > 2) /* attempt only 2 retries */ + return -EFAULT; + printk(MPT2SAS_INFO_FMT "%s: attempting retry (%d)\n", + ioc->name, __func__, retry_count); + } wait_state_count = 0; ioc_state = mpt2sas_base_get_iocstate(ioc, 1); while (ioc_state != MPI2_IOC_STATE_OPERATIONAL) { @@ -254,8 +262,8 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t printk(MPT2SAS_ERR_FMT "%s: failed due to ioc not operational\n", ioc->name, __func__); - ioc->config_cmds.status = MPT2_CMD_NOT_USED; - return -EFAULT; + r = -EFAULT; + goto out; } ssleep(1); ioc_state = mpt2sas_base_get_iocstate(ioc, 1); @@ -271,8 +279,8 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t if (!smid) { printk(MPT2SAS_ERR_FMT "%s: failed obtaining a smid\n", ioc->name, __func__); - ioc->config_cmds.status = MPT2_CMD_NOT_USED; - return -EAGAIN; + r = -EAGAIN; + goto out; } r = 0; @@ -292,9 +300,15 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t ioc->name, __func__); _debug_dump_mf(mpi_request, sizeof(Mpi2ConfigRequest_t)/4); - if (!(ioc->config_cmds.status & MPT2_CMD_RESET)) - issue_reset = 1; - goto issue_host_reset; + retry_count++; + if (ioc->config_cmds.smid == smid) + mpt2sas_base_free_smid(ioc, smid); + if ((ioc->shost_recovery) || + (ioc->config_cmds.status & MPT2_CMD_RESET)) + goto retry_config; + issue_host_reset = 1; + r = -EFAULT; + goto out; } if (ioc->config_cmds.status & MPT2_CMD_REPLY_VALID) memcpy(mpi_reply, ioc->config_cmds.reply, @@ -302,21 +316,13 @@ _config_request(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigRequest_t if (retry_count) printk(MPT2SAS_INFO_FMT "%s: retry completed!!\n", ioc->name, __func__); +out: ioc->config_cmds.status = MPT2_CMD_NOT_USED; - return r; - - issue_host_reset: - if (issue_reset) + mutex_unlock(&ioc->config_cmds.mutex); + if (issue_host_reset) mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, FORCE_BIG_HAMMER); - ioc->config_cmds.status = MPT2_CMD_NOT_USED; - if (!retry_count) { - printk(MPT2SAS_INFO_FMT "%s: attempting retry\n", - ioc->name, __func__); - retry_count++; - goto retry_config; - } - return -EFAULT; + return r; } /** @@ -375,7 +381,6 @@ mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2ManufacturingPage0_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -417,7 +422,6 @@ mpt2sas_config_get_manufacturing_pg0(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -438,7 +442,6 @@ mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2BiosPage2_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -480,7 +483,6 @@ mpt2sas_config_get_bios_pg2(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -501,7 +503,6 @@ mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2BiosPage3_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -543,7 +544,6 @@ mpt2sas_config_get_bios_pg3(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -564,7 +564,6 @@ mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2IOUnitPage0_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -606,7 +605,6 @@ mpt2sas_config_get_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -627,7 +625,6 @@ mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2IOUnitPage1_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -669,7 +666,6 @@ mpt2sas_config_get_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -690,7 +686,6 @@ mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; @@ -732,7 +727,6 @@ mpt2sas_config_set_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -753,7 +747,6 @@ mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2IOCPage8_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -795,7 +788,6 @@ mpt2sas_config_get_ioc_pg8(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -818,7 +810,6 @@ mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2SasDevicePage0_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -863,7 +854,6 @@ mpt2sas_config_get_sas_device_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -886,7 +876,6 @@ mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2SasDevicePage1_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -931,7 +920,6 @@ mpt2sas_config_get_sas_device_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -953,7 +941,6 @@ mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys) Mpi2ConfigReply_t mpi_reply; Mpi2SasIOUnitPage0_t config_page; - mutex_lock(&ioc->config_cmds.mutex); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; mpi_request.Action = MPI2_CONFIG_ACTION_PAGE_HEADER; @@ -1002,7 +989,6 @@ mpt2sas_config_get_number_hba_phys(struct MPT2SAS_ADAPTER *ioc, u8 *num_phys) _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1026,8 +1012,6 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t Mpi2ConfigRequest_t mpi_request; int r; struct config_request mem; - - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sz); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1070,7 +1054,6 @@ mpt2sas_config_get_sas_iounit_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1095,7 +1078,6 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sz); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1138,7 +1120,6 @@ mpt2sas_config_get_sas_iounit_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1161,7 +1142,6 @@ mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2ExpanderPage0_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1206,7 +1186,6 @@ mpt2sas_config_get_expander_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1230,7 +1209,6 @@ mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2ExpanderPage1_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1277,7 +1255,6 @@ mpt2sas_config_get_expander_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1300,7 +1277,6 @@ mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2SasEnclosurePage0_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1345,7 +1321,6 @@ mpt2sas_config_get_enclosure_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1367,7 +1342,6 @@ mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2SasPhyPage0_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1413,7 +1387,6 @@ mpt2sas_config_get_phy_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1435,7 +1408,6 @@ mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2SasPhyPage1_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1481,7 +1453,6 @@ mpt2sas_config_get_phy_pg1(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1505,7 +1476,6 @@ mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(config_page, 0, sizeof(Mpi2RaidVolPage1_t)); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1548,7 +1518,6 @@ mpt2sas_config_get_raid_volume_pg1(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1572,7 +1541,6 @@ mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, struct config_request mem; u16 ioc_status; - mutex_lock(&ioc->config_cmds.mutex); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); *num_pds = 0; mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1620,7 +1588,6 @@ mpt2sas_config_get_number_pds(struct MPT2SAS_ADAPTER *ioc, u16 handle, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1645,7 +1612,6 @@ mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); memset(config_page, 0, sz); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1687,7 +1653,6 @@ mpt2sas_config_get_raid_volume_pg0(struct MPT2SAS_ADAPTER *ioc, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1711,7 +1676,6 @@ mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t int r; struct config_request mem; - mutex_lock(&ioc->config_cmds.mutex); memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); memset(config_page, 0, sizeof(Mpi2RaidPhysDiskPage0_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1754,7 +1718,6 @@ mpt2sas_config_get_phys_disk_pg0(struct MPT2SAS_ADAPTER *ioc, Mpi2ConfigReply_t _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } @@ -1778,7 +1741,6 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, struct config_request mem; u16 ioc_status; - mutex_lock(&ioc->config_cmds.mutex); *volume_handle = 0; memset(&mpi_request, 0, sizeof(Mpi2ConfigRequest_t)); mpi_request.Function = MPI2_FUNCTION_CONFIG; @@ -1842,7 +1804,6 @@ mpt2sas_config_get_volume_handle(struct MPT2SAS_ADAPTER *ioc, u16 pd_handle, _config_free_config_dma_memory(ioc, &mem); out: - mutex_unlock(&ioc->config_cmds.mutex); return r; } diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c index 2a01a5f2a84..2e9a4445596 100644 --- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c +++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c @@ -2767,6 +2767,10 @@ _scsih_scsi_ioc_info(struct MPT2SAS_ADAPTER *ioc, struct scsi_cmnd *scmd, char *desc_ioc_state = NULL; char *desc_scsi_status = NULL; char *desc_scsi_state = ioc->tmp_string; + u32 log_info = le32_to_cpu(mpi_reply->IOCLogInfo); + + if (log_info == 0x31170000) + return; switch (ioc_status) { case MPI2_IOCSTATUS_SUCCESS: @@ -3426,7 +3430,7 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) __le64 sas_address; int i; unsigned long flags; - struct _sas_port *mpt2sas_port; + struct _sas_port *mpt2sas_port = NULL; int rc = 0; if (!handle) @@ -3518,12 +3522,20 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) &expander_pg1, i, handle))) { printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", ioc->name, __FILE__, __LINE__, __func__); - continue; + rc = -1; + goto out_fail; } sas_expander->phy[i].handle = handle; sas_expander->phy[i].phy_id = i; - mpt2sas_transport_add_expander_phy(ioc, &sas_expander->phy[i], - expander_pg1, sas_expander->parent_dev); + + if ((mpt2sas_transport_add_expander_phy(ioc, + &sas_expander->phy[i], expander_pg1, + sas_expander->parent_dev))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + rc = -1; + goto out_fail; + } } if (sas_expander->enclosure_handle) { @@ -3540,8 +3552,9 @@ _scsih_expander_add(struct MPT2SAS_ADAPTER *ioc, u16 handle) out_fail: - if (sas_expander) - kfree(sas_expander->phy); + if (mpt2sas_port) + mpt2sas_transport_port_remove(ioc, sas_expander->sas_address, + sas_expander->parent_handle); kfree(sas_expander); return rc; } @@ -3663,12 +3676,11 @@ _scsih_add_device(struct MPT2SAS_ADAPTER *ioc, u16 handle, u8 phy_num, u8 is_pd) sas_device->hidden_raid_component = is_pd; /* get enclosure_logical_id */ - if (!(mpt2sas_config_get_enclosure_pg0(ioc, &mpi_reply, &enclosure_pg0, - MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, - sas_device->enclosure_handle))) { + if (sas_device->enclosure_handle && !(mpt2sas_config_get_enclosure_pg0( + ioc, &mpi_reply, &enclosure_pg0, MPI2_SAS_ENCLOS_PGAD_FORM_HANDLE, + sas_device->enclosure_handle))) sas_device->enclosure_logical_id = le64_to_cpu(enclosure_pg0.EnclosureLogicalID); - } /* get device name */ sas_device->device_name = le64_to_cpu(sas_device_pg0.DeviceName); @@ -4250,12 +4262,6 @@ _scsih_sas_volume_add(struct MPT2SAS_ADAPTER *ioc, u16 handle = le16_to_cpu(element->VolDevHandle); int rc; -#if 0 /* RAID_HACKS */ - if (le32_to_cpu(event_data->Flags) & - MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) - return; -#endif - mpt2sas_config_get_volume_wwid(ioc, handle, &wwid); if (!wwid) { printk(MPT2SAS_ERR_FMT @@ -4310,12 +4316,6 @@ _scsih_sas_volume_delete(struct MPT2SAS_ADAPTER *ioc, unsigned long flags; struct MPT2SAS_TARGET *sas_target_priv_data; -#if 0 /* RAID_HACKS */ - if (le32_to_cpu(event_data->Flags) & - MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) - return; -#endif - spin_lock_irqsave(&ioc->raid_device_lock, flags); raid_device = _scsih_raid_device_find_by_handle(ioc, handle); spin_unlock_irqrestore(&ioc->raid_device_lock, flags); @@ -4428,14 +4428,38 @@ _scsih_sas_pd_add(struct MPT2SAS_ADAPTER *ioc, struct _sas_device *sas_device; unsigned long flags; u16 handle = le16_to_cpu(element->PhysDiskDevHandle); + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + u32 ioc_status; spin_lock_irqsave(&ioc->sas_device_lock, flags); sas_device = _scsih_sas_device_find_by_handle(ioc, handle); spin_unlock_irqrestore(&ioc->sas_device_lock, flags); - if (sas_device) + if (sas_device) { sas_device->hidden_raid_component = 1; - else - _scsih_add_device(ioc, handle, 0, 1); + return; + } + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, &sas_device_pg0, + MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + _scsih_link_change(ioc, + le16_to_cpu(sas_device_pg0.ParentDevHandle), + handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + + _scsih_add_device(ioc, handle, 0, 1); } #ifdef CONFIG_SCSI_MPT2SAS_LOGGING @@ -4535,12 +4559,15 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, { Mpi2EventIrConfigElement_t *element; int i; + u8 foreign_config; #ifdef CONFIG_SCSI_MPT2SAS_LOGGING if (ioc->logging_level & MPT_DEBUG_EVENT_WORK_TASK) _scsih_sas_ir_config_change_event_debug(ioc, event_data); #endif + foreign_config = (le32_to_cpu(event_data->Flags) & + MPI2_EVENT_IR_CHANGE_FLAGS_FOREIGN_CONFIG) ? 1 : 0; element = (Mpi2EventIrConfigElement_t *)&event_data->ConfigElement[0]; for (i = 0; i < event_data->NumElements; i++, element++) { @@ -4548,11 +4575,13 @@ _scsih_sas_ir_config_change_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, switch (element->ReasonCode) { case MPI2_EVENT_IR_CHANGE_RC_VOLUME_CREATED: case MPI2_EVENT_IR_CHANGE_RC_ADDED: - _scsih_sas_volume_add(ioc, element); + if (!foreign_config) + _scsih_sas_volume_add(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_VOLUME_DELETED: case MPI2_EVENT_IR_CHANGE_RC_REMOVED: - _scsih_sas_volume_delete(ioc, element); + if (!foreign_config) + _scsih_sas_volume_delete(ioc, element); break; case MPI2_EVENT_IR_CHANGE_RC_PD_CREATED: _scsih_sas_pd_hide(ioc, element); @@ -4671,6 +4700,9 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, u32 state; struct _sas_device *sas_device; unsigned long flags; + Mpi2ConfigReply_t mpi_reply; + Mpi2SasDevicePage0_t sas_device_pg0; + u32 ioc_status; if (event_data->ReasonCode != MPI2_EVENT_IR_PHYSDISK_RC_STATE_CHANGED) return; @@ -4687,22 +4719,40 @@ _scsih_sas_ir_physical_disk_event(struct MPT2SAS_ADAPTER *ioc, u8 VF_ID, spin_unlock_irqrestore(&ioc->sas_device_lock, flags); switch (state) { -#if 0 - case MPI2_RAID_PD_STATE_OFFLINE: - if (sas_device) - _scsih_remove_device(ioc, handle); - break; -#endif case MPI2_RAID_PD_STATE_ONLINE: case MPI2_RAID_PD_STATE_DEGRADED: case MPI2_RAID_PD_STATE_REBUILDING: case MPI2_RAID_PD_STATE_OPTIMAL: - if (sas_device) + if (sas_device) { sas_device->hidden_raid_component = 1; - else - _scsih_add_device(ioc, handle, 0, 1); + return; + } + + if ((mpt2sas_config_get_sas_device_pg0(ioc, &mpi_reply, + &sas_device_pg0, MPI2_SAS_DEVICE_PGAD_FORM_HANDLE, + handle))) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + ioc_status = le16_to_cpu(mpi_reply.IOCStatus) & + MPI2_IOCSTATUS_MASK; + if (ioc_status != MPI2_IOCSTATUS_SUCCESS) { + printk(MPT2SAS_ERR_FMT "failure at %s:%d/%s()!\n", + ioc->name, __FILE__, __LINE__, __func__); + return; + } + + _scsih_link_change(ioc, + le16_to_cpu(sas_device_pg0.ParentDevHandle), + handle, sas_device_pg0.PhyNum, MPI2_SAS_NEG_LINK_RATE_1_5); + + _scsih_add_device(ioc, handle, 0, 1); + break; + case MPI2_RAID_PD_STATE_OFFLINE: case MPI2_RAID_PD_STATE_NOT_CONFIGURED: case MPI2_RAID_PD_STATE_NOT_COMPATIBLE: case MPI2_RAID_PD_STATE_HOT_SPARE: @@ -5774,6 +5824,7 @@ _scsih_suspend(struct pci_dev *pdev, pm_message_t state) struct MPT2SAS_ADAPTER *ioc = shost_priv(shost); u32 device_state; + mpt2sas_base_stop_watchdog(ioc); flush_scheduled_work(); scsi_block_requests(shost); device_state = pci_choose_state(pdev, state); @@ -5816,6 +5867,7 @@ _scsih_resume(struct pci_dev *pdev) mpt2sas_base_hard_reset_handler(ioc, CAN_SLEEP, SOFT_RESET); scsi_unblock_requests(shost); + mpt2sas_base_start_watchdog(ioc); return 0; } #endif /* CONFIG_PM */ diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 037c1e0b7c4..6553833c12d 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -527,7 +527,7 @@ config SERIAL_S3C24A0 config SERIAL_S3C6400 tristate "Samsung S3C6400/S3C6410 Serial port support" - depends on SERIAL_SAMSUNG && (CPU_S3C600 || CPU_S3C6410) + depends on SERIAL_SAMSUNG && (CPU_S3C6400 || CPU_S3C6410) default y help Serial port support for the Samsung S3C6400 and S3C6410 diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 4cbb87ad070..32dc2fc50e6 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -272,7 +272,8 @@ static inline void sci_init_pins(struct uart_port *port, unsigned int cflag) __raw_writew(data, PSCR); } } -#elif defined(CONFIG_CPU_SUBTYPE_SH7763) || \ +#elif defined(CONFIG_CPU_SUBTYPE_SH7757) || \ + defined(CONFIG_CPU_SUBTYPE_SH7763) || \ defined(CONFIG_CPU_SUBTYPE_SH7780) || \ defined(CONFIG_CPU_SUBTYPE_SH7785) || \ defined(CONFIG_CPU_SUBTYPE_SH7786) || \ diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 38072c15b84..3e2fcf93b42 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -112,6 +112,13 @@ #elif defined(CONFIG_H8S2678) # define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */ # define H8300_SCI_DR(ch) *(volatile char *)(P1DR + h8300_sci_pins[ch].port) +#elif defined(CONFIG_CPU_SUBTYPE_SH7757) +# define SCSPTR0 0xfe4b0020 +# define SCSPTR1 0xfe4b0020 +# define SCSPTR2 0xfe4b0020 +# define SCIF_ORER 0x0001 +# define SCSCR_INIT(port) 0x38 +# define SCIF_ONLY #elif defined(CONFIG_CPU_SUBTYPE_SH7763) # define SCSPTR0 0xffe00024 /* 16 bit SCIF */ # define SCSPTR1 0xffe08024 /* 16 bit SCIF */ @@ -562,6 +569,16 @@ static inline int sci_rxd_in(struct uart_port *port) return ctrl_inw(SCSPTR2)&0x0001 ? 1 : 0; /* SCIF */ return 1; } +#elif defined(CONFIG_CPU_SUBTYPE_SH7757) +static inline int sci_rxd_in(struct uart_port *port) +{ + if (port->mapbase == 0xfe4b0000) + return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; + if (port->mapbase == 0xfe4c0000) + return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; + if (port->mapbase == 0xfe4d0000) + return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; +} #elif defined(CONFIG_CPU_SUBTYPE_SH7760) static inline int sci_rxd_in(struct uart_port *port) { diff --git a/drivers/sh/intc.c b/drivers/sh/intc.c index 3dd231a643b..559b5fe9dc0 100644 --- a/drivers/sh/intc.c +++ b/drivers/sh/intc.c @@ -77,7 +77,7 @@ static unsigned long ack_handle[NR_IRQS]; static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { struct irq_chip *chip = get_irq_chip(irq); - return (void *)((char *)chip - offsetof(struct intc_desc_int, chip)); + return container_of(chip, struct intc_desc_int, chip); } static inline unsigned int set_field(unsigned int value, @@ -95,16 +95,19 @@ static inline unsigned int set_field(unsigned int value, static void write_8(unsigned long addr, unsigned long h, unsigned long data) { __raw_writeb(set_field(0, data, h), addr); + (void)__raw_readb(addr); /* Defeat write posting */ } static void write_16(unsigned long addr, unsigned long h, unsigned long data) { __raw_writew(set_field(0, data, h), addr); + (void)__raw_readw(addr); /* Defeat write posting */ } static void write_32(unsigned long addr, unsigned long h, unsigned long data) { __raw_writel(set_field(0, data, h), addr); + (void)__raw_readl(addr); /* Defeat write posting */ } static void modify_8(unsigned long addr, unsigned long h, unsigned long data) @@ -112,6 +115,7 @@ static void modify_8(unsigned long addr, unsigned long h, unsigned long data) unsigned long flags; local_irq_save(flags); __raw_writeb(set_field(__raw_readb(addr), data, h), addr); + (void)__raw_readb(addr); /* Defeat write posting */ local_irq_restore(flags); } @@ -120,6 +124,7 @@ static void modify_16(unsigned long addr, unsigned long h, unsigned long data) unsigned long flags; local_irq_save(flags); __raw_writew(set_field(__raw_readw(addr), data, h), addr); + (void)__raw_readw(addr); /* Defeat write posting */ local_irq_restore(flags); } @@ -128,6 +133,7 @@ static void modify_32(unsigned long addr, unsigned long h, unsigned long data) unsigned long flags; local_irq_save(flags); __raw_writel(set_field(__raw_readl(addr), data, h), addr); + (void)__raw_readl(addr); /* Defeat write posting */ local_irq_restore(flags); } @@ -657,16 +663,9 @@ static unsigned int __init save_reg(struct intc_desc_int *d, return 0; } -static unsigned char *intc_evt2irq_table; - -unsigned int intc_evt2irq(unsigned int vector) +static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) { - unsigned int irq = evt2irq(vector); - - if (intc_evt2irq_table && intc_evt2irq_table[irq]) - irq = intc_evt2irq_table[irq]; - - return irq; + generic_handle_irq((unsigned int)get_irq_data(irq)); } void __init register_intc_controller(struct intc_desc *desc) @@ -739,50 +738,48 @@ void __init register_intc_controller(struct intc_desc *desc) BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ - /* keep the first vector only if same enum is used multiple times */ + /* register the vectors one by one */ for (i = 0; i < desc->nr_vectors; i++) { struct intc_vect *vect = desc->vectors + i; - int first_irq = evt2irq(vect->vect); + unsigned int irq = evt2irq(vect->vect); + struct irq_desc *irq_desc; if (!vect->enum_id) continue; + irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); + if (unlikely(!irq_desc)) { + pr_info("can't get irq_desc for %d\n", irq); + continue; + } + + intc_register_irq(desc, d, vect->enum_id, irq); + for (k = i + 1; k < desc->nr_vectors; k++) { struct intc_vect *vect2 = desc->vectors + k; + unsigned int irq2 = evt2irq(vect2->vect); if (vect->enum_id != vect2->enum_id) continue; - vect2->enum_id = 0; - - if (!intc_evt2irq_table) - intc_evt2irq_table = kzalloc(NR_IRQS, GFP_NOWAIT); - - if (!intc_evt2irq_table) { - pr_warning("intc: cannot allocate evt2irq!\n"); + /* + * In the case of multi-evt handling and sparse + * IRQ support, each vector still needs to have + * its own backing irq_desc. + */ + irq_desc = irq_to_desc_alloc_node(irq2, numa_node_id()); + if (unlikely(!irq_desc)) { + pr_info("can't get irq_desc for %d\n", irq2); continue; } - intc_evt2irq_table[evt2irq(vect2->vect)] = first_irq; - } - } - - /* register the vectors one by one */ - for (i = 0; i < desc->nr_vectors; i++) { - struct intc_vect *vect = desc->vectors + i; - unsigned int irq = evt2irq(vect->vect); - struct irq_desc *irq_desc; - - if (!vect->enum_id) - continue; + vect2->enum_id = 0; - irq_desc = irq_to_desc_alloc_node(irq, numa_node_id()); - if (unlikely(!irq_desc)) { - printk(KERN_INFO "can not get irq_desc for %d\n", irq); - continue; + /* redirect this interrupts to the first one */ + set_irq_chip_and_handler_name(irq2, &d->chip, + intc_redirect_irq, "redirect"); + set_irq_data(irq2, (void *)irq); } - - intc_register_irq(desc, d, vect->enum_id, irq); } } diff --git a/drivers/spi/spi_s3c24xx.c b/drivers/spi/spi_s3c24xx.c index e0d44af4745..3f3119d760d 100644 --- a/drivers/spi/spi_s3c24xx.c +++ b/drivers/spi/spi_s3c24xx.c @@ -111,29 +111,32 @@ static int s3c24xx_spi_setupxfer(struct spi_device *spi, unsigned int bpw; unsigned int hz; unsigned int div; + unsigned long clk; bpw = t ? t->bits_per_word : spi->bits_per_word; hz = t ? t->speed_hz : spi->max_speed_hz; + if (!bpw) + bpw = 8; + + if (!hz) + hz = spi->max_speed_hz; + if (bpw != 8) { dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); return -EINVAL; } - div = clk_get_rate(hw->clk) / hz; - - /* is clk = pclk / (2 * (pre+1)), or is it - * clk = (pclk * 2) / ( pre + 1) */ - - div /= 2; - - if (div > 0) - div -= 1; + clk = clk_get_rate(hw->clk); + div = DIV_ROUND_UP(clk, hz * 2) - 1; if (div > 255) div = 255; - dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", div, hz); + dev_dbg(&spi->dev, "setting pre-scaler to %d (wanted %d, got %ld)\n", + div, hz, clk / (2 * (div + 1))); + + writeb(div, hw->regs + S3C2410_SPPRE); spin_lock(&hw->bitbang.lock); diff --git a/drivers/uio/uio_pdrv_genirq.c b/drivers/uio/uio_pdrv_genirq.c index 3f06818cf9f..02347c57357 100644 --- a/drivers/uio/uio_pdrv_genirq.c +++ b/drivers/uio/uio_pdrv_genirq.c @@ -20,6 +20,7 @@ #include <linux/bitops.h> #include <linux/interrupt.h> #include <linux/stringify.h> +#include <linux/pm_runtime.h> #define DRIVER_NAME "uio_pdrv_genirq" @@ -27,8 +28,27 @@ struct uio_pdrv_genirq_platdata { struct uio_info *uioinfo; spinlock_t lock; unsigned long flags; + struct platform_device *pdev; }; +static int uio_pdrv_genirq_open(struct uio_info *info, struct inode *inode) +{ + struct uio_pdrv_genirq_platdata *priv = info->priv; + + /* Wait until the Runtime PM code has woken up the device */ + pm_runtime_get_sync(&priv->pdev->dev); + return 0; +} + +static int uio_pdrv_genirq_release(struct uio_info *info, struct inode *inode) +{ + struct uio_pdrv_genirq_platdata *priv = info->priv; + + /* Tell the Runtime PM code that the device has become idle */ + pm_runtime_put_sync(&priv->pdev->dev); + return 0; +} + static irqreturn_t uio_pdrv_genirq_handler(int irq, struct uio_info *dev_info) { struct uio_pdrv_genirq_platdata *priv = dev_info->priv; @@ -97,6 +117,7 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) priv->uioinfo = uioinfo; spin_lock_init(&priv->lock); priv->flags = 0; /* interrupt is enabled to begin with */ + priv->pdev = pdev; uiomem = &uioinfo->mem[0]; @@ -136,8 +157,17 @@ static int uio_pdrv_genirq_probe(struct platform_device *pdev) uioinfo->irq_flags |= IRQF_DISABLED; uioinfo->handler = uio_pdrv_genirq_handler; uioinfo->irqcontrol = uio_pdrv_genirq_irqcontrol; + uioinfo->open = uio_pdrv_genirq_open; + uioinfo->release = uio_pdrv_genirq_release; uioinfo->priv = priv; + /* Enable Runtime PM for this device: + * The device starts in suspended state to allow the hardware to be + * turned off by default. The Runtime PM bus code should power on the + * hardware and enable clocks at open(). + */ + pm_runtime_enable(&pdev->dev); + ret = uio_register_device(&pdev->dev, priv->uioinfo); if (ret) { dev_err(&pdev->dev, "unable to register uio device\n"); @@ -157,16 +187,40 @@ static int uio_pdrv_genirq_remove(struct platform_device *pdev) struct uio_pdrv_genirq_platdata *priv = platform_get_drvdata(pdev); uio_unregister_device(priv->uioinfo); + pm_runtime_disable(&pdev->dev); kfree(priv); return 0; } +static int uio_pdrv_genirq_runtime_nop(struct device *dev) +{ + /* Runtime PM callback shared between ->runtime_suspend() + * and ->runtime_resume(). Simply returns success. + * + * In this driver pm_runtime_get_sync() and pm_runtime_put_sync() + * are used at open() and release() time. This allows the + * Runtime PM code to turn off power to the device while the + * device is unused, ie before open() and after release(). + * + * This Runtime PM callback does not need to save or restore + * any registers since user space is responsbile for hardware + * register reinitialization after open(). + */ + return 0; +} + +static struct dev_pm_ops uio_pdrv_genirq_dev_pm_ops = { + .runtime_suspend = uio_pdrv_genirq_runtime_nop, + .runtime_resume = uio_pdrv_genirq_runtime_nop, +}; + static struct platform_driver uio_pdrv_genirq = { .probe = uio_pdrv_genirq_probe, .remove = uio_pdrv_genirq_remove, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, + .pm = &uio_pdrv_genirq_dev_pm_ops, }, }; diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index b7f10bc25c2..9f986b417c5 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -251,6 +251,24 @@ config USB_PXA25X_SMALL default y if USB_ETH default y if USB_G_SERIAL +config USB_GADGET_R8A66597 + boolean "Renesas R8A66597 USB Peripheral Controller" + select USB_GADGET_DUALSPEED + help + R8A66597 is a discrete USB host and peripheral controller chip that + supports both full and high speed USB 2.0 data transfers. + It has nine configurable endpoints, and endpoint zero. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "r8a66597_udc" and force all + gadget drivers to also be dynamically linked. + +config USB_R8A66597 + tristate + depends on USB_GADGET_R8A66597 + default USB_GADGET + select USB_GADGET_SELECTED + config USB_GADGET_PXA27X boolean "PXA 27x" depends on ARCH_PXA && (PXA27x || PXA3xx) diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index e6017e6bf6d..9d7b87c52e9 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -23,6 +23,7 @@ ifeq ($(CONFIG_ARCH_MXC),y) fsl_usb2_udc-objs += fsl_mx3_udc.o endif obj-$(CONFIG_USB_M66592) += m66592-udc.o +obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 8e0e9a0b736..f2d270b202f 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -173,6 +173,12 @@ // CONFIG_USB_GADGET_AU1X00 // ... +#ifdef CONFIG_USB_GADGET_R8A66597 +#define gadget_is_r8a66597(g) !strcmp("r8a66597_udc", (g)->name) +#else +#define gadget_is_r8a66597(g) 0 +#endif + /** * usb_gadget_controller_number - support bcdDevice id convention @@ -239,6 +245,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x23; else if (gadget_is_langwell(gadget)) return 0x24; + else if (gadget_is_r8a66597(gadget)) + return 0x25; return -ENOENT; } diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c index a61c70caff1..a8c8543d1b0 100644 --- a/drivers/usb/gadget/m66592-udc.c +++ b/drivers/usb/gadget/m66592-udc.c @@ -25,7 +25,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/platform_device.h> - +#include <linux/err.h> #include <linux/usb/ch9.h> #include <linux/usb/gadget.h> diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c new file mode 100644 index 00000000000..e220fb8091a --- /dev/null +++ b/drivers/usb/gadget/r8a66597-udc.c @@ -0,0 +1,1689 @@ +/* + * R8A66597 UDC (USB gadget) + * + * Copyright (C) 2006-2009 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/clk.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include "r8a66597-udc.h" + +#define DRIVER_VERSION "2009-08-18" + +static const char udc_name[] = "r8a66597_udc"; +static const char *r8a66597_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7", + "ep8", "ep9", +}; + +static void disable_controller(struct r8a66597 *r8a66597); +static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req); +static void irq_packet_write(struct r8a66597_ep *ep, + struct r8a66597_request *req); +static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags); + +static void transfer_complete(struct r8a66597_ep *ep, + struct r8a66597_request *req, int status); + +/*-------------------------------------------------------------------------*/ +static inline u16 get_usb_speed(struct r8a66597 *r8a66597) +{ + return r8a66597_read(r8a66597, DVSTCTR0) & RHST; +} + +static void enable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, + INTENB0); + r8a66597_bset(r8a66597, (1 << pipenum), reg); + r8a66597_write(r8a66597, tmp, INTENB0); +} + +static void disable_pipe_irq(struct r8a66597 *r8a66597, u16 pipenum, + unsigned long reg) +{ + u16 tmp; + + tmp = r8a66597_read(r8a66597, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | NRDYE | BRDYE, + INTENB0); + r8a66597_bclr(r8a66597, (1 << pipenum), reg); + r8a66597_write(r8a66597, tmp, INTENB0); +} + +static void r8a66597_usb_connect(struct r8a66597 *r8a66597) +{ + r8a66597_bset(r8a66597, CTRE, INTENB0); + r8a66597_bset(r8a66597, BEMPE | BRDYE, INTENB0); + + r8a66597_bset(r8a66597, DPRPU, SYSCFG0); +} + +static void r8a66597_usb_disconnect(struct r8a66597 *r8a66597) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + r8a66597_bclr(r8a66597, CTRE, INTENB0); + r8a66597_bclr(r8a66597, BEMPE | BRDYE, INTENB0); + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + + r8a66597->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock(&r8a66597->lock); + r8a66597->driver->disconnect(&r8a66597->gadget); + spin_lock(&r8a66597->lock); + + disable_controller(r8a66597); + INIT_LIST_HEAD(&r8a66597->ep[0].queue); +} + +static inline u16 control_reg_get_pid(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 pid = 0; + unsigned long offset; + + if (pipenum == 0) + pid = r8a66597_read(r8a66597, DCPCTR) & PID; + else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + pid = r8a66597_read(r8a66597, offset) & PID; + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + + return pid; +} + +static inline void control_reg_set_pid(struct r8a66597 *r8a66597, u16 pipenum, + u16 pid) +{ + unsigned long offset; + + if (pipenum == 0) + r8a66597_mdfy(r8a66597, pid, PID, DCPCTR); + else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_mdfy(r8a66597, pid, PID, offset); + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); +} + +static inline void pipe_start(struct r8a66597 *r8a66597, u16 pipenum) +{ + control_reg_set_pid(r8a66597, pipenum, PID_BUF); +} + +static inline void pipe_stop(struct r8a66597 *r8a66597, u16 pipenum) +{ + control_reg_set_pid(r8a66597, pipenum, PID_NAK); +} + +static inline void pipe_stall(struct r8a66597 *r8a66597, u16 pipenum) +{ + control_reg_set_pid(r8a66597, pipenum, PID_STALL); +} + +static inline u16 control_reg_get(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 ret = 0; + unsigned long offset; + + if (pipenum == 0) + ret = r8a66597_read(r8a66597, DCPCTR); + else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + ret = r8a66597_read(r8a66597, offset); + } else + printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum); + + return ret; +} + +static inline void control_reg_sqclr(struct r8a66597 *r8a66597, u16 pipenum) +{ + unsigned long offset; + + pipe_stop(r8a66597, pipenum); + + if (pipenum == 0) + r8a66597_bset(r8a66597, SQCLR, DCPCTR); + else if (pipenum < R8A66597_MAX_NUM_PIPE) { + offset = get_pipectr_addr(pipenum); + r8a66597_bset(r8a66597, SQCLR, offset); + } else + printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum); +} + +static inline int get_buffer_size(struct r8a66597 *r8a66597, u16 pipenum) +{ + u16 tmp; + int size; + + if (pipenum == 0) { + tmp = r8a66597_read(r8a66597, DCPCFG); + if ((tmp & R8A66597_CNTMD) != 0) + size = 256; + else { + tmp = r8a66597_read(r8a66597, DCPMAXP); + size = tmp & MAXP; + } + } else { + r8a66597_write(r8a66597, pipenum, PIPESEL); + tmp = r8a66597_read(r8a66597, PIPECFG); + if ((tmp & R8A66597_CNTMD) != 0) { + tmp = r8a66597_read(r8a66597, PIPEBUF); + size = ((tmp >> 10) + 1) * 64; + } else { + tmp = r8a66597_read(r8a66597, PIPEMAXP); + size = tmp & MXPS; + } + } + + return size; +} + +static inline unsigned short mbw_value(struct r8a66597 *r8a66597) +{ + if (r8a66597->pdata->on_chip) + return MBW_32; + else + return MBW_16; +} + +static inline void pipe_change(struct r8a66597 *r8a66597, u16 pipenum) +{ + struct r8a66597_ep *ep = r8a66597->pipenum2ep[pipenum]; + + if (ep->use_dma) + return; + + r8a66597_mdfy(r8a66597, pipenum, CURPIPE, ep->fifosel); + + ndelay(450); + + r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); +} + +static int pipe_buffer_setting(struct r8a66597 *r8a66597, + struct r8a66597_pipe_info *info) +{ + u16 bufnum = 0, buf_bsize = 0; + u16 pipecfg = 0; + + if (info->pipe == 0) + return -EINVAL; + + r8a66597_write(r8a66597, info->pipe, PIPESEL); + + if (info->dir_in) + pipecfg |= R8A66597_DIR; + pipecfg |= info->type; + pipecfg |= info->epnum; + switch (info->type) { + case R8A66597_INT: + bufnum = 4 + (info->pipe - R8A66597_BASE_PIPENUM_INT); + buf_bsize = 0; + break; + case R8A66597_BULK: + /* isochronous pipes may be used as bulk pipes */ + if (info->pipe > R8A66597_BASE_PIPENUM_BULK) + bufnum = info->pipe - R8A66597_BASE_PIPENUM_BULK; + else + bufnum = info->pipe - R8A66597_BASE_PIPENUM_ISOC; + + bufnum = R8A66597_BASE_BUFNUM + (bufnum * 16); + buf_bsize = 7; + pipecfg |= R8A66597_DBLB; + if (!info->dir_in) + pipecfg |= R8A66597_SHTNAK; + break; + case R8A66597_ISO: + bufnum = R8A66597_BASE_BUFNUM + + (info->pipe - R8A66597_BASE_PIPENUM_ISOC) * 16; + buf_bsize = 7; + break; + } + + if (buf_bsize && ((bufnum + 16) >= R8A66597_MAX_BUFNUM)) { + pr_err(KERN_ERR "r8a66597 pipe memory is insufficient\n"); + return -ENOMEM; + } + + r8a66597_write(r8a66597, pipecfg, PIPECFG); + r8a66597_write(r8a66597, (buf_bsize << 10) | (bufnum), PIPEBUF); + r8a66597_write(r8a66597, info->maxpacket, PIPEMAXP); + if (info->interval) + info->interval--; + r8a66597_write(r8a66597, info->interval, PIPEPERI); + + return 0; +} + +static void pipe_buffer_release(struct r8a66597 *r8a66597, + struct r8a66597_pipe_info *info) +{ + if (info->pipe == 0) + return; + + if (is_bulk_pipe(info->pipe)) + r8a66597->bulk--; + else if (is_interrupt_pipe(info->pipe)) + r8a66597->interrupt--; + else if (is_isoc_pipe(info->pipe)) { + r8a66597->isochronous--; + if (info->type == R8A66597_BULK) + r8a66597->bulk--; + } else + printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n", + info->pipe); +} + +static void pipe_initialize(struct r8a66597_ep *ep) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + + r8a66597_mdfy(r8a66597, 0, CURPIPE, ep->fifosel); + + r8a66597_write(r8a66597, ACLRM, ep->pipectr); + r8a66597_write(r8a66597, 0, ep->pipectr); + r8a66597_write(r8a66597, SQCLR, ep->pipectr); + if (ep->use_dma) { + r8a66597_mdfy(r8a66597, ep->pipenum, CURPIPE, ep->fifosel); + + ndelay(450); + + r8a66597_bset(r8a66597, mbw_value(r8a66597), ep->fifosel); + } +} + +static void r8a66597_ep_setting(struct r8a66597 *r8a66597, + struct r8a66597_ep *ep, + const struct usb_endpoint_descriptor *desc, + u16 pipenum, int dma) +{ + ep->use_dma = 0; + ep->fifoaddr = CFIFO; + ep->fifosel = CFIFOSEL; + ep->fifoctr = CFIFOCTR; + ep->fifotrn = 0; + + ep->pipectr = get_pipectr_addr(pipenum); + ep->pipenum = pipenum; + ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + r8a66597->pipenum2ep[pipenum] = ep; + r8a66597->epaddr2ep[desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK] + = ep; + INIT_LIST_HEAD(&ep->queue); +} + +static void r8a66597_ep_release(struct r8a66597_ep *ep) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + u16 pipenum = ep->pipenum; + + if (pipenum == 0) + return; + + if (ep->use_dma) + r8a66597->num_dma--; + ep->pipenum = 0; + ep->busy = 0; + ep->use_dma = 0; +} + +static int alloc_pipe_config(struct r8a66597_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + struct r8a66597_pipe_info info; + int dma = 0; + unsigned char *counter; + int ret; + + ep->desc = desc; + + if (ep->pipenum) /* already allocated pipe */ + return 0; + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_BULK: + if (r8a66597->bulk >= R8A66597_MAX_NUM_BULK) { + if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { + printk(KERN_ERR "bulk pipe is insufficient\n"); + return -ENODEV; + } else { + info.pipe = R8A66597_BASE_PIPENUM_ISOC + + r8a66597->isochronous; + counter = &r8a66597->isochronous; + } + } else { + info.pipe = R8A66597_BASE_PIPENUM_BULK + r8a66597->bulk; + counter = &r8a66597->bulk; + } + info.type = R8A66597_BULK; + dma = 1; + break; + case USB_ENDPOINT_XFER_INT: + if (r8a66597->interrupt >= R8A66597_MAX_NUM_INT) { + printk(KERN_ERR "interrupt pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = R8A66597_BASE_PIPENUM_INT + r8a66597->interrupt; + info.type = R8A66597_INT; + counter = &r8a66597->interrupt; + break; + case USB_ENDPOINT_XFER_ISOC: + if (r8a66597->isochronous >= R8A66597_MAX_NUM_ISOC) { + printk(KERN_ERR "isochronous pipe is insufficient\n"); + return -ENODEV; + } + info.pipe = R8A66597_BASE_PIPENUM_ISOC + r8a66597->isochronous; + info.type = R8A66597_ISO; + counter = &r8a66597->isochronous; + break; + default: + printk(KERN_ERR "unexpect xfer type\n"); + return -EINVAL; + } + ep->type = info.type; + + info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + info.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + info.interval = desc->bInterval; + if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + info.dir_in = 1; + else + info.dir_in = 0; + + ret = pipe_buffer_setting(r8a66597, &info); + if (ret < 0) { + printk(KERN_ERR "pipe_buffer_setting fail\n"); + return ret; + } + + (*counter)++; + if ((counter == &r8a66597->isochronous) && info.type == R8A66597_BULK) + r8a66597->bulk++; + + r8a66597_ep_setting(r8a66597, ep, desc, info.pipe, dma); + pipe_initialize(ep); + + return 0; +} + +static int free_pipe_config(struct r8a66597_ep *ep) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + struct r8a66597_pipe_info info; + + info.pipe = ep->pipenum; + info.type = ep->type; + pipe_buffer_release(r8a66597, &info); + r8a66597_ep_release(ep); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +static void pipe_irq_enable(struct r8a66597 *r8a66597, u16 pipenum) +{ + enable_irq_ready(r8a66597, pipenum); + enable_irq_nrdy(r8a66597, pipenum); +} + +static void pipe_irq_disable(struct r8a66597 *r8a66597, u16 pipenum) +{ + disable_irq_ready(r8a66597, pipenum); + disable_irq_nrdy(r8a66597, pipenum); +} + +/* if complete is true, gadget driver complete function is not call */ +static void control_end(struct r8a66597 *r8a66597, unsigned ccpl) +{ + r8a66597->ep[0].internal_ccpl = ccpl; + pipe_start(r8a66597, 0); + r8a66597_bset(r8a66597, CCPL, DCPCTR); +} + +static void start_ep0_write(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + + pipe_change(r8a66597, ep->pipenum); + r8a66597_mdfy(r8a66597, ISEL, (ISEL | CURPIPE), CFIFOSEL); + r8a66597_write(r8a66597, BCLR, ep->fifoctr); + if (req->req.length == 0) { + r8a66597_bset(r8a66597, BVAL, ep->fifoctr); + pipe_start(r8a66597, 0); + transfer_complete(ep, req, 0); + } else { + r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); + irq_ep0_write(ep, req); + } +} + +static void start_packet_write(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + u16 tmp; + + pipe_change(r8a66597, ep->pipenum); + disable_irq_empty(r8a66597, ep->pipenum); + pipe_start(r8a66597, ep->pipenum); + + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) + pipe_irq_enable(r8a66597, ep->pipenum); + else + irq_packet_write(ep, req); +} + +static void start_packet_read(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + struct r8a66597 *r8a66597 = ep->r8a66597; + u16 pipenum = ep->pipenum; + + if (ep->pipenum == 0) { + r8a66597_mdfy(r8a66597, 0, (ISEL | CURPIPE), CFIFOSEL); + r8a66597_write(r8a66597, BCLR, ep->fifoctr); + pipe_start(r8a66597, pipenum); + pipe_irq_enable(r8a66597, pipenum); + } else { + if (ep->use_dma) { + r8a66597_bset(r8a66597, TRCLR, ep->fifosel); + pipe_change(r8a66597, pipenum); + r8a66597_bset(r8a66597, TRENB, ep->fifosel); + r8a66597_write(r8a66597, + (req->req.length + ep->ep.maxpacket - 1) + / ep->ep.maxpacket, + ep->fifotrn); + } + pipe_start(r8a66597, pipenum); /* trigger once */ + pipe_irq_enable(r8a66597, pipenum); + } +} + +static void start_packet(struct r8a66597_ep *ep, struct r8a66597_request *req) +{ + if (ep->desc->bEndpointAddress & USB_DIR_IN) + start_packet_write(ep, req); + else + start_packet_read(ep, req); +} + +static void start_ep0(struct r8a66597_ep *ep, struct r8a66597_request *req) +{ + u16 ctsq; + + ctsq = r8a66597_read(ep->r8a66597, INTSTS0) & CTSQ; + + switch (ctsq) { + case CS_RDDS: + start_ep0_write(ep, req); + break; + case CS_WRDS: + start_packet_read(ep, req); + break; + + case CS_WRND: + control_end(ep->r8a66597, 0); + break; + default: + printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static void init_controller(struct r8a66597 *r8a66597) +{ + u16 vif = r8a66597->pdata->vif ? LDRV : 0; + u16 irq_sense = r8a66597->irq_sense_low ? INTL : 0; + u16 endian = r8a66597->pdata->endian ? BIGEND : 0; + + if (r8a66597->pdata->on_chip) { + r8a66597_bset(r8a66597, 0x04, SYSCFG1); + r8a66597_bset(r8a66597, HSE, SYSCFG0); + + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, SCKE, SYSCFG0); + + r8a66597_bset(r8a66597, irq_sense, INTENB1); + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, + DMA0CFG); + } else { + r8a66597_bset(r8a66597, vif | endian, PINCFG); + r8a66597_bset(r8a66597, HSE, SYSCFG0); /* High spd */ + r8a66597_mdfy(r8a66597, get_xtal_from_pdata(r8a66597->pdata), + XTAL, SYSCFG0); + + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_bclr(r8a66597, DPRPU, SYSCFG0); + r8a66597_bset(r8a66597, USBE, SYSCFG0); + + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + + msleep(3); + + r8a66597_bset(r8a66597, PLLC, SYSCFG0); + + msleep(1); + + r8a66597_bset(r8a66597, SCKE, SYSCFG0); + + r8a66597_bset(r8a66597, irq_sense, INTENB1); + r8a66597_write(r8a66597, BURST | CPU_ADR_RD_WR, + DMA0CFG); + } +} + +static void disable_controller(struct r8a66597 *r8a66597) +{ + if (r8a66597->pdata->on_chip) { + r8a66597_bset(r8a66597, SCKE, SYSCFG0); + + /* disable interrupts */ + r8a66597_write(r8a66597, 0, INTENB0); + r8a66597_write(r8a66597, 0, INTENB1); + r8a66597_write(r8a66597, 0, BRDYENB); + r8a66597_write(r8a66597, 0, BEMPENB); + r8a66597_write(r8a66597, 0, NRDYENB); + + /* clear status */ + r8a66597_write(r8a66597, 0, BRDYSTS); + r8a66597_write(r8a66597, 0, NRDYSTS); + r8a66597_write(r8a66597, 0, BEMPSTS); + + r8a66597_bclr(r8a66597, USBE, SYSCFG0); + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + + } else { + r8a66597_bclr(r8a66597, SCKE, SYSCFG0); + udelay(1); + r8a66597_bclr(r8a66597, PLLC, SYSCFG0); + udelay(1); + udelay(1); + r8a66597_bclr(r8a66597, XCKE, SYSCFG0); + } +} + +static void r8a66597_start_xclock(struct r8a66597 *r8a66597) +{ + u16 tmp; + + if (!r8a66597->pdata->on_chip) { + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (!(tmp & XCKE)) + r8a66597_bset(r8a66597, XCKE, SYSCFG0); + } +} + +static struct r8a66597_request *get_request_from_ep(struct r8a66597_ep *ep) +{ + return list_entry(ep->queue.next, struct r8a66597_request, queue); +} + +/*-------------------------------------------------------------------------*/ +static void transfer_complete(struct r8a66597_ep *ep, + struct r8a66597_request *req, int status) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + int restart = 0; + + if (unlikely(ep->pipenum == 0)) { + if (ep->internal_ccpl) { + ep->internal_ccpl = 0; + return; + } + } + + list_del_init(&req->queue); + if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN) + req->req.status = -ESHUTDOWN; + else + req->req.status = status; + + if (!list_empty(&ep->queue)) + restart = 1; + + spin_unlock(&ep->r8a66597->lock); + req->req.complete(&ep->ep, &req->req); + spin_lock(&ep->r8a66597->lock); + + if (restart) { + req = get_request_from_ep(ep); + if (ep->desc) + start_packet(ep, req); + } +} + +static void irq_ep0_write(struct r8a66597_ep *ep, struct r8a66597_request *req) +{ + int i; + u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct r8a66597 *r8a66597 = ep->r8a66597; + + pipe_change(r8a66597, pipenum); + r8a66597_bset(r8a66597, ISEL, ep->fifosel); + + i = 0; + do { + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (i++ > 100000) { + printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus" + "conflict. please power off this controller."); + return; + } + ndelay(1); + } while ((tmp & FRDY) == 0); + + /* prepare parameters */ + bufsize = get_buffer_size(r8a66597, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + if (size > 0) + r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + if ((size == 0) || ((size % ep->ep.maxpacket) != 0)) + r8a66597_bset(r8a66597, BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + disable_irq_ready(r8a66597, pipenum); + disable_irq_empty(r8a66597, pipenum); + } else { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } + pipe_start(r8a66597, pipenum); +} + +static void irq_packet_write(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + u16 tmp; + unsigned bufsize; + size_t size; + void *buf; + u16 pipenum = ep->pipenum; + struct r8a66597 *r8a66597 = ep->r8a66597; + + pipe_change(r8a66597, pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) { + pipe_stop(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum); + return; + } + + /* prepare parameters */ + bufsize = get_buffer_size(r8a66597, pipenum); + buf = req->req.buf + req->req.actual; + size = min(bufsize, req->req.length - req->req.actual); + + /* write fifo */ + if (req->req.buf) { + r8a66597_write_fifo(r8a66597, ep->fifoaddr, buf, size); + if ((size == 0) + || ((size % ep->ep.maxpacket) != 0) + || ((bufsize != ep->ep.maxpacket) + && (bufsize > size))) + r8a66597_bset(r8a66597, BVAL, ep->fifoctr); + } + + /* update parameters */ + req->req.actual += size; + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + disable_irq_ready(r8a66597, pipenum); + enable_irq_empty(r8a66597, pipenum); + } else { + disable_irq_empty(r8a66597, pipenum); + pipe_irq_enable(r8a66597, pipenum); + } +} + +static void irq_packet_read(struct r8a66597_ep *ep, + struct r8a66597_request *req) +{ + u16 tmp; + int rcv_len, bufsize, req_len; + int size; + void *buf; + u16 pipenum = ep->pipenum; + struct r8a66597 *r8a66597 = ep->r8a66597; + int finish = 0; + + pipe_change(r8a66597, pipenum); + tmp = r8a66597_read(r8a66597, ep->fifoctr); + if (unlikely((tmp & FRDY) == 0)) { + req->req.status = -EPIPE; + pipe_stop(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + printk(KERN_ERR "read fifo not ready"); + return; + } + + /* prepare parameters */ + rcv_len = tmp & DTLN; + bufsize = get_buffer_size(r8a66597, pipenum); + + buf = req->req.buf + req->req.actual; + req_len = req->req.length - req->req.actual; + if (rcv_len < bufsize) + size = min(rcv_len, req_len); + else + size = min(bufsize, req_len); + + /* update parameters */ + req->req.actual += size; + + /* check transfer finish */ + if ((!req->req.zero && (req->req.actual == req->req.length)) + || (size % ep->ep.maxpacket) + || (size == 0)) { + pipe_stop(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + finish = 1; + } + + /* read fifo */ + if (req->req.buf) { + if (size == 0) + r8a66597_write(r8a66597, BCLR, ep->fifoctr); + else + r8a66597_read_fifo(r8a66597, ep->fifoaddr, buf, size); + + } + + if ((ep->pipenum != 0) && finish) + transfer_complete(ep, req, 0); +} + +static void irq_pipe_ready(struct r8a66597 *r8a66597, u16 status, u16 enb) +{ + u16 check; + u16 pipenum; + struct r8a66597_ep *ep; + struct r8a66597_request *req; + + if ((status & BRDY0) && (enb & BRDY0)) { + r8a66597_write(r8a66597, ~BRDY0, BRDYSTS); + r8a66597_mdfy(r8a66597, 0, CURPIPE, CFIFOSEL); + + ep = &r8a66597->ep[0]; + req = get_request_from_ep(ep); + irq_packet_read(ep, req); + } else { + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + r8a66597_write(r8a66597, ~check, BRDYSTS); + ep = r8a66597->pipenum2ep[pipenum]; + req = get_request_from_ep(ep); + if (ep->desc->bEndpointAddress & USB_DIR_IN) + irq_packet_write(ep, req); + else + irq_packet_read(ep, req); + } + } + } +} + +static void irq_pipe_empty(struct r8a66597 *r8a66597, u16 status, u16 enb) +{ + u16 tmp; + u16 check; + u16 pipenum; + struct r8a66597_ep *ep; + struct r8a66597_request *req; + + if ((status & BEMP0) && (enb & BEMP0)) { + r8a66597_write(r8a66597, ~BEMP0, BEMPSTS); + + ep = &r8a66597->ep[0]; + req = get_request_from_ep(ep); + irq_ep0_write(ep, req); + } else { + for (pipenum = 1; pipenum < R8A66597_MAX_NUM_PIPE; pipenum++) { + check = 1 << pipenum; + if ((status & check) && (enb & check)) { + r8a66597_write(r8a66597, ~check, BEMPSTS); + tmp = control_reg_get(r8a66597, pipenum); + if ((tmp & INBUFM) == 0) { + disable_irq_empty(r8a66597, pipenum); + pipe_irq_disable(r8a66597, pipenum); + pipe_stop(r8a66597, pipenum); + ep = r8a66597->pipenum2ep[pipenum]; + req = get_request_from_ep(ep); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, 0); + } + } + } + } +} + +static void get_status(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + struct r8a66597_ep *ep; + u16 pid; + u16 status = 0; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = 1 << USB_DEVICE_SELF_POWERED; + break; + case USB_RECIP_INTERFACE: + status = 0; + break; + case USB_RECIP_ENDPOINT: + ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + pid = control_reg_get_pid(r8a66597, ep->pipenum); + if (pid == PID_STALL) + status = 1 << USB_ENDPOINT_HALT; + else + status = 0; + break; + default: + pipe_stall(r8a66597, 0); + return; /* exit */ + } + + r8a66597->ep0_data = cpu_to_le16(status); + r8a66597->ep0_req->buf = &r8a66597->ep0_data; + r8a66597->ep0_req->length = 2; + /* AV: what happens if we get called again before that gets through? */ + spin_unlock(&r8a66597->lock); + r8a66597_queue(r8a66597->gadget.ep0, r8a66597->ep0_req, GFP_KERNEL); + spin_lock(&r8a66597->lock); +} + +static void clear_feature(struct r8a66597 *r8a66597, + struct usb_ctrlrequest *ctrl) +{ + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + control_end(r8a66597, 1); + break; + case USB_RECIP_INTERFACE: + control_end(r8a66597, 1); + break; + case USB_RECIP_ENDPOINT: { + struct r8a66597_ep *ep; + struct r8a66597_request *req; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + if (!ep->wedge) { + pipe_stop(r8a66597, ep->pipenum); + control_reg_sqclr(r8a66597, ep->pipenum); + spin_unlock(&r8a66597->lock); + usb_ep_clear_halt(&ep->ep); + spin_lock(&r8a66597->lock); + } + + control_end(r8a66597, 1); + + req = get_request_from_ep(ep); + if (ep->busy) { + ep->busy = 0; + if (list_empty(&ep->queue)) + break; + start_packet(ep, req); + } else if (!list_empty(&ep->queue)) + pipe_start(r8a66597, ep->pipenum); + } + break; + default: + pipe_stall(r8a66597, 0); + break; + } +} + +static void set_feature(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl) +{ + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + control_end(r8a66597, 1); + break; + case USB_RECIP_INTERFACE: + control_end(r8a66597, 1); + break; + case USB_RECIP_ENDPOINT: { + struct r8a66597_ep *ep; + u16 w_index = le16_to_cpu(ctrl->wIndex); + + ep = r8a66597->epaddr2ep[w_index & USB_ENDPOINT_NUMBER_MASK]; + pipe_stall(r8a66597, ep->pipenum); + + control_end(r8a66597, 1); + } + break; + default: + pipe_stall(r8a66597, 0); + break; + } +} + +/* if return value is true, call class driver's setup() */ +static int setup_packet(struct r8a66597 *r8a66597, struct usb_ctrlrequest *ctrl) +{ + u16 *p = (u16 *)ctrl; + unsigned long offset = USBREQ; + int i, ret = 0; + + /* read fifo */ + r8a66597_write(r8a66597, ~VALID, INTSTS0); + + for (i = 0; i < 4; i++) + p[i] = r8a66597_read(r8a66597, offset + i*2); + + /* check request */ + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_GET_STATUS: + get_status(r8a66597, ctrl); + break; + case USB_REQ_CLEAR_FEATURE: + clear_feature(r8a66597, ctrl); + break; + case USB_REQ_SET_FEATURE: + set_feature(r8a66597, ctrl); + break; + default: + ret = 1; + break; + } + } else + ret = 1; + return ret; +} + +static void r8a66597_update_usb_speed(struct r8a66597 *r8a66597) +{ + u16 speed = get_usb_speed(r8a66597); + + switch (speed) { + case HSMODE: + r8a66597->gadget.speed = USB_SPEED_HIGH; + break; + case FSMODE: + r8a66597->gadget.speed = USB_SPEED_FULL; + break; + default: + r8a66597->gadget.speed = USB_SPEED_UNKNOWN; + printk(KERN_ERR "USB speed unknown\n"); + } +} + +static void irq_device_state(struct r8a66597 *r8a66597) +{ + u16 dvsq; + + dvsq = r8a66597_read(r8a66597, INTSTS0) & DVSQ; + r8a66597_write(r8a66597, ~DVST, INTSTS0); + + if (dvsq == DS_DFLT) { + /* bus reset */ + r8a66597->driver->disconnect(&r8a66597->gadget); + r8a66597_update_usb_speed(r8a66597); + } + if (r8a66597->old_dvsq == DS_CNFG && dvsq != DS_CNFG) + r8a66597_update_usb_speed(r8a66597); + if ((dvsq == DS_CNFG || dvsq == DS_ADDS) + && r8a66597->gadget.speed == USB_SPEED_UNKNOWN) + r8a66597_update_usb_speed(r8a66597); + + r8a66597->old_dvsq = dvsq; +} + +static void irq_control_stage(struct r8a66597 *r8a66597) +__releases(r8a66597->lock) +__acquires(r8a66597->lock) +{ + struct usb_ctrlrequest ctrl; + u16 ctsq; + + ctsq = r8a66597_read(r8a66597, INTSTS0) & CTSQ; + r8a66597_write(r8a66597, ~CTRT, INTSTS0); + + switch (ctsq) { + case CS_IDST: { + struct r8a66597_ep *ep; + struct r8a66597_request *req; + ep = &r8a66597->ep[0]; + req = get_request_from_ep(ep); + transfer_complete(ep, req, 0); + } + break; + + case CS_RDDS: + case CS_WRDS: + case CS_WRND: + if (setup_packet(r8a66597, &ctrl)) { + spin_unlock(&r8a66597->lock); + if (r8a66597->driver->setup(&r8a66597->gadget, &ctrl) + < 0) + pipe_stall(r8a66597, 0); + spin_lock(&r8a66597->lock); + } + break; + case CS_RDSS: + case CS_WRSS: + control_end(r8a66597, 0); + break; + default: + printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq); + break; + } +} + +static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) +{ + struct r8a66597 *r8a66597 = _r8a66597; + u16 intsts0; + u16 intenb0; + u16 brdysts, nrdysts, bempsts; + u16 brdyenb, nrdyenb, bempenb; + u16 savepipe; + u16 mask0; + + spin_lock(&r8a66597->lock); + + intsts0 = r8a66597_read(r8a66597, INTSTS0); + intenb0 = r8a66597_read(r8a66597, INTENB0); + + savepipe = r8a66597_read(r8a66597, CFIFOSEL); + + mask0 = intsts0 & intenb0; + if (mask0) { + brdysts = r8a66597_read(r8a66597, BRDYSTS); + nrdysts = r8a66597_read(r8a66597, NRDYSTS); + bempsts = r8a66597_read(r8a66597, BEMPSTS); + brdyenb = r8a66597_read(r8a66597, BRDYENB); + nrdyenb = r8a66597_read(r8a66597, NRDYENB); + bempenb = r8a66597_read(r8a66597, BEMPENB); + + if (mask0 & VBINT) { + r8a66597_write(r8a66597, 0xffff & ~VBINT, + INTSTS0); + r8a66597_start_xclock(r8a66597); + + /* start vbus sampling */ + r8a66597->old_vbus = r8a66597_read(r8a66597, INTSTS0) + & VBSTS; + r8a66597->scount = R8A66597_MAX_SAMPLING; + + mod_timer(&r8a66597->timer, + jiffies + msecs_to_jiffies(50)); + } + if (intsts0 & DVSQ) + irq_device_state(r8a66597); + + if ((intsts0 & BRDY) && (intenb0 & BRDYE) + && (brdysts & brdyenb)) + irq_pipe_ready(r8a66597, brdysts, brdyenb); + if ((intsts0 & BEMP) && (intenb0 & BEMPE) + && (bempsts & bempenb)) + irq_pipe_empty(r8a66597, bempsts, bempenb); + + if (intsts0 & CTRT) + irq_control_stage(r8a66597); + } + + r8a66597_write(r8a66597, savepipe, CFIFOSEL); + + spin_unlock(&r8a66597->lock); + return IRQ_HANDLED; +} + +static void r8a66597_timer(unsigned long _r8a66597) +{ + struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597; + unsigned long flags; + u16 tmp; + + spin_lock_irqsave(&r8a66597->lock, flags); + tmp = r8a66597_read(r8a66597, SYSCFG0); + if (r8a66597->scount > 0) { + tmp = r8a66597_read(r8a66597, INTSTS0) & VBSTS; + if (tmp == r8a66597->old_vbus) { + r8a66597->scount--; + if (r8a66597->scount == 0) { + if (tmp == VBSTS) + r8a66597_usb_connect(r8a66597); + else + r8a66597_usb_disconnect(r8a66597); + } else { + mod_timer(&r8a66597->timer, + jiffies + msecs_to_jiffies(50)); + } + } else { + r8a66597->scount = R8A66597_MAX_SAMPLING; + r8a66597->old_vbus = tmp; + mod_timer(&r8a66597->timer, + jiffies + msecs_to_jiffies(50)); + } + } + spin_unlock_irqrestore(&r8a66597->lock, flags); +} + +/*-------------------------------------------------------------------------*/ +static int r8a66597_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct r8a66597_ep *ep; + + ep = container_of(_ep, struct r8a66597_ep, ep); + return alloc_pipe_config(ep, desc); +} + +static int r8a66597_disable(struct usb_ep *_ep) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + BUG_ON(!ep); + + while (!list_empty(&ep->queue)) { + req = get_request_from_ep(ep); + spin_lock_irqsave(&ep->r8a66597->lock, flags); + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + } + + pipe_irq_disable(ep->r8a66597, ep->pipenum); + return free_pipe_config(ep); +} + +static struct usb_request *r8a66597_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct r8a66597_request *req; + + req = kzalloc(sizeof(struct r8a66597_request), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void r8a66597_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct r8a66597_request *req; + + req = container_of(_req, struct r8a66597_request, req); + kfree(req); +} + +static int r8a66597_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + int request = 0; + + ep = container_of(_ep, struct r8a66597_ep, ep); + req = container_of(_req, struct r8a66597_request, req); + + if (ep->r8a66597->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + + spin_lock_irqsave(&ep->r8a66597->lock, flags); + + if (list_empty(&ep->queue)) + request = 1; + + list_add_tail(&req->queue, &ep->queue); + req->req.actual = 0; + req->req.status = -EINPROGRESS; + + if (ep->desc == NULL) /* control */ + start_ep0(ep, req); + else { + if (request && !ep->busy) + start_packet(ep, req); + } + + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + + return 0; +} + +static int r8a66597_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + req = container_of(_req, struct r8a66597_request, req); + + spin_lock_irqsave(&ep->r8a66597->lock, flags); + if (!list_empty(&ep->queue)) + transfer_complete(ep, req, -ECONNRESET); + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + + return 0; +} + +static int r8a66597_set_halt(struct usb_ep *_ep, int value) +{ + struct r8a66597_ep *ep; + struct r8a66597_request *req; + unsigned long flags; + int ret = 0; + + ep = container_of(_ep, struct r8a66597_ep, ep); + req = get_request_from_ep(ep); + + spin_lock_irqsave(&ep->r8a66597->lock, flags); + if (!list_empty(&ep->queue)) { + ret = -EAGAIN; + goto out; + } + if (value) { + ep->busy = 1; + pipe_stall(ep->r8a66597, ep->pipenum); + } else { + ep->busy = 0; + ep->wedge = 0; + pipe_stop(ep->r8a66597, ep->pipenum); + } + +out: + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + return ret; +} + +static int r8a66597_set_wedge(struct usb_ep *_ep) +{ + struct r8a66597_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + + if (!ep || !ep->desc) + return -EINVAL; + + spin_lock_irqsave(&ep->r8a66597->lock, flags); + ep->wedge = 1; + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); + + return usb_ep_set_halt(_ep); +} + +static void r8a66597_fifo_flush(struct usb_ep *_ep) +{ + struct r8a66597_ep *ep; + unsigned long flags; + + ep = container_of(_ep, struct r8a66597_ep, ep); + spin_lock_irqsave(&ep->r8a66597->lock, flags); + if (list_empty(&ep->queue) && !ep->busy) { + pipe_stop(ep->r8a66597, ep->pipenum); + r8a66597_bclr(ep->r8a66597, BCLR, ep->fifoctr); + } + spin_unlock_irqrestore(&ep->r8a66597->lock, flags); +} + +static struct usb_ep_ops r8a66597_ep_ops = { + .enable = r8a66597_enable, + .disable = r8a66597_disable, + + .alloc_request = r8a66597_alloc_request, + .free_request = r8a66597_free_request, + + .queue = r8a66597_queue, + .dequeue = r8a66597_dequeue, + + .set_halt = r8a66597_set_halt, + .set_wedge = r8a66597_set_wedge, + .fifo_flush = r8a66597_fifo_flush, +}; + +/*-------------------------------------------------------------------------*/ +static struct r8a66597 *the_controller; + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct r8a66597 *r8a66597 = the_controller; + int retval; + + if (!driver + || driver->speed != USB_SPEED_HIGH + || !driver->bind + || !driver->setup) + return -EINVAL; + if (!r8a66597) + return -ENODEV; + if (r8a66597->driver) + return -EBUSY; + + /* hook up the driver */ + driver->driver.bus = NULL; + r8a66597->driver = driver; + r8a66597->gadget.dev.driver = &driver->driver; + + retval = device_add(&r8a66597->gadget.dev); + if (retval) { + printk(KERN_ERR "device_add error (%d)\n", retval); + goto error; + } + + retval = driver->bind(&r8a66597->gadget); + if (retval) { + printk(KERN_ERR "bind to driver error (%d)\n", retval); + device_del(&r8a66597->gadget.dev); + goto error; + } + + r8a66597_bset(r8a66597, VBSE, INTENB0); + if (r8a66597_read(r8a66597, INTSTS0) & VBSTS) { + r8a66597_start_xclock(r8a66597); + /* start vbus sampling */ + r8a66597->old_vbus = r8a66597_read(r8a66597, + INTSTS0) & VBSTS; + r8a66597->scount = R8A66597_MAX_SAMPLING; + mod_timer(&r8a66597->timer, jiffies + msecs_to_jiffies(50)); + } + + return 0; + +error: + r8a66597->driver = NULL; + r8a66597->gadget.dev.driver = NULL; + + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct r8a66597 *r8a66597 = the_controller; + unsigned long flags; + + if (driver != r8a66597->driver || !driver->unbind) + return -EINVAL; + + spin_lock_irqsave(&r8a66597->lock, flags); + if (r8a66597->gadget.speed != USB_SPEED_UNKNOWN) + r8a66597_usb_disconnect(r8a66597); + spin_unlock_irqrestore(&r8a66597->lock, flags); + + r8a66597_bclr(r8a66597, VBSE, INTENB0); + + driver->unbind(&r8a66597->gadget); + + init_controller(r8a66597); + disable_controller(r8a66597); + + device_del(&r8a66597->gadget.dev); + r8a66597->driver = NULL; + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ +static int r8a66597_get_frame(struct usb_gadget *_gadget) +{ + struct r8a66597 *r8a66597 = gadget_to_r8a66597(_gadget); + return r8a66597_read(r8a66597, FRMNUM) & 0x03FF; +} + +static struct usb_gadget_ops r8a66597_gadget_ops = { + .get_frame = r8a66597_get_frame, +}; + +static int __exit r8a66597_remove(struct platform_device *pdev) +{ + struct r8a66597 *r8a66597 = dev_get_drvdata(&pdev->dev); + + del_timer_sync(&r8a66597->timer); + iounmap((void *)r8a66597->reg); + free_irq(platform_get_irq(pdev, 0), r8a66597); + r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req); +#ifdef CONFIG_HAVE_CLK + if (r8a66597->pdata->on_chip) { + clk_disable(r8a66597->clk); + clk_put(r8a66597->clk); + } +#endif + kfree(r8a66597); + return 0; +} + +static void nop_completion(struct usb_ep *ep, struct usb_request *r) +{ +} + +static int __init r8a66597_probe(struct platform_device *pdev) +{ +#ifdef CONFIG_HAVE_CLK + char clk_name[8]; +#endif + struct resource *res, *ires; + int irq; + void __iomem *reg = NULL; + struct r8a66597 *r8a66597 = NULL; + int ret = 0; + int i; + unsigned long irq_trigger; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = -ENODEV; + printk(KERN_ERR "platform_get_resource error.\n"); + goto clean_up; + } + + ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + irq = ires->start; + irq_trigger = ires->flags & IRQF_TRIGGER_MASK; + + if (irq < 0) { + ret = -ENODEV; + printk(KERN_ERR "platform_get_irq error.\n"); + goto clean_up; + } + + reg = ioremap(res->start, resource_size(res)); + if (reg == NULL) { + ret = -ENOMEM; + printk(KERN_ERR "ioremap error.\n"); + goto clean_up; + } + + /* initialize ucd */ + r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL); + if (r8a66597 == NULL) { + printk(KERN_ERR "kzalloc error\n"); + goto clean_up; + } + + spin_lock_init(&r8a66597->lock); + dev_set_drvdata(&pdev->dev, r8a66597); + r8a66597->pdata = pdev->dev.platform_data; + r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW; + + r8a66597->gadget.ops = &r8a66597_gadget_ops; + device_initialize(&r8a66597->gadget.dev); + dev_set_name(&r8a66597->gadget.dev, "gadget"); + r8a66597->gadget.is_dualspeed = 1; + r8a66597->gadget.dev.parent = &pdev->dev; + r8a66597->gadget.dev.dma_mask = pdev->dev.dma_mask; + r8a66597->gadget.dev.release = pdev->dev.release; + r8a66597->gadget.name = udc_name; + + init_timer(&r8a66597->timer); + r8a66597->timer.function = r8a66597_timer; + r8a66597->timer.data = (unsigned long)r8a66597; + r8a66597->reg = (unsigned long)reg; + +#ifdef CONFIG_HAVE_CLK + if (r8a66597->pdata->on_chip) { + snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id); + r8a66597->clk = clk_get(&pdev->dev, clk_name); + if (IS_ERR(r8a66597->clk)) { + dev_err(&pdev->dev, "cannot get clock \"%s\"\n", + clk_name); + ret = PTR_ERR(r8a66597->clk); + goto clean_up; + } + clk_enable(r8a66597->clk); + } +#endif + + disable_controller(r8a66597); /* make sure controller is disabled */ + + ret = request_irq(irq, r8a66597_irq, IRQF_DISABLED | IRQF_SHARED, + udc_name, r8a66597); + if (ret < 0) { + printk(KERN_ERR "request_irq error (%d)\n", ret); + goto clean_up2; + } + + INIT_LIST_HEAD(&r8a66597->gadget.ep_list); + r8a66597->gadget.ep0 = &r8a66597->ep[0].ep; + INIT_LIST_HEAD(&r8a66597->gadget.ep0->ep_list); + for (i = 0; i < R8A66597_MAX_NUM_PIPE; i++) { + struct r8a66597_ep *ep = &r8a66597->ep[i]; + + if (i != 0) { + INIT_LIST_HEAD(&r8a66597->ep[i].ep.ep_list); + list_add_tail(&r8a66597->ep[i].ep.ep_list, + &r8a66597->gadget.ep_list); + } + ep->r8a66597 = r8a66597; + INIT_LIST_HEAD(&ep->queue); + ep->ep.name = r8a66597_ep_name[i]; + ep->ep.ops = &r8a66597_ep_ops; + ep->ep.maxpacket = 512; + } + r8a66597->ep[0].ep.maxpacket = 64; + r8a66597->ep[0].pipenum = 0; + r8a66597->ep[0].fifoaddr = CFIFO; + r8a66597->ep[0].fifosel = CFIFOSEL; + r8a66597->ep[0].fifoctr = CFIFOCTR; + r8a66597->ep[0].fifotrn = 0; + r8a66597->ep[0].pipectr = get_pipectr_addr(0); + r8a66597->pipenum2ep[0] = &r8a66597->ep[0]; + r8a66597->epaddr2ep[0] = &r8a66597->ep[0]; + + the_controller = r8a66597; + + r8a66597->ep0_req = r8a66597_alloc_request(&r8a66597->ep[0].ep, + GFP_KERNEL); + if (r8a66597->ep0_req == NULL) + goto clean_up3; + r8a66597->ep0_req->complete = nop_completion; + + init_controller(r8a66597); + + dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION); + return 0; + +clean_up3: + free_irq(irq, r8a66597); +clean_up2: +#ifdef CONFIG_HAVE_CLK + if (r8a66597->pdata->on_chip) { + clk_disable(r8a66597->clk); + clk_put(r8a66597->clk); + } +#endif +clean_up: + if (r8a66597) { + if (r8a66597->ep0_req) + r8a66597_free_request(&r8a66597->ep[0].ep, + r8a66597->ep0_req); + kfree(r8a66597); + } + if (reg) + iounmap(reg); + + return ret; +} + +/*-------------------------------------------------------------------------*/ +static struct platform_driver r8a66597_driver = { + .remove = __exit_p(r8a66597_remove), + .driver = { + .name = (char *) udc_name, + }, +}; + +static int __init r8a66597_udc_init(void) +{ + return platform_driver_probe(&r8a66597_driver, r8a66597_probe); +} +module_init(r8a66597_udc_init); + +static void __exit r8a66597_udc_cleanup(void) +{ + platform_driver_unregister(&r8a66597_driver); +} +module_exit(r8a66597_udc_cleanup); + +MODULE_DESCRIPTION("R8A66597 USB gadget driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Yoshihiro Shimoda"); + diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/r8a66597-udc.h new file mode 100644 index 00000000000..03087e7b919 --- /dev/null +++ b/drivers/usb/gadget/r8a66597-udc.h @@ -0,0 +1,256 @@ +/* + * R8A66597 UDC + * + * Copyright (C) 2007-2009 Renesas Solutions Corp. + * + * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __R8A66597_H__ +#define __R8A66597_H__ + +#ifdef CONFIG_HAVE_CLK +#include <linux/clk.h> +#endif + +#include <linux/usb/r8a66597.h> + +#define R8A66597_MAX_SAMPLING 10 + +#define R8A66597_MAX_NUM_PIPE 8 +#define R8A66597_MAX_NUM_BULK 3 +#define R8A66597_MAX_NUM_ISOC 2 +#define R8A66597_MAX_NUM_INT 2 + +#define R8A66597_BASE_PIPENUM_BULK 3 +#define R8A66597_BASE_PIPENUM_ISOC 1 +#define R8A66597_BASE_PIPENUM_INT 6 + +#define R8A66597_BASE_BUFNUM 6 +#define R8A66597_MAX_BUFNUM 0x4F + +#define is_bulk_pipe(pipenum) \ + ((pipenum >= R8A66597_BASE_PIPENUM_BULK) && \ + (pipenum < (R8A66597_BASE_PIPENUM_BULK + R8A66597_MAX_NUM_BULK))) +#define is_interrupt_pipe(pipenum) \ + ((pipenum >= R8A66597_BASE_PIPENUM_INT) && \ + (pipenum < (R8A66597_BASE_PIPENUM_INT + R8A66597_MAX_NUM_INT))) +#define is_isoc_pipe(pipenum) \ + ((pipenum >= R8A66597_BASE_PIPENUM_ISOC) && \ + (pipenum < (R8A66597_BASE_PIPENUM_ISOC + R8A66597_MAX_NUM_ISOC))) + +struct r8a66597_pipe_info { + u16 pipe; + u16 epnum; + u16 maxpacket; + u16 type; + u16 interval; + u16 dir_in; +}; + +struct r8a66597_request { + struct usb_request req; + struct list_head queue; +}; + +struct r8a66597_ep { + struct usb_ep ep; + struct r8a66597 *r8a66597; + + struct list_head queue; + unsigned busy:1; + unsigned wedge:1; + unsigned internal_ccpl:1; /* use only control */ + + /* this member can able to after r8a66597_enable */ + unsigned use_dma:1; + u16 pipenum; + u16 type; + const struct usb_endpoint_descriptor *desc; + /* register address */ + unsigned char fifoaddr; + unsigned char fifosel; + unsigned char fifoctr; + unsigned char fifotrn; + unsigned char pipectr; +}; + +struct r8a66597 { + spinlock_t lock; + unsigned long reg; + +#ifdef CONFIG_HAVE_CLK + struct clk *clk; +#endif + struct r8a66597_platdata *pdata; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + struct r8a66597_ep ep[R8A66597_MAX_NUM_PIPE]; + struct r8a66597_ep *pipenum2ep[R8A66597_MAX_NUM_PIPE]; + struct r8a66597_ep *epaddr2ep[16]; + + struct timer_list timer; + struct usb_request *ep0_req; /* for internal request */ + u16 ep0_data; /* for internal request */ + u16 old_vbus; + u16 scount; + u16 old_dvsq; + + /* pipe config */ + unsigned char bulk; + unsigned char interrupt; + unsigned char isochronous; + unsigned char num_dma; + + unsigned irq_sense_low:1; +}; + +#define gadget_to_r8a66597(_gadget) \ + container_of(_gadget, struct r8a66597, gadget) +#define r8a66597_to_gadget(r8a66597) (&r8a66597->gadget) + +static inline u16 r8a66597_read(struct r8a66597 *r8a66597, unsigned long offset) +{ + return inw(r8a66597->reg + offset); +} + +static inline void r8a66597_read_fifo(struct r8a66597 *r8a66597, + unsigned long offset, u16 *buf, + int len) +{ + if (r8a66597->pdata->on_chip) { + unsigned long fifoaddr = r8a66597->reg + offset; + unsigned long count; + union { + unsigned long dword; + unsigned char byte[4]; + } data; + unsigned char *pb; + int i; + + count = len / 4; + insl(fifoaddr, buf, count); + + if (len & 0x00000003) { + data.dword = inl(fifoaddr); + pb = (unsigned char *)buf + count * 4; + for (i = 0; i < (len & 0x00000003); i++) + pb[i] = data.byte[i]; + } + } else { + len = (len + 1) / 2; + insw(r8a66597->reg + offset, buf, len); + } +} + +static inline void r8a66597_write(struct r8a66597 *r8a66597, u16 val, + unsigned long offset) +{ + outw(val, r8a66597->reg + offset); +} + +static inline void r8a66597_write_fifo(struct r8a66597 *r8a66597, + unsigned long offset, u16 *buf, + int len) +{ + unsigned long fifoaddr = r8a66597->reg + offset; + + if (r8a66597->pdata->on_chip) { + unsigned long count; + unsigned char *pb; + int i; + + count = len / 4; + outsl(fifoaddr, buf, count); + + if (len & 0x00000003) { + pb = (unsigned char *)buf + count * 4; + for (i = 0; i < (len & 0x00000003); i++) { + if (r8a66597_read(r8a66597, CFIFOSEL) & BIGEND) + outb(pb[i], fifoaddr + i); + else + outb(pb[i], fifoaddr + 3 - i); + } + } + } else { + int odd = len & 0x0001; + + len = len / 2; + outsw(fifoaddr, buf, len); + if (unlikely(odd)) { + buf = &buf[len]; + outb((unsigned char)*buf, fifoaddr); + } + } +} + +static inline void r8a66597_mdfy(struct r8a66597 *r8a66597, + u16 val, u16 pat, unsigned long offset) +{ + u16 tmp; + tmp = r8a66597_read(r8a66597, offset); + tmp = tmp & (~pat); + tmp = tmp | val; + r8a66597_write(r8a66597, tmp, offset); +} + +static inline u16 get_xtal_from_pdata(struct r8a66597_platdata *pdata) +{ + u16 clock = 0; + + switch (pdata->xtal) { + case R8A66597_PLATDATA_XTAL_12MHZ: + clock = XTAL12; + break; + case R8A66597_PLATDATA_XTAL_24MHZ: + clock = XTAL24; + break; + case R8A66597_PLATDATA_XTAL_48MHZ: + clock = XTAL48; + break; + default: + printk(KERN_ERR "r8a66597: platdata clock is wrong.\n"); + break; + } + + return clock; +} + +#define r8a66597_bclr(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, 0, val, offset) +#define r8a66597_bset(r8a66597, val, offset) \ + r8a66597_mdfy(r8a66597, val, 0, offset) + +#define get_pipectr_addr(pipenum) (PIPE1CTR + (pipenum - 1) * 2) + +#define enable_irq_ready(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, BRDYENB) +#define disable_irq_ready(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, BRDYENB) +#define enable_irq_empty(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, BEMPENB) +#define disable_irq_empty(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, BEMPENB) +#define enable_irq_nrdy(r8a66597, pipenum) \ + enable_pipe_irq(r8a66597, pipenum, NRDYENB) +#define disable_irq_nrdy(r8a66597, pipenum) \ + disable_pipe_irq(r8a66597, pipenum, NRDYENB) + +#endif /* __R8A66597_H__ */ + diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index c7c1ca0494c..1d26beddf2c 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2167,8 +2167,9 @@ static int __devexit musb_remove(struct platform_device *pdev) #ifdef CONFIG_PM -static int musb_suspend(struct platform_device *pdev, pm_message_t message) +static int musb_suspend(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); unsigned long flags; struct musb *musb = dev_to_musb(&pdev->dev); @@ -2195,8 +2196,9 @@ static int musb_suspend(struct platform_device *pdev, pm_message_t message) return 0; } -static int musb_resume_early(struct platform_device *pdev) +static int musb_resume_noirq(struct device *dev) { + struct platform_device *pdev = to_platform_device(dev); struct musb *musb = dev_to_musb(&pdev->dev); if (!musb->clock) @@ -2214,9 +2216,14 @@ static int musb_resume_early(struct platform_device *pdev) return 0; } +static struct dev_pm_ops musb_dev_pm_ops = { + .suspend = musb_suspend, + .resume_noirq = musb_resume_noirq, +}; + +#define MUSB_DEV_PM_OPS (&musb_dev_pm_ops) #else -#define musb_suspend NULL -#define musb_resume_early NULL +#define MUSB_DEV_PM_OPS NULL #endif static struct platform_driver musb_driver = { @@ -2224,11 +2231,10 @@ static struct platform_driver musb_driver = { .name = (char *)musb_driver_name, .bus = &platform_bus_type, .owner = THIS_MODULE, + .pm = MUSB_DEV_PM_OPS, }, .remove = __devexit_p(musb_remove), .shutdown = musb_shutdown, - .suspend = musb_suspend, - .resume_early = musb_resume_early, }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index fc3f9662cea..1cb5213c1a0 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c @@ -14,6 +14,7 @@ #include <linux/mm.h> #include <linux/fb.h> #include <linux/clk.h> +#include <linux/pm_runtime.h> #include <linux/platform_device.h> #include <linux/dma-mapping.h> #include <linux/interrupt.h> @@ -23,33 +24,6 @@ #define PALETTE_NR 16 -struct sh_mobile_lcdc_priv; -struct sh_mobile_lcdc_chan { - struct sh_mobile_lcdc_priv *lcdc; - unsigned long *reg_offs; - unsigned long ldmt1r_value; - unsigned long enabled; /* ME and SE in LDCNT2R */ - struct sh_mobile_lcdc_chan_cfg cfg; - u32 pseudo_palette[PALETTE_NR]; - struct fb_info *info; - dma_addr_t dma_handle; - struct fb_deferred_io defio; - struct scatterlist *sglist; - unsigned long frame_end; - wait_queue_head_t frame_end_wait; -}; - -struct sh_mobile_lcdc_priv { - void __iomem *base; - int irq; - atomic_t clk_usecnt; - struct clk *dot_clk; - struct clk *clk; - unsigned long lddckr; - struct sh_mobile_lcdc_chan ch[2]; - int started; -}; - /* shared registers */ #define _LDDCKR 0x410 #define _LDDCKSTPR 0x414 @@ -63,11 +37,23 @@ struct sh_mobile_lcdc_priv { #define _LDDWAR 0x900 #define _LDDRAR 0x904 +/* shared registers and their order for context save/restore */ +static int lcdc_shared_regs[] = { + _LDDCKR, + _LDDCKSTPR, + _LDINTR, + _LDDDSR, + _LDCNT1R, + _LDCNT2R, +}; +#define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) + /* per-channel registers */ enum { LDDCKPAT1R, LDDCKPAT2R, LDMT1R, LDMT2R, LDMT3R, LDDFR, LDSM1R, - LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR }; + LDSM2R, LDSA1R, LDMLSR, LDHCNR, LDHSYNR, LDVLNR, LDVSYNR, LDPMR, + NR_CH_REGS }; -static unsigned long lcdc_offs_mainlcd[] = { +static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x400, [LDDCKPAT2R] = 0x404, [LDMT1R] = 0x418, @@ -85,7 +71,7 @@ static unsigned long lcdc_offs_mainlcd[] = { [LDPMR] = 0x460, }; -static unsigned long lcdc_offs_sublcd[] = { +static unsigned long lcdc_offs_sublcd[NR_CH_REGS] = { [LDDCKPAT1R] = 0x408, [LDDCKPAT2R] = 0x40c, [LDMT1R] = 0x600, @@ -110,6 +96,35 @@ static unsigned long lcdc_offs_sublcd[] = { #define LDINTR_FE 0x00000400 #define LDINTR_FS 0x00000004 +struct sh_mobile_lcdc_priv; +struct sh_mobile_lcdc_chan { + struct sh_mobile_lcdc_priv *lcdc; + unsigned long *reg_offs; + unsigned long ldmt1r_value; + unsigned long enabled; /* ME and SE in LDCNT2R */ + struct sh_mobile_lcdc_chan_cfg cfg; + u32 pseudo_palette[PALETTE_NR]; + unsigned long saved_ch_regs[NR_CH_REGS]; + struct fb_info *info; + dma_addr_t dma_handle; + struct fb_deferred_io defio; + struct scatterlist *sglist; + unsigned long frame_end; + wait_queue_head_t frame_end_wait; +}; + +struct sh_mobile_lcdc_priv { + void __iomem *base; + int irq; + atomic_t hw_usecnt; + struct device *dev; + struct clk *dot_clk; + unsigned long lddckr; + struct sh_mobile_lcdc_chan ch[2]; + unsigned long saved_shared_regs[NR_SHARED_REGS]; + int started; +}; + static void lcdc_write_chan(struct sh_mobile_lcdc_chan *chan, int reg_nr, unsigned long data) { @@ -188,8 +203,8 @@ struct sh_mobile_lcdc_sys_bus_ops sh_mobile_lcdc_sys_bus_ops = { static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) { - if (atomic_inc_and_test(&priv->clk_usecnt)) { - clk_enable(priv->clk); + if (atomic_inc_and_test(&priv->hw_usecnt)) { + pm_runtime_get_sync(priv->dev); if (priv->dot_clk) clk_enable(priv->dot_clk); } @@ -197,10 +212,10 @@ static void sh_mobile_lcdc_clk_on(struct sh_mobile_lcdc_priv *priv) static void sh_mobile_lcdc_clk_off(struct sh_mobile_lcdc_priv *priv) { - if (atomic_sub_return(1, &priv->clk_usecnt) == -1) { + if (atomic_sub_return(1, &priv->hw_usecnt) == -1) { if (priv->dot_clk) clk_disable(priv->dot_clk); - clk_disable(priv->clk); + pm_runtime_put(priv->dev); } } @@ -574,7 +589,6 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, int clock_source, struct sh_mobile_lcdc_priv *priv) { - char clk_name[8]; char *str; int icksel; @@ -588,23 +602,21 @@ static int sh_mobile_lcdc_setup_clocks(struct platform_device *pdev, priv->lddckr = icksel << 16; - atomic_set(&priv->clk_usecnt, -1); - snprintf(clk_name, sizeof(clk_name), "lcdc%d", pdev->id); - priv->clk = clk_get(&pdev->dev, clk_name); - if (IS_ERR(priv->clk)) { - dev_err(&pdev->dev, "cannot get clock \"%s\"\n", clk_name); - return PTR_ERR(priv->clk); - } - if (str) { priv->dot_clk = clk_get(&pdev->dev, str); if (IS_ERR(priv->dot_clk)) { dev_err(&pdev->dev, "cannot get dot clock %s\n", str); - clk_put(priv->clk); return PTR_ERR(priv->dot_clk); } } - + atomic_set(&priv->hw_usecnt, -1); + + /* Runtime PM support involves two step for this driver: + * 1) Enable Runtime PM + * 2) Force Runtime PM Resume since hardware is accessed from probe() + */ + pm_runtime_enable(priv->dev); + pm_runtime_resume(priv->dev); return 0; } @@ -722,9 +734,59 @@ static int sh_mobile_lcdc_resume(struct device *dev) return sh_mobile_lcdc_start(platform_get_drvdata(pdev)); } +static int sh_mobile_lcdc_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); + struct sh_mobile_lcdc_chan *ch; + int k, n; + + /* save per-channel registers */ + for (k = 0; k < ARRAY_SIZE(p->ch); k++) { + ch = &p->ch[k]; + if (!ch->enabled) + continue; + for (n = 0; n < NR_CH_REGS; n++) + ch->saved_ch_regs[n] = lcdc_read_chan(ch, n); + } + + /* save shared registers */ + for (n = 0; n < NR_SHARED_REGS; n++) + p->saved_shared_regs[n] = lcdc_read(p, lcdc_shared_regs[n]); + + /* turn off LCDC hardware */ + lcdc_write(p, _LDCNT1R, 0); + return 0; +} + +static int sh_mobile_lcdc_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct sh_mobile_lcdc_priv *p = platform_get_drvdata(pdev); + struct sh_mobile_lcdc_chan *ch; + int k, n; + + /* restore per-channel registers */ + for (k = 0; k < ARRAY_SIZE(p->ch); k++) { + ch = &p->ch[k]; + if (!ch->enabled) + continue; + for (n = 0; n < NR_CH_REGS; n++) + lcdc_write_chan(ch, n, ch->saved_ch_regs[n]); + } + + /* restore shared registers */ + for (n = 0; n < NR_SHARED_REGS; n++) + lcdc_write(p, lcdc_shared_regs[n], p->saved_shared_regs[n]); + + return 0; +} + static struct dev_pm_ops sh_mobile_lcdc_dev_pm_ops = { .suspend = sh_mobile_lcdc_suspend, .resume = sh_mobile_lcdc_resume, + .runtime_suspend = sh_mobile_lcdc_runtime_suspend, + .runtime_resume = sh_mobile_lcdc_runtime_resume, }; static int sh_mobile_lcdc_remove(struct platform_device *pdev); @@ -769,6 +831,7 @@ static int __init sh_mobile_lcdc_probe(struct platform_device *pdev) } priv->irq = i; + priv->dev = &pdev->dev; platform_set_drvdata(pdev, priv); pdata = pdev->dev.platform_data; @@ -940,7 +1003,8 @@ static int sh_mobile_lcdc_remove(struct platform_device *pdev) if (priv->dot_clk) clk_put(priv->dot_clk); - clk_put(priv->clk); + + pm_runtime_disable(priv->dev); if (priv->base) iounmap(priv->base); |