diff options
Diffstat (limited to 'drivers')
118 files changed, 3894 insertions, 907 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index c11f9aeca70..1ba9d61ea69 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -367,7 +367,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) /* * Treat freezing temperatures as invalid as well; some * BIOSes return really low values and cause reboots at startup. - * Below zero (Celcius) values clearly aren't right for sure.. + * Below zero (Celsius) values clearly aren't right for sure.. * ... so lets discard those as invalid. */ if (ACPI_FAILURE(status) || diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig index 0bcf2646467..9120717c070 100644 --- a/drivers/ata/Kconfig +++ b/drivers/ata/Kconfig @@ -86,7 +86,7 @@ config ATA_SFF For users with exclusively modern controllers like AHCI, Silicon Image 3124, or Marvell 6440, you may choose to - disable this uneeded SFF support. + disable this unneeded SFF support. If unsure, say Y. diff --git a/drivers/base/node.c b/drivers/base/node.c index f8f578a71b2..40b809742a1 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -24,7 +24,7 @@ static struct sysdev_class node_class = { static ssize_t node_read_cpumap(struct sys_device *dev, int type, char *buf) { struct node *node_dev = to_node(dev); - node_to_cpumask_ptr(mask, node_dev->sysdev.id); + const struct cpumask *mask = cpumask_of_node(node_dev->sysdev.id); int len; /* 2008/04/07: buf currently PAGE_SIZE, need 9 chars per 32 bits. */ diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 76ce75bad91..3236b434b96 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -300,7 +300,7 @@ void sysdev_unregister(struct sys_device *sysdev) * and the class driver. * * Note: The list is iterated in reverse order, so that we shut down - * child devices before we shut down thier parents. The list ordering + * child devices before we shut down their parents. The list ordering * is guaranteed by virtue of the fact that child devices are registered * after their parents. */ diff --git a/drivers/char/bsr.c b/drivers/char/bsr.c index f6094ae0ef3..140ea10ecb8 100644 --- a/drivers/char/bsr.c +++ b/drivers/char/bsr.c @@ -140,7 +140,7 @@ static int bsr_open(struct inode * inode, struct file * filp) return 0; } -const static struct file_operations bsr_fops = { +static const struct file_operations bsr_fops = { .owner = THIS_MODULE, .mmap = bsr_mmap, .open = bsr_open, diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c index ebea9b2c30a..6de020d078e 100644 --- a/drivers/char/sysrq.c +++ b/drivers/char/sysrq.c @@ -283,7 +283,7 @@ static void sysrq_ftrace_dump(int key, struct tty_struct *tty) } static struct sysrq_key_op sysrq_ftrace_dump_op = { .handler = sysrq_ftrace_dump, - .help_msg = "dumpZ-ftrace-buffer", + .help_msg = "dump-ftrace-buffer(Z)", .action_msg = "Dump ftrace buffer", .enable_mask = SYSRQ_ENABLE_DUMP, }; diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 48ea59e7967..3b3c01b6f1e 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -98,6 +98,17 @@ config NET_DMA Say Y here if you enabled INTEL_IOATDMA or FSL_DMA, otherwise say N. +config ASYNC_TX_DMA + bool "Async_tx: Offload support for the async_tx api" + depends on DMA_ENGINE + help + This allows the async_tx api to take advantage of offload engines for + memcpy, memset, xor, and raid6 p+q operations. If your platform has + a dma engine that can perform raid operations and you have enabled + MD_RAID456 say Y. + + If unsure, say N. + config DMATEST tristate "DMA Test client" depends on DMA_ENGINE diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 280a9d263eb..92438e9dacc 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -507,6 +507,7 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v * published in the general-purpose allocator */ dma_cap_set(DMA_PRIVATE, device->cap_mask); + device->privatecnt++; err = dma_chan_get(chan); if (err == -ENODEV) { @@ -518,6 +519,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v dma_chan_name(chan), err); else break; + if (--device->privatecnt == 0) + dma_cap_clear(DMA_PRIVATE, device->cap_mask); chan->private = NULL; chan = NULL; } @@ -537,6 +540,9 @@ void dma_release_channel(struct dma_chan *chan) WARN_ONCE(chan->client_count != 1, "chan reference count %d != 1\n", chan->client_count); dma_chan_put(chan); + /* drop PRIVATE cap enabled by __dma_request_channel() */ + if (--chan->device->privatecnt == 0) + dma_cap_clear(DMA_PRIVATE, chan->device->cap_mask); chan->private = NULL; mutex_unlock(&dma_list_mutex); } @@ -602,6 +608,24 @@ void dmaengine_put(void) } EXPORT_SYMBOL(dmaengine_put); +static int get_dma_id(struct dma_device *device) +{ + int rc; + + idr_retry: + if (!idr_pre_get(&dma_idr, GFP_KERNEL)) + return -ENOMEM; + mutex_lock(&dma_list_mutex); + rc = idr_get_new(&dma_idr, NULL, &device->dev_id); + mutex_unlock(&dma_list_mutex); + if (rc == -EAGAIN) + goto idr_retry; + else if (rc != 0) + return rc; + + return 0; +} + /** * dma_async_device_register - registers DMA devices found * @device: &dma_device @@ -640,27 +664,25 @@ int dma_async_device_register(struct dma_device *device) idr_ref = kmalloc(sizeof(*idr_ref), GFP_KERNEL); if (!idr_ref) return -ENOMEM; - atomic_set(idr_ref, 0); - idr_retry: - if (!idr_pre_get(&dma_idr, GFP_KERNEL)) - return -ENOMEM; - mutex_lock(&dma_list_mutex); - rc = idr_get_new(&dma_idr, NULL, &device->dev_id); - mutex_unlock(&dma_list_mutex); - if (rc == -EAGAIN) - goto idr_retry; - else if (rc != 0) + rc = get_dma_id(device); + if (rc != 0) { + kfree(idr_ref); return rc; + } + + atomic_set(idr_ref, 0); /* represent channels in sysfs. Probably want devs too */ list_for_each_entry(chan, &device->channels, device_node) { + rc = -ENOMEM; chan->local = alloc_percpu(typeof(*chan->local)); if (chan->local == NULL) - continue; + goto err_out; chan->dev = kzalloc(sizeof(*chan->dev), GFP_KERNEL); if (chan->dev == NULL) { free_percpu(chan->local); - continue; + chan->local = NULL; + goto err_out; } chan->chan_id = chancnt++; @@ -677,6 +699,8 @@ int dma_async_device_register(struct dma_device *device) if (rc) { free_percpu(chan->local); chan->local = NULL; + kfree(chan->dev); + atomic_dec(idr_ref); goto err_out; } chan->client_count = 0; @@ -701,12 +725,23 @@ int dma_async_device_register(struct dma_device *device) } } list_add_tail_rcu(&device->global_node, &dma_device_list); + if (dma_has_cap(DMA_PRIVATE, device->cap_mask)) + device->privatecnt++; /* Always private */ dma_channel_rebalance(); mutex_unlock(&dma_list_mutex); return 0; err_out: + /* if we never registered a channel just release the idr */ + if (atomic_read(idr_ref) == 0) { + mutex_lock(&dma_list_mutex); + idr_remove(&dma_idr, device->dev_id); + mutex_unlock(&dma_list_mutex); + kfree(idr_ref); + return rc; + } + list_for_each_entry(chan, &device->channels, device_node) { if (chan->local == NULL) continue; @@ -893,6 +928,7 @@ void dma_async_tx_descriptor_init(struct dma_async_tx_descriptor *tx, { tx->chan = chan; spin_lock_init(&tx->lock); + INIT_LIST_HEAD(&tx->tx_list); } EXPORT_SYMBOL(dma_async_tx_descriptor_init); diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index e190d8b3070..a27c0fb1bc1 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -38,6 +38,11 @@ module_param(max_channels, uint, S_IRUGO); MODULE_PARM_DESC(max_channels, "Maximum number of channels to use (default: all)"); +static unsigned int xor_sources = 3; +module_param(xor_sources, uint, S_IRUGO); +MODULE_PARM_DESC(xor_sources, + "Number of xor source buffers (default: 3)"); + /* * Initialization patterns. All bytes in the source buffer has bit 7 * set, all bytes in the destination buffer has bit 7 cleared. @@ -59,8 +64,9 @@ struct dmatest_thread { struct list_head node; struct task_struct *task; struct dma_chan *chan; - u8 *srcbuf; - u8 *dstbuf; + u8 **srcs; + u8 **dsts; + enum dma_transaction_type type; }; struct dmatest_chan { @@ -98,30 +104,37 @@ static unsigned long dmatest_random(void) return buf; } -static void dmatest_init_srcbuf(u8 *buf, unsigned int start, unsigned int len) +static void dmatest_init_srcs(u8 **bufs, unsigned int start, unsigned int len) { unsigned int i; - - for (i = 0; i < start; i++) - buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); - for ( ; i < start + len; i++) - buf[i] = PATTERN_SRC | PATTERN_COPY - | (~i & PATTERN_COUNT_MASK);; - for ( ; i < test_buf_size; i++) - buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); + u8 *buf; + + for (; (buf = *bufs); bufs++) { + for (i = 0; i < start; i++) + buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); + for ( ; i < start + len; i++) + buf[i] = PATTERN_SRC | PATTERN_COPY + | (~i & PATTERN_COUNT_MASK);; + for ( ; i < test_buf_size; i++) + buf[i] = PATTERN_SRC | (~i & PATTERN_COUNT_MASK); + buf++; + } } -static void dmatest_init_dstbuf(u8 *buf, unsigned int start, unsigned int len) +static void dmatest_init_dsts(u8 **bufs, unsigned int start, unsigned int len) { unsigned int i; - - for (i = 0; i < start; i++) - buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); - for ( ; i < start + len; i++) - buf[i] = PATTERN_DST | PATTERN_OVERWRITE - | (~i & PATTERN_COUNT_MASK); - for ( ; i < test_buf_size; i++) - buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); + u8 *buf; + + for (; (buf = *bufs); bufs++) { + for (i = 0; i < start; i++) + buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); + for ( ; i < start + len; i++) + buf[i] = PATTERN_DST | PATTERN_OVERWRITE + | (~i & PATTERN_COUNT_MASK); + for ( ; i < test_buf_size; i++) + buf[i] = PATTERN_DST | (~i & PATTERN_COUNT_MASK); + } } static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index, @@ -150,23 +163,30 @@ static void dmatest_mismatch(u8 actual, u8 pattern, unsigned int index, thread_name, index, expected, actual); } -static unsigned int dmatest_verify(u8 *buf, unsigned int start, +static unsigned int dmatest_verify(u8 **bufs, unsigned int start, unsigned int end, unsigned int counter, u8 pattern, bool is_srcbuf) { unsigned int i; unsigned int error_count = 0; u8 actual; - - for (i = start; i < end; i++) { - actual = buf[i]; - if (actual != (pattern | (~counter & PATTERN_COUNT_MASK))) { - if (error_count < 32) - dmatest_mismatch(actual, pattern, i, counter, - is_srcbuf); - error_count++; + u8 expected; + u8 *buf; + unsigned int counter_orig = counter; + + for (; (buf = *bufs); bufs++) { + counter = counter_orig; + for (i = start; i < end; i++) { + actual = buf[i]; + expected = pattern | (~counter & PATTERN_COUNT_MASK); + if (actual != expected) { + if (error_count < 32) + dmatest_mismatch(actual, pattern, i, + counter, is_srcbuf); + error_count++; + } + counter++; } - counter++; } if (error_count > 32) @@ -176,12 +196,17 @@ static unsigned int dmatest_verify(u8 *buf, unsigned int start, return error_count; } +static void dmatest_callback(void *completion) +{ + complete(completion); +} + /* * This function repeatedly tests DMA transfers of various lengths and - * offsets until it is told to exit by kthread_stop(). There may be - * multiple threads running this function in parallel for a single - * channel, and there may be multiple channels being tested in - * parallel. + * offsets for a given operation type until it is told to exit by + * kthread_stop(). There may be multiple threads running this function + * in parallel for a single channel, and there may be multiple channels + * being tested in parallel. * * Before each test, the source and destination buffer is initialized * with a known pattern. This pattern is different depending on @@ -201,25 +226,57 @@ static int dmatest_func(void *data) unsigned int total_tests = 0; dma_cookie_t cookie; enum dma_status status; + enum dma_ctrl_flags flags; int ret; + int src_cnt; + int dst_cnt; + int i; thread_name = current->comm; ret = -ENOMEM; - thread->srcbuf = kmalloc(test_buf_size, GFP_KERNEL); - if (!thread->srcbuf) - goto err_srcbuf; - thread->dstbuf = kmalloc(test_buf_size, GFP_KERNEL); - if (!thread->dstbuf) - goto err_dstbuf; smp_rmb(); chan = thread->chan; + if (thread->type == DMA_MEMCPY) + src_cnt = dst_cnt = 1; + else if (thread->type == DMA_XOR) { + src_cnt = xor_sources | 1; /* force odd to ensure dst = src */ + dst_cnt = 1; + } else + goto err_srcs; + + thread->srcs = kcalloc(src_cnt+1, sizeof(u8 *), GFP_KERNEL); + if (!thread->srcs) + goto err_srcs; + for (i = 0; i < src_cnt; i++) { + thread->srcs[i] = kmalloc(test_buf_size, GFP_KERNEL); + if (!thread->srcs[i]) + goto err_srcbuf; + } + thread->srcs[i] = NULL; + + thread->dsts = kcalloc(dst_cnt+1, sizeof(u8 *), GFP_KERNEL); + if (!thread->dsts) + goto err_dsts; + for (i = 0; i < dst_cnt; i++) { + thread->dsts[i] = kmalloc(test_buf_size, GFP_KERNEL); + if (!thread->dsts[i]) + goto err_dstbuf; + } + thread->dsts[i] = NULL; + + set_user_nice(current, 10); + + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT; while (!kthread_should_stop()) { struct dma_device *dev = chan->device; - struct dma_async_tx_descriptor *tx; - dma_addr_t dma_src, dma_dest; + struct dma_async_tx_descriptor *tx = NULL; + dma_addr_t dma_srcs[src_cnt]; + dma_addr_t dma_dsts[dst_cnt]; + struct completion cmp; + unsigned long tmo = msecs_to_jiffies(3000); total_tests++; @@ -227,22 +284,41 @@ static int dmatest_func(void *data) src_off = dmatest_random() % (test_buf_size - len + 1); dst_off = dmatest_random() % (test_buf_size - len + 1); - dmatest_init_srcbuf(thread->srcbuf, src_off, len); - dmatest_init_dstbuf(thread->dstbuf, dst_off, len); + dmatest_init_srcs(thread->srcs, src_off, len); + dmatest_init_dsts(thread->dsts, dst_off, len); - dma_src = dma_map_single(dev->dev, thread->srcbuf + src_off, - len, DMA_TO_DEVICE); + for (i = 0; i < src_cnt; i++) { + u8 *buf = thread->srcs[i] + src_off; + + dma_srcs[i] = dma_map_single(dev->dev, buf, len, + DMA_TO_DEVICE); + } /* map with DMA_BIDIRECTIONAL to force writeback/invalidate */ - dma_dest = dma_map_single(dev->dev, thread->dstbuf, - test_buf_size, DMA_BIDIRECTIONAL); + for (i = 0; i < dst_cnt; i++) { + dma_dsts[i] = dma_map_single(dev->dev, thread->dsts[i], + test_buf_size, + DMA_BIDIRECTIONAL); + } + + if (thread->type == DMA_MEMCPY) + tx = dev->device_prep_dma_memcpy(chan, + dma_dsts[0] + dst_off, + dma_srcs[0], len, + flags); + else if (thread->type == DMA_XOR) + tx = dev->device_prep_dma_xor(chan, + dma_dsts[0] + dst_off, + dma_srcs, xor_sources, + len, flags); - tx = dev->device_prep_dma_memcpy(chan, dma_dest + dst_off, - dma_src, len, - DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP); if (!tx) { - dma_unmap_single(dev->dev, dma_src, len, DMA_TO_DEVICE); - dma_unmap_single(dev->dev, dma_dest, - test_buf_size, DMA_BIDIRECTIONAL); + for (i = 0; i < src_cnt; i++) + dma_unmap_single(dev->dev, dma_srcs[i], len, + DMA_TO_DEVICE); + for (i = 0; i < dst_cnt; i++) + dma_unmap_single(dev->dev, dma_dsts[i], + test_buf_size, + DMA_BIDIRECTIONAL); pr_warning("%s: #%u: prep error with src_off=0x%x " "dst_off=0x%x len=0x%x\n", thread_name, total_tests - 1, @@ -251,7 +327,10 @@ static int dmatest_func(void *data) failed_tests++; continue; } - tx->callback = NULL; + + init_completion(&cmp); + tx->callback = dmatest_callback; + tx->callback_param = &cmp; cookie = tx->tx_submit(tx); if (dma_submit_error(cookie)) { @@ -263,44 +342,50 @@ static int dmatest_func(void *data) failed_tests++; continue; } - dma_async_memcpy_issue_pending(chan); + dma_async_issue_pending(chan); - do { - msleep(1); - status = dma_async_memcpy_complete( - chan, cookie, NULL, NULL); - } while (status == DMA_IN_PROGRESS); + tmo = wait_for_completion_timeout(&cmp, tmo); + status = dma_async_is_tx_complete(chan, cookie, NULL, NULL); - if (status == DMA_ERROR) { - pr_warning("%s: #%u: error during copy\n", - thread_name, total_tests - 1); + if (tmo == 0) { + pr_warning("%s: #%u: test timed out\n", + thread_name, total_tests - 1); + failed_tests++; + continue; + } else if (status != DMA_SUCCESS) { + pr_warning("%s: #%u: got completion callback," + " but status is \'%s\'\n", + thread_name, total_tests - 1, + status == DMA_ERROR ? "error" : "in progress"); failed_tests++; continue; } + /* Unmap by myself (see DMA_COMPL_SKIP_DEST_UNMAP above) */ - dma_unmap_single(dev->dev, dma_dest, - test_buf_size, DMA_BIDIRECTIONAL); + for (i = 0; i < dst_cnt; i++) + dma_unmap_single(dev->dev, dma_dsts[i], test_buf_size, + DMA_BIDIRECTIONAL); error_count = 0; pr_debug("%s: verifying source buffer...\n", thread_name); - error_count += dmatest_verify(thread->srcbuf, 0, src_off, + error_count += dmatest_verify(thread->srcs, 0, src_off, 0, PATTERN_SRC, true); - error_count += dmatest_verify(thread->srcbuf, src_off, + error_count += dmatest_verify(thread->srcs, src_off, src_off + len, src_off, PATTERN_SRC | PATTERN_COPY, true); - error_count += dmatest_verify(thread->srcbuf, src_off + len, + error_count += dmatest_verify(thread->srcs, src_off + len, test_buf_size, src_off + len, PATTERN_SRC, true); pr_debug("%s: verifying dest buffer...\n", thread->task->comm); - error_count += dmatest_verify(thread->dstbuf, 0, dst_off, + error_count += dmatest_verify(thread->dsts, 0, dst_off, 0, PATTERN_DST, false); - error_count += dmatest_verify(thread->dstbuf, dst_off, + error_count += dmatest_verify(thread->dsts, dst_off, dst_off + len, src_off, PATTERN_SRC | PATTERN_COPY, false); - error_count += dmatest_verify(thread->dstbuf, dst_off + len, + error_count += dmatest_verify(thread->dsts, dst_off + len, test_buf_size, dst_off + len, PATTERN_DST, false); @@ -319,10 +404,16 @@ static int dmatest_func(void *data) } ret = 0; - kfree(thread->dstbuf); + for (i = 0; thread->dsts[i]; i++) + kfree(thread->dsts[i]); err_dstbuf: - kfree(thread->srcbuf); + kfree(thread->dsts); +err_dsts: + for (i = 0; thread->srcs[i]; i++) + kfree(thread->srcs[i]); err_srcbuf: + kfree(thread->srcs); +err_srcs: pr_notice("%s: terminating after %u tests, %u failures (status %d)\n", thread_name, total_tests, failed_tests, ret); return ret; @@ -344,35 +435,36 @@ static void dmatest_cleanup_channel(struct dmatest_chan *dtc) kfree(dtc); } -static int dmatest_add_channel(struct dma_chan *chan) +static int dmatest_add_threads(struct dmatest_chan *dtc, enum dma_transaction_type type) { - struct dmatest_chan *dtc; - struct dmatest_thread *thread; - unsigned int i; - - dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); - if (!dtc) { - pr_warning("dmatest: No memory for %s\n", dma_chan_name(chan)); - return -ENOMEM; - } + struct dmatest_thread *thread; + struct dma_chan *chan = dtc->chan; + char *op; + unsigned int i; - dtc->chan = chan; - INIT_LIST_HEAD(&dtc->threads); + if (type == DMA_MEMCPY) + op = "copy"; + else if (type == DMA_XOR) + op = "xor"; + else + return -EINVAL; for (i = 0; i < threads_per_chan; i++) { thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); if (!thread) { - pr_warning("dmatest: No memory for %s-test%u\n", - dma_chan_name(chan), i); + pr_warning("dmatest: No memory for %s-%s%u\n", + dma_chan_name(chan), op, i); + break; } thread->chan = dtc->chan; + thread->type = type; smp_wmb(); - thread->task = kthread_run(dmatest_func, thread, "%s-test%u", - dma_chan_name(chan), i); + thread->task = kthread_run(dmatest_func, thread, "%s-%s%u", + dma_chan_name(chan), op, i); if (IS_ERR(thread->task)) { - pr_warning("dmatest: Failed to run thread %s-test%u\n", - dma_chan_name(chan), i); + pr_warning("dmatest: Failed to run thread %s-%s%u\n", + dma_chan_name(chan), op, i); kfree(thread); break; } @@ -382,7 +474,36 @@ static int dmatest_add_channel(struct dma_chan *chan) list_add_tail(&thread->node, &dtc->threads); } - pr_info("dmatest: Started %u threads using %s\n", i, dma_chan_name(chan)); + return i; +} + +static int dmatest_add_channel(struct dma_chan *chan) +{ + struct dmatest_chan *dtc; + struct dma_device *dma_dev = chan->device; + unsigned int thread_count = 0; + unsigned int cnt; + + dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); + if (!dtc) { + pr_warning("dmatest: No memory for %s\n", dma_chan_name(chan)); + return -ENOMEM; + } + + dtc->chan = chan; + INIT_LIST_HEAD(&dtc->threads); + + if (dma_has_cap(DMA_MEMCPY, dma_dev->cap_mask)) { + cnt = dmatest_add_threads(dtc, DMA_MEMCPY); + thread_count += cnt > 0 ?: 0; + } + if (dma_has_cap(DMA_XOR, dma_dev->cap_mask)) { + cnt = dmatest_add_threads(dtc, DMA_XOR); + thread_count += cnt > 0 ?: 0; + } + + pr_info("dmatest: Started %u threads using %s\n", + thread_count, dma_chan_name(chan)); list_add_tail(&dtc->node, &dmatest_channels); nr_channels++; diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index 20ad3d26bec..98c9a847bf5 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c @@ -363,6 +363,82 @@ static void dwc_handle_error(struct dw_dma *dw, struct dw_dma_chan *dwc) dwc_descriptor_complete(dwc, bad_desc); } +/* --------------------- Cyclic DMA API extensions -------------------- */ + +inline dma_addr_t dw_dma_get_src_addr(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + return channel_readl(dwc, SAR); +} +EXPORT_SYMBOL(dw_dma_get_src_addr); + +inline dma_addr_t dw_dma_get_dst_addr(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + return channel_readl(dwc, DAR); +} +EXPORT_SYMBOL(dw_dma_get_dst_addr); + +/* called with dwc->lock held and all DMAC interrupts disabled */ +static void dwc_handle_cyclic(struct dw_dma *dw, struct dw_dma_chan *dwc, + u32 status_block, u32 status_err, u32 status_xfer) +{ + if (status_block & dwc->mask) { + void (*callback)(void *param); + void *callback_param; + + dev_vdbg(chan2dev(&dwc->chan), "new cyclic period llp 0x%08x\n", + channel_readl(dwc, LLP)); + dma_writel(dw, CLEAR.BLOCK, dwc->mask); + + callback = dwc->cdesc->period_callback; + callback_param = dwc->cdesc->period_callback_param; + if (callback) { + spin_unlock(&dwc->lock); + callback(callback_param); + spin_lock(&dwc->lock); + } + } + + /* + * Error and transfer complete are highly unlikely, and will most + * likely be due to a configuration error by the user. + */ + if (unlikely(status_err & dwc->mask) || + unlikely(status_xfer & dwc->mask)) { + int i; + + dev_err(chan2dev(&dwc->chan), "cyclic DMA unexpected %s " + "interrupt, stopping DMA transfer\n", + status_xfer ? "xfer" : "error"); + dev_err(chan2dev(&dwc->chan), + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); + + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + /* make sure DMA does not restart by loading a new list */ + channel_writel(dwc, LLP, 0); + channel_writel(dwc, CTL_LO, 0); + channel_writel(dwc, CTL_HI, 0); + + dma_writel(dw, CLEAR.BLOCK, dwc->mask); + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + for (i = 0; i < dwc->cdesc->periods; i++) + dwc_dump_lli(dwc, &dwc->cdesc->desc[i]->lli); + } +} + +/* ------------------------------------------------------------------------- */ + static void dw_dma_tasklet(unsigned long data) { struct dw_dma *dw = (struct dw_dma *)data; @@ -382,7 +458,10 @@ static void dw_dma_tasklet(unsigned long data) for (i = 0; i < dw->dma.chancnt; i++) { dwc = &dw->chan[i]; spin_lock(&dwc->lock); - if (status_err & (1 << i)) + if (test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) + dwc_handle_cyclic(dw, dwc, status_block, status_err, + status_xfer); + else if (status_err & (1 << i)) dwc_handle_error(dw, dwc); else if ((status_block | status_xfer) & (1 << i)) dwc_scan_descriptors(dw, dwc); @@ -826,7 +905,6 @@ static int dwc_alloc_chan_resources(struct dma_chan *chan) dma_async_tx_descriptor_init(&desc->txd, chan); desc->txd.tx_submit = dwc_tx_submit; desc->txd.flags = DMA_CTRL_ACK; - INIT_LIST_HEAD(&desc->txd.tx_list); desc->txd.phys = dma_map_single(chan2parent(chan), &desc->lli, sizeof(desc->lli), DMA_TO_DEVICE); dwc_desc_put(dwc, desc); @@ -884,6 +962,257 @@ static void dwc_free_chan_resources(struct dma_chan *chan) dev_vdbg(chan2dev(chan), "free_chan_resources done\n"); } +/* --------------------- Cyclic DMA API extensions -------------------- */ + +/** + * dw_dma_cyclic_start - start the cyclic DMA transfer + * @chan: the DMA channel to start + * + * Must be called with soft interrupts disabled. Returns zero on success or + * -errno on failure. + */ +int dw_dma_cyclic_start(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + + if (!test_bit(DW_DMA_IS_CYCLIC, &dwc->flags)) { + dev_err(chan2dev(&dwc->chan), "missing prep for cyclic DMA\n"); + return -ENODEV; + } + + spin_lock(&dwc->lock); + + /* assert channel is idle */ + if (dma_readl(dw, CH_EN) & dwc->mask) { + dev_err(chan2dev(&dwc->chan), + "BUG: Attempted to start non-idle channel\n"); + dev_err(chan2dev(&dwc->chan), + " SAR: 0x%x DAR: 0x%x LLP: 0x%x CTL: 0x%x:%08x\n", + channel_readl(dwc, SAR), + channel_readl(dwc, DAR), + channel_readl(dwc, LLP), + channel_readl(dwc, CTL_HI), + channel_readl(dwc, CTL_LO)); + spin_unlock(&dwc->lock); + return -EBUSY; + } + + dma_writel(dw, CLEAR.BLOCK, dwc->mask); + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + /* setup DMAC channel registers */ + channel_writel(dwc, LLP, dwc->cdesc->desc[0]->txd.phys); + channel_writel(dwc, CTL_LO, DWC_CTLL_LLP_D_EN | DWC_CTLL_LLP_S_EN); + channel_writel(dwc, CTL_HI, 0); + + channel_set_bit(dw, CH_EN, dwc->mask); + + spin_unlock(&dwc->lock); + + return 0; +} +EXPORT_SYMBOL(dw_dma_cyclic_start); + +/** + * dw_dma_cyclic_stop - stop the cyclic DMA transfer + * @chan: the DMA channel to stop + * + * Must be called with soft interrupts disabled. + */ +void dw_dma_cyclic_stop(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + + spin_lock(&dwc->lock); + + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + spin_unlock(&dwc->lock); +} +EXPORT_SYMBOL(dw_dma_cyclic_stop); + +/** + * dw_dma_cyclic_prep - prepare the cyclic DMA transfer + * @chan: the DMA channel to prepare + * @buf_addr: physical DMA address where the buffer starts + * @buf_len: total number of bytes for the entire buffer + * @period_len: number of bytes for each period + * @direction: transfer direction, to or from device + * + * Must be called before trying to start the transfer. Returns a valid struct + * dw_cyclic_desc if successful or an ERR_PTR(-errno) if not successful. + */ +struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, + dma_addr_t buf_addr, size_t buf_len, size_t period_len, + enum dma_data_direction direction) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_cyclic_desc *cdesc; + struct dw_cyclic_desc *retval = NULL; + struct dw_desc *desc; + struct dw_desc *last = NULL; + struct dw_dma_slave *dws = chan->private; + unsigned long was_cyclic; + unsigned int reg_width; + unsigned int periods; + unsigned int i; + + spin_lock_bh(&dwc->lock); + if (!list_empty(&dwc->queue) || !list_empty(&dwc->active_list)) { + spin_unlock_bh(&dwc->lock); + dev_dbg(chan2dev(&dwc->chan), + "queue and/or active list are not empty\n"); + return ERR_PTR(-EBUSY); + } + + was_cyclic = test_and_set_bit(DW_DMA_IS_CYCLIC, &dwc->flags); + spin_unlock_bh(&dwc->lock); + if (was_cyclic) { + dev_dbg(chan2dev(&dwc->chan), + "channel already prepared for cyclic DMA\n"); + return ERR_PTR(-EBUSY); + } + + retval = ERR_PTR(-EINVAL); + reg_width = dws->reg_width; + periods = buf_len / period_len; + + /* Check for too big/unaligned periods and unaligned DMA buffer. */ + if (period_len > (DWC_MAX_COUNT << reg_width)) + goto out_err; + if (unlikely(period_len & ((1 << reg_width) - 1))) + goto out_err; + if (unlikely(buf_addr & ((1 << reg_width) - 1))) + goto out_err; + if (unlikely(!(direction & (DMA_TO_DEVICE | DMA_FROM_DEVICE)))) + goto out_err; + + retval = ERR_PTR(-ENOMEM); + + if (periods > NR_DESCS_PER_CHANNEL) + goto out_err; + + cdesc = kzalloc(sizeof(struct dw_cyclic_desc), GFP_KERNEL); + if (!cdesc) + goto out_err; + + cdesc->desc = kzalloc(sizeof(struct dw_desc *) * periods, GFP_KERNEL); + if (!cdesc->desc) + goto out_err_alloc; + + for (i = 0; i < periods; i++) { + desc = dwc_desc_get(dwc); + if (!desc) + goto out_err_desc_get; + + switch (direction) { + case DMA_TO_DEVICE: + desc->lli.dar = dws->tx_reg; + desc->lli.sar = buf_addr + (period_len * i); + desc->lli.ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_FIX + | DWC_CTLL_SRC_INC + | DWC_CTLL_FC_M2P + | DWC_CTLL_INT_EN); + break; + case DMA_FROM_DEVICE: + desc->lli.dar = buf_addr + (period_len * i); + desc->lli.sar = dws->rx_reg; + desc->lli.ctllo = (DWC_DEFAULT_CTLLO + | DWC_CTLL_SRC_WIDTH(reg_width) + | DWC_CTLL_DST_WIDTH(reg_width) + | DWC_CTLL_DST_INC + | DWC_CTLL_SRC_FIX + | DWC_CTLL_FC_P2M + | DWC_CTLL_INT_EN); + break; + default: + break; + } + + desc->lli.ctlhi = (period_len >> reg_width); + cdesc->desc[i] = desc; + + if (last) { + last->lli.llp = desc->txd.phys; + dma_sync_single_for_device(chan2parent(chan), + last->txd.phys, sizeof(last->lli), + DMA_TO_DEVICE); + } + + last = desc; + } + + /* lets make a cyclic list */ + last->lli.llp = cdesc->desc[0]->txd.phys; + dma_sync_single_for_device(chan2parent(chan), last->txd.phys, + sizeof(last->lli), DMA_TO_DEVICE); + + dev_dbg(chan2dev(&dwc->chan), "cyclic prepared buf 0x%08x len %zu " + "period %zu periods %d\n", buf_addr, buf_len, + period_len, periods); + + cdesc->periods = periods; + dwc->cdesc = cdesc; + + return cdesc; + +out_err_desc_get: + while (i--) + dwc_desc_put(dwc, cdesc->desc[i]); +out_err_alloc: + kfree(cdesc); +out_err: + clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); + return (struct dw_cyclic_desc *)retval; +} +EXPORT_SYMBOL(dw_dma_cyclic_prep); + +/** + * dw_dma_cyclic_free - free a prepared cyclic DMA transfer + * @chan: the DMA channel to free + */ +void dw_dma_cyclic_free(struct dma_chan *chan) +{ + struct dw_dma_chan *dwc = to_dw_dma_chan(chan); + struct dw_dma *dw = to_dw_dma(dwc->chan.device); + struct dw_cyclic_desc *cdesc = dwc->cdesc; + int i; + + dev_dbg(chan2dev(&dwc->chan), "cyclic free\n"); + + if (!cdesc) + return; + + spin_lock_bh(&dwc->lock); + + channel_clear_bit(dw, CH_EN, dwc->mask); + while (dma_readl(dw, CH_EN) & dwc->mask) + cpu_relax(); + + dma_writel(dw, CLEAR.BLOCK, dwc->mask); + dma_writel(dw, CLEAR.ERROR, dwc->mask); + dma_writel(dw, CLEAR.XFER, dwc->mask); + + spin_unlock_bh(&dwc->lock); + + for (i = 0; i < cdesc->periods; i++) + dwc_desc_put(dwc, cdesc->desc[i]); + + kfree(cdesc->desc); + kfree(cdesc); + + clear_bit(DW_DMA_IS_CYCLIC, &dwc->flags); +} +EXPORT_SYMBOL(dw_dma_cyclic_free); + /*----------------------------------------------------------------------*/ static void dw_dma_off(struct dw_dma *dw) diff --git a/drivers/dma/dw_dmac_regs.h b/drivers/dma/dw_dmac_regs.h index b252b202c5c..13a58076703 100644 --- a/drivers/dma/dw_dmac_regs.h +++ b/drivers/dma/dw_dmac_regs.h @@ -126,6 +126,10 @@ struct dw_dma_regs { #define DW_REGLEN 0x400 +enum dw_dmac_flags { + DW_DMA_IS_CYCLIC = 0, +}; + struct dw_dma_chan { struct dma_chan chan; void __iomem *ch_regs; @@ -134,10 +138,12 @@ struct dw_dma_chan { spinlock_t lock; /* these other elements are all protected by lock */ + unsigned long flags; dma_cookie_t completed; struct list_head active_list; struct list_head queue; struct list_head free_list; + struct dw_cyclic_desc *cdesc; unsigned int descs_allocated; }; @@ -158,7 +164,6 @@ static inline struct dw_dma_chan *to_dw_dma_chan(struct dma_chan *chan) return container_of(chan, struct dw_dma_chan, chan); } - struct dw_dma { struct dma_device dma; void __iomem *regs; diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 86d6da47f55..da8a8ed9e41 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -354,7 +354,6 @@ static struct fsl_desc_sw *fsl_dma_alloc_descriptor( dma_async_tx_descriptor_init(&desc_sw->async_tx, &fsl_chan->common); desc_sw->async_tx.tx_submit = fsl_dma_tx_submit; - INIT_LIST_HEAD(&desc_sw->async_tx.tx_list); desc_sw->async_tx.phys = pdesc; } diff --git a/drivers/dma/ioat_dma.c b/drivers/dma/ioat_dma.c index 5905cd36bcd..e4fc33c1c32 100644 --- a/drivers/dma/ioat_dma.c +++ b/drivers/dma/ioat_dma.c @@ -693,7 +693,6 @@ static struct ioat_desc_sw *ioat_dma_alloc_descriptor( desc_sw->async_tx.tx_submit = ioat2_tx_submit; break; } - INIT_LIST_HEAD(&desc_sw->async_tx.tx_list); desc_sw->hw = desc; desc_sw->async_tx.phys = phys; diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 16adbe61cfb..2f052265122 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -498,7 +498,6 @@ static int iop_adma_alloc_chan_resources(struct dma_chan *chan) slot->async_tx.tx_submit = iop_adma_tx_submit; INIT_LIST_HEAD(&slot->chain_node); INIT_LIST_HEAD(&slot->slot_node); - INIT_LIST_HEAD(&slot->async_tx.tx_list); hw_desc = (char *) iop_chan->device->dma_desc_pool; slot->async_tx.phys = (dma_addr_t) &hw_desc[idx * IOP_ADMA_SLOT_SIZE]; diff --git a/drivers/dma/ipu/ipu_idmac.c b/drivers/dma/ipu/ipu_idmac.c index da781d10789..e202a6ce557 100644 --- a/drivers/dma/ipu/ipu_idmac.c +++ b/drivers/dma/ipu/ipu_idmac.c @@ -28,6 +28,9 @@ #define FS_VF_IN_VALID 0x00000002 #define FS_ENC_IN_VALID 0x00000001 +static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, + bool wait_for_stop); + /* * There can be only one, we could allocate it dynamically, but then we'd have * to add an extra parameter to some functions, and use something as ugly as @@ -107,7 +110,7 @@ static uint32_t bytes_per_pixel(enum pixel_fmt fmt) } } -/* Enable / disable direct write to memory by the Camera Sensor Interface */ +/* Enable direct write to memory by the Camera Sensor Interface */ static void ipu_ic_enable_task(struct ipu *ipu, enum ipu_channel channel) { uint32_t ic_conf, mask; @@ -126,6 +129,7 @@ static void ipu_ic_enable_task(struct ipu *ipu, enum ipu_channel channel) idmac_write_icreg(ipu, ic_conf, IC_CONF); } +/* Called under spin_lock_irqsave(&ipu_data.lock) */ static void ipu_ic_disable_task(struct ipu *ipu, enum ipu_channel channel) { uint32_t ic_conf, mask; @@ -422,7 +426,7 @@ static void ipu_ch_param_set_size(union chan_param_mem *params, break; default: dev_err(ipu_data.dev, - "mxc ipu: unimplemented pixel format %d\n", pixel_fmt); + "mx3 ipu: unimplemented pixel format %d\n", pixel_fmt); break; } @@ -433,20 +437,20 @@ static void ipu_ch_param_set_burst_size(union chan_param_mem *params, uint16_t burst_pixels) { params->pp.npb = burst_pixels - 1; -}; +} static void ipu_ch_param_set_buffer(union chan_param_mem *params, dma_addr_t buf0, dma_addr_t buf1) { params->pp.eba0 = buf0; params->pp.eba1 = buf1; -}; +} static void ipu_ch_param_set_rotation(union chan_param_mem *params, enum ipu_rotate_mode rotate) { params->pp.bam = rotate; -}; +} static void ipu_write_param_mem(uint32_t addr, uint32_t *data, uint32_t num_words) @@ -571,7 +575,7 @@ static uint32_t dma_param_addr(uint32_t dma_ch) { /* Channel Parameter Memory */ return 0x10000 | (dma_ch << 4); -}; +} static void ipu_channel_set_priority(struct ipu *ipu, enum ipu_channel channel, bool prio) @@ -611,7 +615,8 @@ static uint32_t ipu_channel_conf_mask(enum ipu_channel channel) /** * ipu_enable_channel() - enable an IPU channel. - * @channel: channel ID. + * @idmac: IPU DMAC context. + * @ichan: IDMAC channel. * @return: 0 on success or negative error code on failure. */ static int ipu_enable_channel(struct idmac *idmac, struct idmac_channel *ichan) @@ -649,7 +654,7 @@ static int ipu_enable_channel(struct idmac *idmac, struct idmac_channel *ichan) /** * ipu_init_channel_buffer() - initialize a buffer for logical IPU channel. - * @channel: channel ID. + * @ichan: IDMAC channel. * @pixel_fmt: pixel format of buffer. Pixel format is a FOURCC ASCII code. * @width: width of buffer in pixels. * @height: height of buffer in pixels. @@ -687,7 +692,7 @@ static int ipu_init_channel_buffer(struct idmac_channel *ichan, } /* IC channel's stride must be a multiple of 8 pixels */ - if ((channel <= 13) && (stride % 8)) { + if ((channel <= IDMAC_IC_13) && (stride % 8)) { dev_err(ipu->dev, "Stride must be 8 pixel multiple\n"); return -EINVAL; } @@ -752,7 +757,7 @@ static void ipu_select_buffer(enum ipu_channel channel, int buffer_n) /** * ipu_update_channel_buffer() - update physical address of a channel buffer. - * @channel: channel ID. + * @ichan: IDMAC channel. * @buffer_n: buffer number to update. * 0 or 1 are the only valid values. * @phyaddr: buffer physical address. @@ -760,9 +765,10 @@ static void ipu_select_buffer(enum ipu_channel channel, int buffer_n) * function will fail if the buffer is set to ready. */ /* Called under spin_lock(_irqsave)(&ichan->lock) */ -static int ipu_update_channel_buffer(enum ipu_channel channel, +static int ipu_update_channel_buffer(struct idmac_channel *ichan, int buffer_n, dma_addr_t phyaddr) { + enum ipu_channel channel = ichan->dma_chan.chan_id; uint32_t reg; unsigned long flags; @@ -771,8 +777,8 @@ static int ipu_update_channel_buffer(enum ipu_channel channel, if (buffer_n == 0) { reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); if (reg & (1UL << channel)) { - spin_unlock_irqrestore(&ipu_data.lock, flags); - return -EACCES; + ipu_ic_disable_task(&ipu_data, channel); + ichan->status = IPU_CHANNEL_READY; } /* 44.3.3.1.9 - Row Number 1 (WORD1, offset 0) */ @@ -782,8 +788,8 @@ static int ipu_update_channel_buffer(enum ipu_channel channel, } else { reg = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); if (reg & (1UL << channel)) { - spin_unlock_irqrestore(&ipu_data.lock, flags); - return -EACCES; + ipu_ic_disable_task(&ipu_data, channel); + ichan->status = IPU_CHANNEL_READY; } /* Check if double-buffering is already enabled */ @@ -805,6 +811,39 @@ static int ipu_update_channel_buffer(enum ipu_channel channel, } /* Called under spin_lock_irqsave(&ichan->lock) */ +static int ipu_submit_buffer(struct idmac_channel *ichan, + struct idmac_tx_desc *desc, struct scatterlist *sg, int buf_idx) +{ + unsigned int chan_id = ichan->dma_chan.chan_id; + struct device *dev = &ichan->dma_chan.dev->device; + int ret; + + if (async_tx_test_ack(&desc->txd)) + return -EINTR; + + /* + * On first invocation this shouldn't be necessary, the call to + * ipu_init_channel_buffer() above will set addresses for us, so we + * could make it conditional on status >= IPU_CHANNEL_ENABLED, but + * doing it again shouldn't hurt either. + */ + ret = ipu_update_channel_buffer(ichan, buf_idx, + sg_dma_address(sg)); + + if (ret < 0) { + dev_err(dev, "Updating sg %p on channel 0x%x buffer %d failed!\n", + sg, chan_id, buf_idx); + return ret; + } + + ipu_select_buffer(chan_id, buf_idx); + dev_dbg(dev, "Updated sg %p on channel 0x%x buffer %d\n", + sg, chan_id, buf_idx); + + return 0; +} + +/* Called under spin_lock_irqsave(&ichan->lock) */ static int ipu_submit_channel_buffers(struct idmac_channel *ichan, struct idmac_tx_desc *desc) { @@ -815,20 +854,10 @@ static int ipu_submit_channel_buffers(struct idmac_channel *ichan, if (!ichan->sg[i]) { ichan->sg[i] = sg; - /* - * On first invocation this shouldn't be necessary, the - * call to ipu_init_channel_buffer() above will set - * addresses for us, so we could make it conditional - * on status >= IPU_CHANNEL_ENABLED, but doing it again - * shouldn't hurt either. - */ - ret = ipu_update_channel_buffer(ichan->dma_chan.chan_id, i, - sg_dma_address(sg)); + ret = ipu_submit_buffer(ichan, desc, sg, i); if (ret < 0) return ret; - ipu_select_buffer(ichan->dma_chan.chan_id, i); - sg = sg_next(sg); } } @@ -842,19 +871,22 @@ static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) struct idmac_channel *ichan = to_idmac_chan(tx->chan); struct idmac *idmac = to_idmac(tx->chan->device); struct ipu *ipu = to_ipu(idmac); + struct device *dev = &ichan->dma_chan.dev->device; dma_cookie_t cookie; unsigned long flags; + int ret; /* Sanity check */ if (!list_empty(&desc->list)) { /* The descriptor doesn't belong to client */ - dev_err(&ichan->dma_chan.dev->device, - "Descriptor %p not prepared!\n", tx); + dev_err(dev, "Descriptor %p not prepared!\n", tx); return -EBUSY; } mutex_lock(&ichan->chan_mutex); + async_tx_clear_ack(tx); + if (ichan->status < IPU_CHANNEL_READY) { struct idmac_video_param *video = &ichan->params.video; /* @@ -878,16 +910,7 @@ static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) goto out; } - /* ipu->lock can be taken under ichan->lock, but not v.v. */ - spin_lock_irqsave(&ichan->lock, flags); - - /* submit_buffers() atomically verifies and fills empty sg slots */ - cookie = ipu_submit_channel_buffers(ichan, desc); - - spin_unlock_irqrestore(&ichan->lock, flags); - - if (cookie < 0) - goto out; + dev_dbg(dev, "Submitting sg %p\n", &desc->sg[0]); cookie = ichan->dma_chan.cookie; @@ -897,24 +920,40 @@ static dma_cookie_t idmac_tx_submit(struct dma_async_tx_descriptor *tx) /* from dmaengine.h: "last cookie value returned to client" */ ichan->dma_chan.cookie = cookie; tx->cookie = cookie; + + /* ipu->lock can be taken under ichan->lock, but not v.v. */ spin_lock_irqsave(&ichan->lock, flags); + list_add_tail(&desc->list, &ichan->queue); + /* submit_buffers() atomically verifies and fills empty sg slots */ + ret = ipu_submit_channel_buffers(ichan, desc); + spin_unlock_irqrestore(&ichan->lock, flags); + if (ret < 0) { + cookie = ret; + goto dequeue; + } + if (ichan->status < IPU_CHANNEL_ENABLED) { - int ret = ipu_enable_channel(idmac, ichan); + ret = ipu_enable_channel(idmac, ichan); if (ret < 0) { cookie = ret; - spin_lock_irqsave(&ichan->lock, flags); - list_del_init(&desc->list); - spin_unlock_irqrestore(&ichan->lock, flags); - tx->cookie = cookie; - ichan->dma_chan.cookie = cookie; + goto dequeue; } } dump_idmac_reg(ipu); +dequeue: + if (cookie < 0) { + spin_lock_irqsave(&ichan->lock, flags); + list_del_init(&desc->list); + spin_unlock_irqrestore(&ichan->lock, flags); + tx->cookie = cookie; + ichan->dma_chan.cookie = cookie; + } + out: mutex_unlock(&ichan->chan_mutex); @@ -944,8 +983,6 @@ static int idmac_desc_alloc(struct idmac_channel *ichan, int n) memset(txd, 0, sizeof(*txd)); dma_async_tx_descriptor_init(txd, &ichan->dma_chan); txd->tx_submit = idmac_tx_submit; - txd->chan = &ichan->dma_chan; - INIT_LIST_HEAD(&txd->tx_list); list_add(&desc->list, &ichan->free_list); @@ -1161,6 +1198,24 @@ static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, return 0; } +static struct scatterlist *idmac_sg_next(struct idmac_channel *ichan, + struct idmac_tx_desc **desc, struct scatterlist *sg) +{ + struct scatterlist *sgnew = sg ? sg_next(sg) : NULL; + + if (sgnew) + /* next sg-element in this list */ + return sgnew; + + if ((*desc)->list.next == &ichan->queue) + /* No more descriptors on the queue */ + return NULL; + + /* Fetch next descriptor */ + *desc = list_entry((*desc)->list.next, struct idmac_tx_desc, list); + return (*desc)->sg; +} + /* * We have several possibilities here: * current BUF next BUF @@ -1176,23 +1231,46 @@ static int ipu_disable_channel(struct idmac *idmac, struct idmac_channel *ichan, static irqreturn_t idmac_interrupt(int irq, void *dev_id) { struct idmac_channel *ichan = dev_id; + struct device *dev = &ichan->dma_chan.dev->device; unsigned int chan_id = ichan->dma_chan.chan_id; struct scatterlist **sg, *sgnext, *sgnew = NULL; /* Next transfer descriptor */ - struct idmac_tx_desc *desc = NULL, *descnew; + struct idmac_tx_desc *desc, *descnew; dma_async_tx_callback callback; void *callback_param; bool done = false; - u32 ready0 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY), - ready1 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY), - curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); + u32 ready0, ready1, curbuf, err; + unsigned long flags; /* IDMAC has cleared the respective BUFx_RDY bit, we manage the buffer */ - pr_debug("IDMAC irq %d\n", irq); + dev_dbg(dev, "IDMAC irq %d, buf %d\n", irq, ichan->active_buffer); + + spin_lock_irqsave(&ipu_data.lock, flags); + + ready0 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF0_RDY); + ready1 = idmac_read_ipureg(&ipu_data, IPU_CHA_BUF1_RDY); + curbuf = idmac_read_ipureg(&ipu_data, IPU_CHA_CUR_BUF); + err = idmac_read_ipureg(&ipu_data, IPU_INT_STAT_4); + + if (err & (1 << chan_id)) { + idmac_write_ipureg(&ipu_data, 1 << chan_id, IPU_INT_STAT_4); + spin_unlock_irqrestore(&ipu_data.lock, flags); + /* + * Doing this + * ichan->sg[0] = ichan->sg[1] = NULL; + * you can force channel re-enable on the next tx_submit(), but + * this is dirty - think about descriptors with multiple + * sg elements. + */ + dev_warn(dev, "NFB4EOF on channel %d, ready %x, %x, cur %x\n", + chan_id, ready0, ready1, curbuf); + return IRQ_HANDLED; + } + spin_unlock_irqrestore(&ipu_data.lock, flags); + /* Other interrupts do not interfere with this channel */ spin_lock(&ichan->lock); - if (unlikely(chan_id != IDMAC_SDC_0 && chan_id != IDMAC_SDC_1 && ((curbuf >> chan_id) & 1) == ichan->active_buffer)) { int i = 100; @@ -1207,19 +1285,23 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) if (!i) { spin_unlock(&ichan->lock); - dev_dbg(ichan->dma_chan.device->dev, + dev_dbg(dev, "IRQ on active buffer on channel %x, active " "%d, ready %x, %x, current %x!\n", chan_id, ichan->active_buffer, ready0, ready1, curbuf); return IRQ_NONE; - } + } else + dev_dbg(dev, + "Buffer deactivated on channel %x, active " + "%d, ready %x, %x, current %x, rest %d!\n", chan_id, + ichan->active_buffer, ready0, ready1, curbuf, i); } if (unlikely((ichan->active_buffer && (ready1 >> chan_id) & 1) || (!ichan->active_buffer && (ready0 >> chan_id) & 1) )) { spin_unlock(&ichan->lock); - dev_dbg(ichan->dma_chan.device->dev, + dev_dbg(dev, "IRQ with active buffer still ready on channel %x, " "active %d, ready %x, %x!\n", chan_id, ichan->active_buffer, ready0, ready1); @@ -1227,8 +1309,9 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) } if (unlikely(list_empty(&ichan->queue))) { + ichan->sg[ichan->active_buffer] = NULL; spin_unlock(&ichan->lock); - dev_err(ichan->dma_chan.device->dev, + dev_err(dev, "IRQ without queued buffers on channel %x, active %d, " "ready %x, %x!\n", chan_id, ichan->active_buffer, ready0, ready1); @@ -1243,40 +1326,44 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) sg = &ichan->sg[ichan->active_buffer]; sgnext = ichan->sg[!ichan->active_buffer]; + if (!*sg) { + spin_unlock(&ichan->lock); + return IRQ_HANDLED; + } + + desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list); + descnew = desc; + + dev_dbg(dev, "IDMAC irq %d, dma 0x%08x, next dma 0x%08x, current %d, curbuf 0x%08x\n", + irq, sg_dma_address(*sg), sgnext ? sg_dma_address(sgnext) : 0, ichan->active_buffer, curbuf); + + /* Find the descriptor of sgnext */ + sgnew = idmac_sg_next(ichan, &descnew, *sg); + if (sgnext != sgnew) + dev_err(dev, "Submitted buffer %p, next buffer %p\n", sgnext, sgnew); + /* * if sgnext == NULL sg must be the last element in a scatterlist and * queue must be empty */ if (unlikely(!sgnext)) { - if (unlikely(sg_next(*sg))) { - dev_err(ichan->dma_chan.device->dev, - "Broken buffer-update locking on channel %x!\n", - chan_id); - /* We'll let the user catch up */ + if (!WARN_ON(sg_next(*sg))) + dev_dbg(dev, "Underrun on channel %x\n", chan_id); + ichan->sg[!ichan->active_buffer] = sgnew; + + if (unlikely(sgnew)) { + ipu_submit_buffer(ichan, descnew, sgnew, !ichan->active_buffer); } else { - /* Underrun */ + spin_lock_irqsave(&ipu_data.lock, flags); ipu_ic_disable_task(&ipu_data, chan_id); - dev_dbg(ichan->dma_chan.device->dev, - "Underrun on channel %x\n", chan_id); + spin_unlock_irqrestore(&ipu_data.lock, flags); ichan->status = IPU_CHANNEL_READY; /* Continue to check for complete descriptor */ } } - desc = list_entry(ichan->queue.next, struct idmac_tx_desc, list); - - /* First calculate and submit the next sg element */ - if (likely(sgnext)) - sgnew = sg_next(sgnext); - - if (unlikely(!sgnew)) { - /* Start a new scatterlist, if any queued */ - if (likely(desc->list.next != &ichan->queue)) { - descnew = list_entry(desc->list.next, - struct idmac_tx_desc, list); - sgnew = &descnew->sg[0]; - } - } + /* Calculate and submit the next sg element */ + sgnew = idmac_sg_next(ichan, &descnew, sgnew); if (unlikely(!sg_next(*sg)) || !sgnext) { /* @@ -1289,17 +1376,13 @@ static irqreturn_t idmac_interrupt(int irq, void *dev_id) *sg = sgnew; - if (likely(sgnew)) { - int ret; - - ret = ipu_update_channel_buffer(chan_id, ichan->active_buffer, - sg_dma_address(*sg)); - if (ret < 0) - dev_err(ichan->dma_chan.device->dev, - "Failed to update buffer on channel %x buffer %d!\n", - chan_id, ichan->active_buffer); - else - ipu_select_buffer(chan_id, ichan->active_buffer); + if (likely(sgnew) && + ipu_submit_buffer(ichan, descnew, sgnew, ichan->active_buffer) < 0) { + callback = desc->txd.callback; + callback_param = desc->txd.callback_param; + spin_unlock(&ichan->lock); + callback(callback_param); + spin_lock(&ichan->lock); } /* Flip the active buffer - even if update above failed */ @@ -1327,13 +1410,20 @@ static void ipu_gc_tasklet(unsigned long arg) struct idmac_channel *ichan = ipu->channel + i; struct idmac_tx_desc *desc; unsigned long flags; - int j; + struct scatterlist *sg; + int j, k; for (j = 0; j < ichan->n_tx_desc; j++) { desc = ichan->desc + j; spin_lock_irqsave(&ichan->lock, flags); if (async_tx_test_ack(&desc->txd)) { list_move(&desc->list, &ichan->free_list); + for_each_sg(desc->sg, sg, desc->sg_len, k) { + if (ichan->sg[0] == sg) + ichan->sg[0] = NULL; + else if (ichan->sg[1] == sg) + ichan->sg[1] = NULL; + } async_tx_clear_ack(&desc->txd); } spin_unlock_irqrestore(&ichan->lock, flags); @@ -1341,13 +1431,7 @@ static void ipu_gc_tasklet(unsigned long arg) } } -/* - * At the time .device_alloc_chan_resources() method is called, we cannot know, - * whether the client will accept the channel. Thus we must only check, if we - * can satisfy client's request but the only real criterion to verify, whether - * the client has accepted our offer is the client_count. That's why we have to - * perform the rest of our allocation tasks on the first call to this function. - */ +/* Allocate and initialise a transfer descriptor. */ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len, enum dma_data_direction direction, unsigned long tx_flags) @@ -1358,8 +1442,8 @@ static struct dma_async_tx_descriptor *idmac_prep_slave_sg(struct dma_chan *chan unsigned long flags; /* We only can handle these three channels so far */ - if (ichan->dma_chan.chan_id != IDMAC_SDC_0 && ichan->dma_chan.chan_id != IDMAC_SDC_1 && - ichan->dma_chan.chan_id != IDMAC_IC_7) + if (chan->chan_id != IDMAC_SDC_0 && chan->chan_id != IDMAC_SDC_1 && + chan->chan_id != IDMAC_IC_7) return NULL; if (direction != DMA_FROM_DEVICE && direction != DMA_TO_DEVICE) { @@ -1400,7 +1484,7 @@ static void idmac_issue_pending(struct dma_chan *chan) /* This is not always needed, but doesn't hurt either */ spin_lock_irqsave(&ipu->lock, flags); - ipu_select_buffer(ichan->dma_chan.chan_id, ichan->active_buffer); + ipu_select_buffer(chan->chan_id, ichan->active_buffer); spin_unlock_irqrestore(&ipu->lock, flags); /* @@ -1432,8 +1516,7 @@ static void __idmac_terminate_all(struct dma_chan *chan) struct idmac_tx_desc *desc = ichan->desc + i; if (list_empty(&desc->list)) /* Descriptor was prepared, but not submitted */ - list_add(&desc->list, - &ichan->free_list); + list_add(&desc->list, &ichan->free_list); async_tx_clear_ack(&desc->txd); } @@ -1458,6 +1541,28 @@ static void idmac_terminate_all(struct dma_chan *chan) mutex_unlock(&ichan->chan_mutex); } +#ifdef DEBUG +static irqreturn_t ic_sof_irq(int irq, void *dev_id) +{ + struct idmac_channel *ichan = dev_id; + printk(KERN_DEBUG "Got SOF IRQ %d on Channel %d\n", + irq, ichan->dma_chan.chan_id); + disable_irq(irq); + return IRQ_HANDLED; +} + +static irqreturn_t ic_eof_irq(int irq, void *dev_id) +{ + struct idmac_channel *ichan = dev_id; + printk(KERN_DEBUG "Got EOF IRQ %d on Channel %d\n", + irq, ichan->dma_chan.chan_id); + disable_irq(irq); + return IRQ_HANDLED; +} + +static int ic_sof = -EINVAL, ic_eof = -EINVAL; +#endif + static int idmac_alloc_chan_resources(struct dma_chan *chan) { struct idmac_channel *ichan = to_idmac_chan(chan); @@ -1471,31 +1576,49 @@ static int idmac_alloc_chan_resources(struct dma_chan *chan) chan->cookie = 1; ichan->completed = -ENXIO; - ret = ipu_irq_map(ichan->dma_chan.chan_id); + ret = ipu_irq_map(chan->chan_id); if (ret < 0) goto eimap; ichan->eof_irq = ret; + + /* + * Important to first disable the channel, because maybe someone + * used it before us, e.g., the bootloader + */ + ipu_disable_channel(idmac, ichan, true); + + ret = ipu_init_channel(idmac, ichan); + if (ret < 0) + goto eichan; + ret = request_irq(ichan->eof_irq, idmac_interrupt, 0, ichan->eof_name, ichan); if (ret < 0) goto erirq; - ret = ipu_init_channel(idmac, ichan); - if (ret < 0) - goto eichan; +#ifdef DEBUG + if (chan->chan_id == IDMAC_IC_7) { + ic_sof = ipu_irq_map(69); + if (ic_sof > 0) + request_irq(ic_sof, ic_sof_irq, 0, "IC SOF", ichan); + ic_eof = ipu_irq_map(70); + if (ic_eof > 0) + request_irq(ic_eof, ic_eof_irq, 0, "IC EOF", ichan); + } +#endif ichan->status = IPU_CHANNEL_INITIALIZED; - dev_dbg(&ichan->dma_chan.dev->device, "Found channel 0x%x, irq %d\n", - ichan->dma_chan.chan_id, ichan->eof_irq); + dev_dbg(&chan->dev->device, "Found channel 0x%x, irq %d\n", + chan->chan_id, ichan->eof_irq); return ret; -eichan: - free_irq(ichan->eof_irq, ichan); erirq: - ipu_irq_unmap(ichan->dma_chan.chan_id); + ipu_uninit_channel(idmac, ichan); +eichan: + ipu_irq_unmap(chan->chan_id); eimap: return ret; } @@ -1510,8 +1633,22 @@ static void idmac_free_chan_resources(struct dma_chan *chan) __idmac_terminate_all(chan); if (ichan->status > IPU_CHANNEL_FREE) { +#ifdef DEBUG + if (chan->chan_id == IDMAC_IC_7) { + if (ic_sof > 0) { + free_irq(ic_sof, ichan); + ipu_irq_unmap(69); + ic_sof = -EINVAL; + } + if (ic_eof > 0) { + free_irq(ic_eof, ichan); + ipu_irq_unmap(70); + ic_eof = -EINVAL; + } + } +#endif free_irq(ichan->eof_irq, ichan); - ipu_irq_unmap(ichan->dma_chan.chan_id); + ipu_irq_unmap(chan->chan_id); } ichan->status = IPU_CHANNEL_FREE; @@ -1573,7 +1710,7 @@ static int __init ipu_idmac_init(struct ipu *ipu) dma_chan->device = &idmac->dma; dma_chan->cookie = 1; dma_chan->chan_id = i; - list_add_tail(&ichan->dma_chan.device_node, &dma->channels); + list_add_tail(&dma_chan->device_node, &dma->channels); } idmac_write_icreg(ipu, 0x00000070, IDMAC_CONF); @@ -1581,7 +1718,7 @@ static int __init ipu_idmac_init(struct ipu *ipu) return dma_async_device_register(&idmac->dma); } -static void ipu_idmac_exit(struct ipu *ipu) +static void __exit ipu_idmac_exit(struct ipu *ipu) { int i; struct idmac *idmac = &ipu->idmac; @@ -1600,7 +1737,7 @@ static void ipu_idmac_exit(struct ipu *ipu) * IPU common probe / remove */ -static int ipu_probe(struct platform_device *pdev) +static int __init ipu_probe(struct platform_device *pdev) { struct ipu_platform_data *pdata = pdev->dev.platform_data; struct resource *mem_ipu, *mem_ic; @@ -1700,7 +1837,7 @@ err_noirq: return ret; } -static int ipu_remove(struct platform_device *pdev) +static int __exit ipu_remove(struct platform_device *pdev) { struct ipu *ipu = platform_get_drvdata(pdev); @@ -1725,7 +1862,7 @@ static struct platform_driver ipu_platform_driver = { .name = "ipu-core", .owner = THIS_MODULE, }, - .remove = ipu_remove, + .remove = __exit_p(ipu_remove), }; static int __init ipu_init(void) diff --git a/drivers/dma/ipu/ipu_irq.c b/drivers/dma/ipu/ipu_irq.c index 83f532cc767..dd8ebc75b66 100644 --- a/drivers/dma/ipu/ipu_irq.c +++ b/drivers/dma/ipu/ipu_irq.c @@ -352,7 +352,7 @@ static struct irq_chip ipu_irq_chip = { }; /* Install the IRQ handler */ -int ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev) +int __init ipu_irq_attach_irq(struct ipu *ipu, struct platform_device *dev) { struct ipu_platform_data *pdata = dev->dev.platform_data; unsigned int irq, irq_base, i; diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index cb7f26fb9f1..ddab94f5122 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -632,7 +632,6 @@ static int mv_xor_alloc_chan_resources(struct dma_chan *chan) slot->async_tx.tx_submit = mv_xor_tx_submit; INIT_LIST_HEAD(&slot->chain_node); INIT_LIST_HEAD(&slot->slot_node); - INIT_LIST_HEAD(&slot->async_tx.tx_list); hw_desc = (char *) mv_chan->device->dma_desc_pool; slot->async_tx.phys = (dma_addr_t) &hw_desc[idx * MV_XOR_SLOT_SIZE]; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3d2565441b3..edb02530e46 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -42,9 +42,9 @@ config DEBUG_GPIO depends on DEBUG_KERNEL help Say Y here to add some extra checks and diagnostics to GPIO calls. - The checks help ensure that GPIOs have been properly initialized - before they are used and that sleeping calls aren not made from - nonsleeping contexts. They can make bitbanged serial protocols + These checks help ensure that GPIOs have been properly initialized + before they are used, and that sleeping calls are not made from + non-sleeping contexts. They can make bitbanged serial protocols slower. The diagnostics help catch the type of setup errors that are most common when setting up new platforms or boards. diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index e85c8fe9ffc..7e67dcb3d4f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -29,11 +29,11 @@ config HID For docs and specs, see http://www.usb.org/developers/hidpage/ - If unsure, say Y + If unsure, say Y. config HID_DEBUG bool "HID debugging support" - default y if !EMBEDDED + default y depends on HID ---help--- This option lets the HID layer output diagnostics about its internal @@ -44,7 +44,7 @@ config HID_DEBUG This feature is useful for those who are either debugging the HID parser or any HID hardware device. - If unsure, say N + If unsure, say Y. config HIDRAW bool "/dev/hidraw raw HID device support" @@ -70,18 +70,6 @@ source "drivers/hid/usbhid/Kconfig" menu "Special HID drivers" depends on HID -config HID_COMPAT - bool "Load all HID drivers on hid core load" - default y - ---help--- - Compatible option for older userspace. If you have system without udev - support of module loading through aliases and also old - module-init-tools which can't handle hid bus, choose Y here. Otherwise - say N. If you say N and your userspace is old enough, the only - functionality you lose is modules autoloading. - - If unsure, say Y. - config HID_A4TECH tristate "A4 tech" if EMBEDDED depends on USB_HID @@ -128,6 +116,14 @@ config HID_CYPRESS ---help--- Support for cypress mouse and barcode readers. +config DRAGONRISE_FF + tristate "DragonRise Inc. force feedback support" + depends on USB_HID + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you want to enable force feedback support for DragonRise Inc. + game controllers. + config HID_EZKEY tristate "Ezkey" if EMBEDDED depends on USB_HID @@ -135,6 +131,13 @@ config HID_EZKEY ---help--- Support for Ezkey BTC 8193 keyboard. +config HID_KYE + tristate "Kye" if EMBEDDED + depends on USB_HID + default !EMBEDDED + ---help--- + Support for Kye/Genius Ergo Mouse. + config HID_GYRATION tristate "Gyration" if EMBEDDED depends on USB_HID @@ -142,6 +145,13 @@ config HID_GYRATION ---help--- Support for Gyration remote control. +config HID_KENSINGTON + tristate "Kensington" if EMBEDDED + depends on USB_HID + default !EMBEDDED + ---help--- + Support for Kensington Slimblade Trackball. + config HID_LOGITECH tristate "Logitech" if EMBEDDED depends on USB_HID @@ -243,7 +253,7 @@ config GREENASIA_FF select INPUT_FF_MEMLESS ---help--- Say Y here if you have a GreenAsia (Product ID 0x12) based game controller - (like MANTA Warior MM816 and SpeedLink Strike2 SL-6635) or adapter + (like MANTA Warrior MM816 and SpeedLink Strike2 SL-6635) or adapter and want to enable force feedback support for it. config HID_TOPSEED diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index fbd021f153f..1f7cb0fd450 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -8,10 +8,6 @@ obj-$(CONFIG_HID) += hid.o hid-$(CONFIG_HID_DEBUG) += hid-debug.o hid-$(CONFIG_HIDRAW) += hidraw.o -ifdef CONFIG_HID_COMPAT -obj-m += hid-dummy.o -endif - hid-logitech-objs := hid-lg.o ifdef CONFIG_LOGITECH_FF hid-logitech-objs += hid-lgff.o @@ -26,8 +22,11 @@ obj-$(CONFIG_HID_BELKIN) += hid-belkin.o obj-$(CONFIG_HID_CHERRY) += hid-cherry.o obj-$(CONFIG_HID_CHICONY) += hid-chicony.o obj-$(CONFIG_HID_CYPRESS) += hid-cypress.o +obj-$(CONFIG_DRAGONRISE_FF) += hid-drff.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o obj-$(CONFIG_HID_GYRATION) += hid-gyration.o +obj-$(CONFIG_HID_KENSINGTON) += hid-kensington.o +obj-$(CONFIG_HID_KYE) += hid-kye.o obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o diff --git a/drivers/hid/hid-a4tech.c b/drivers/hid/hid-a4tech.c index ebca00e6c10..42ea359e94c 100644 --- a/drivers/hid/hid-a4tech.c +++ b/drivers/hid/hid-a4tech.c @@ -158,5 +158,3 @@ static void a4_exit(void) module_init(a4_init); module_exit(a4_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(a4tech); diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index aa28aed0e46..7359d9d88e4 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -53,7 +53,7 @@ struct apple_key_translation { u8 flags; }; -static struct apple_key_translation apple_fn_keys[] = { +static const struct apple_key_translation apple_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_ENTER, KEY_INSERT }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, @@ -75,7 +75,7 @@ static struct apple_key_translation apple_fn_keys[] = { { } }; -static struct apple_key_translation powerbook_fn_keys[] = { +static const struct apple_key_translation powerbook_fn_keys[] = { { KEY_BACKSPACE, KEY_DELETE }, { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY }, { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY }, @@ -94,7 +94,7 @@ static struct apple_key_translation powerbook_fn_keys[] = { { } }; -static struct apple_key_translation powerbook_numlock_keys[] = { +static const struct apple_key_translation powerbook_numlock_keys[] = { { KEY_J, KEY_KP1 }, { KEY_K, KEY_KP2 }, { KEY_L, KEY_KP3 }, @@ -117,16 +117,16 @@ static struct apple_key_translation powerbook_numlock_keys[] = { { } }; -static struct apple_key_translation apple_iso_keyboard[] = { +static const struct apple_key_translation apple_iso_keyboard[] = { { KEY_GRAVE, KEY_102ND }, { KEY_102ND, KEY_GRAVE }, { } }; -static struct apple_key_translation *apple_find_translation( - struct apple_key_translation *table, u16 from) +static const struct apple_key_translation *apple_find_translation( + const struct apple_key_translation *table, u16 from) { - struct apple_key_translation *trans; + const struct apple_key_translation *trans; /* Look for the translation */ for (trans = table; trans->from; trans++) @@ -140,7 +140,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, struct hid_usage *usage, __s32 value) { struct apple_sc *asc = hid_get_drvdata(hid); - struct apple_key_translation *trans; + const struct apple_key_translation *trans; if (usage->code == KEY_FN) { asc->fn_on = !!value; @@ -253,7 +253,7 @@ static void apple_report_fixup(struct hid_device *hdev, __u8 *rdesc, static void apple_setup_input(struct input_dev *input) { - struct apple_key_translation *trans; + const struct apple_key_translation *trans; set_bit(KEY_NUMLOCK, input->keybit); @@ -387,6 +387,12 @@ static const struct hid_device_id apple_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS), .driver_data = APPLE_NUMLOCK_EMULATION | APPLE_HAS_FN | APPLE_RDESC_JIS }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI), + .driver_data = APPLE_HAS_FN }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO), + .driver_data = APPLE_HAS_FN | APPLE_ISO_KEYBOARD }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS), + .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI), .driver_data = APPLE_HAS_FN }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO), @@ -468,5 +474,3 @@ static void apple_exit(void) module_init(apple_init); module_exit(apple_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(apple); diff --git a/drivers/hid/hid-belkin.c b/drivers/hid/hid-belkin.c index 12c8a9ba6ed..2f6723133a4 100644 --- a/drivers/hid/hid-belkin.c +++ b/drivers/hid/hid-belkin.c @@ -101,5 +101,3 @@ static void belkin_exit(void) module_init(belkin_init); module_exit(belkin_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(belkin); diff --git a/drivers/hid/hid-cherry.c b/drivers/hid/hid-cherry.c index b833b9769ab..ab8209e7e45 100644 --- a/drivers/hid/hid-cherry.c +++ b/drivers/hid/hid-cherry.c @@ -83,5 +83,3 @@ static void ch_exit(void) module_init(ch_init); module_exit(ch_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(cherry); diff --git a/drivers/hid/hid-chicony.c b/drivers/hid/hid-chicony.c index a54d4096e0f..7f91076d849 100644 --- a/drivers/hid/hid-chicony.c +++ b/drivers/hid/hid-chicony.c @@ -76,5 +76,3 @@ static void ch_exit(void) module_init(ch_init); module_exit(ch_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(chicony); diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 1cc967448f4..5746a5903bc 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1236,6 +1236,9 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_GEYSER4_JIS) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ANSI) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_ISO) }, + { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_MINI_JIS) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ANSI) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_ISO) }, { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ALU_JIS) }, @@ -1262,6 +1265,7 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_1) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_BARCODE_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_CYPRESS, USB_DEVICE_ID_CYPRESS_MOUSE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006) }, { HID_USB_DEVICE(USB_VENDOR_ID_EZKEY, USB_DEVICE_ID_BTC_8193) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PSX_ADAPTOR) }, { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR) }, @@ -1269,6 +1273,8 @@ static const struct hid_device_id hid_blacklist[] = { { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0012) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE) }, { HID_USB_DEVICE(USB_VENDOR_ID_GYRATION, USB_DEVICE_ID_GYRATION_REMOTE_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, { HID_USB_DEVICE(USB_VENDOR_ID_LABTEC, USB_DEVICE_ID_LABTEC_WIRELESS_KEYBOARD) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_MX3000_RECEIVER) }, { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, USB_DEVICE_ID_S510_RECEIVER) }, @@ -1813,14 +1819,21 @@ void hid_unregister_driver(struct hid_driver *hdrv) } EXPORT_SYMBOL_GPL(hid_unregister_driver); -#ifdef CONFIG_HID_COMPAT -static void hid_compat_load(struct work_struct *ws) +int hid_check_keys_pressed(struct hid_device *hid) { - request_module("hid-dummy"); + struct hid_input *hidinput; + int i; + + list_for_each_entry(hidinput, &hid->inputs, list) { + for (i = 0; i < BITS_TO_LONGS(KEY_MAX); i++) + if (hidinput->input->key[i]) + return 1; + } + + return 0; } -static DECLARE_WORK(hid_compat_work, hid_compat_load); -static struct workqueue_struct *hid_compat_wq; -#endif + +EXPORT_SYMBOL_GPL(hid_check_keys_pressed); static int __init hid_init(void) { @@ -1836,15 +1849,6 @@ static int __init hid_init(void) if (ret) goto err_bus; -#ifdef CONFIG_HID_COMPAT - hid_compat_wq = create_singlethread_workqueue("hid_compat"); - if (!hid_compat_wq) { - hidraw_exit(); - goto err; - } - queue_work(hid_compat_wq, &hid_compat_work); -#endif - return 0; err_bus: bus_unregister(&hid_bus_type); @@ -1854,9 +1858,6 @@ err: static void __exit hid_exit(void) { -#ifdef CONFIG_HID_COMPAT - destroy_workqueue(hid_compat_wq); -#endif hidraw_exit(); bus_unregister(&hid_bus_type); } diff --git a/drivers/hid/hid-cypress.c b/drivers/hid/hid-cypress.c index 5d69d27b935..9d6d3b91773 100644 --- a/drivers/hid/hid-cypress.c +++ b/drivers/hid/hid-cypress.c @@ -154,5 +154,3 @@ static void cp_exit(void) module_init(cp_init); module_exit(cp_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(cypress); diff --git a/drivers/hid/hid-drff.c b/drivers/hid/hid-drff.c new file mode 100644 index 00000000000..34f3eb65100 --- /dev/null +++ b/drivers/hid/hid-drff.c @@ -0,0 +1,188 @@ +/* + * Force feedback support for DragonRise Inc. game controllers + * + * From what I have gathered, these devices are mass produced in China and are + * distributed under several vendors. They often share the same design as + * the original PlayStation DualShock controller. + * + * 0079:0006 "DragonRise Inc. Generic USB Joystick " + * - tested with a Tesun USB-703 game controller. + * + * Copyright (c) 2009 Richard Walmsley <richwalm@gmail.com> + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/input.h> +#include <linux/usb.h> +#include <linux/hid.h> + +#include "hid-ids.h" +#include "usbhid/usbhid.h" + +struct drff_device { + struct hid_report *report; +}; + +static int drff_play(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct hid_device *hid = input_get_drvdata(dev); + struct drff_device *drff = data; + int strong, weak; + + strong = effect->u.rumble.strong_magnitude; + weak = effect->u.rumble.weak_magnitude; + + dbg_hid("called with 0x%04x 0x%04x", strong, weak); + + if (strong || weak) { + strong = strong * 0xff / 0xffff; + weak = weak * 0xff / 0xffff; + + /* While reverse engineering this device, I found that when + this value is set, it causes the strong rumble to function + at a near maximum speed, so we'll bypass it. */ + if (weak == 0x0a) + weak = 0x0b; + + drff->report->field[0]->value[0] = 0x51; + drff->report->field[0]->value[1] = 0x00; + drff->report->field[0]->value[2] = weak; + drff->report->field[0]->value[4] = strong; + usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + + drff->report->field[0]->value[0] = 0xfa; + drff->report->field[0]->value[1] = 0xfe; + } else { + drff->report->field[0]->value[0] = 0xf3; + drff->report->field[0]->value[1] = 0x00; + } + + drff->report->field[0]->value[2] = 0x00; + drff->report->field[0]->value[4] = 0x00; + dbg_hid("running with 0x%02x 0x%02x", strong, weak); + usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + + return 0; +} + +static int drff_init(struct hid_device *hid) +{ + struct drff_device *drff; + struct hid_report *report; + struct hid_input *hidinput = list_first_entry(&hid->inputs, + struct hid_input, list); + struct list_head *report_list = + &hid->report_enum[HID_OUTPUT_REPORT].report_list; + struct input_dev *dev = hidinput->input; + int error; + + if (list_empty(report_list)) { + dev_err(&hid->dev, "no output reports found\n"); + return -ENODEV; + } + + report = list_first_entry(report_list, struct hid_report, list); + if (report->maxfield < 1) { + dev_err(&hid->dev, "no fields in the report\n"); + return -ENODEV; + } + + if (report->field[0]->report_count < 7) { + dev_err(&hid->dev, "not enough values in the field\n"); + return -ENODEV; + } + + drff = kzalloc(sizeof(struct drff_device), GFP_KERNEL); + if (!drff) + return -ENOMEM; + + set_bit(FF_RUMBLE, dev->ffbit); + + error = input_ff_create_memless(dev, drff, drff_play); + if (error) { + kfree(drff); + return error; + } + + drff->report = report; + drff->report->field[0]->value[0] = 0xf3; + drff->report->field[0]->value[1] = 0x00; + drff->report->field[0]->value[2] = 0x00; + drff->report->field[0]->value[3] = 0x00; + drff->report->field[0]->value[4] = 0x00; + drff->report->field[0]->value[5] = 0x00; + drff->report->field[0]->value[6] = 0x00; + usbhid_submit_report(hid, drff->report, USB_DIR_OUT); + + dev_info(&hid->dev, "Force Feedback for DragonRise Inc. game " + "controllers by Richard Walmsley <richwalm@gmail.com>\n"); + + return 0; +} + +static int dr_probe(struct hid_device *hdev, const struct hid_device_id *id) +{ + int ret; + + dev_dbg(&hdev->dev, "DragonRise Inc. HID hardware probe..."); + + ret = hid_parse(hdev); + if (ret) { + dev_err(&hdev->dev, "parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT & ~HID_CONNECT_FF); + if (ret) { + dev_err(&hdev->dev, "hw start failed\n"); + goto err; + } + + drff_init(hdev); + + return 0; +err: + return ret; +} + +static const struct hid_device_id dr_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_DRAGONRISE, 0x0006), }, + { } +}; +MODULE_DEVICE_TABLE(hid, dr_devices); + +static struct hid_driver dr_driver = { + .name = "dragonrise", + .id_table = dr_devices, + .probe = dr_probe, +}; + +static int __init dr_init(void) +{ + return hid_register_driver(&dr_driver); +} + +static void __exit dr_exit(void) +{ + hid_unregister_driver(&dr_driver); +} + +module_init(dr_init); +module_exit(dr_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-dummy.c b/drivers/hid/hid-dummy.c deleted file mode 100644 index b4cc0f743d6..00000000000 --- a/drivers/hid/hid-dummy.c +++ /dev/null @@ -1,78 +0,0 @@ -#include <linux/autoconf.h> -#include <linux/module.h> -#include <linux/hid.h> - -static int __init hid_dummy_init(void) -{ -#ifdef CONFIG_HID_A4TECH_MODULE - HID_COMPAT_CALL_DRIVER(a4tech); -#endif -#ifdef CONFIG_HID_APPLE_MODULE - HID_COMPAT_CALL_DRIVER(apple); -#endif -#ifdef CONFIG_HID_BELKIN_MODULE - HID_COMPAT_CALL_DRIVER(belkin); -#endif -#ifdef CONFIG_HID_BRIGHT_MODULE - HID_COMPAT_CALL_DRIVER(bright); -#endif -#ifdef CONFIG_HID_CHERRY_MODULE - HID_COMPAT_CALL_DRIVER(cherry); -#endif -#ifdef CONFIG_HID_CHICONY_MODULE - HID_COMPAT_CALL_DRIVER(chicony); -#endif -#ifdef CONFIG_HID_CYPRESS_MODULE - HID_COMPAT_CALL_DRIVER(cypress); -#endif -#ifdef CONFIG_HID_DELL_MODULE - HID_COMPAT_CALL_DRIVER(dell); -#endif -#ifdef CONFIG_HID_EZKEY_MODULE - HID_COMPAT_CALL_DRIVER(ezkey); -#endif -#ifdef CONFIG_HID_GYRATION_MODULE - HID_COMPAT_CALL_DRIVER(gyration); -#endif -#ifdef CONFIG_HID_LOGITECH_MODULE - HID_COMPAT_CALL_DRIVER(logitech); -#endif -#ifdef CONFIG_HID_MICROSOFT_MODULE - HID_COMPAT_CALL_DRIVER(microsoft); -#endif -#ifdef CONFIG_HID_MONTEREY_MODULE - HID_COMPAT_CALL_DRIVER(monterey); -#endif -#ifdef CONFIG_HID_NTRIG_MODULE - HID_COMPAT_CALL_DRIVER(ntrig); -#endif -#ifdef CONFIG_HID_PANTHERLORD_MODULE - HID_COMPAT_CALL_DRIVER(pantherlord); -#endif -#ifdef CONFIG_HID_PETALYNX_MODULE - HID_COMPAT_CALL_DRIVER(petalynx); -#endif -#ifdef CONFIG_HID_SAMSUNG_MODULE - HID_COMPAT_CALL_DRIVER(samsung); -#endif -#ifdef CONFIG_HID_SONY_MODULE - HID_COMPAT_CALL_DRIVER(sony); -#endif -#ifdef CONFIG_HID_SUNPLUS_MODULE - HID_COMPAT_CALL_DRIVER(sunplus); -#endif -#ifdef CONFIG_GREENASIA_FF_MODULE - HID_COMPAT_CALL_DRIVER(greenasia); -#endif -#ifdef CONFIG_THRUSTMASTER_FF_MODULE - HID_COMPAT_CALL_DRIVER(thrustmaster); -#endif -#ifdef CONFIG_ZEROPLUS_FF_MODULE - HID_COMPAT_CALL_DRIVER(zeroplus); -#endif - - return -EIO; -} -module_init(hid_dummy_init); - -MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-ezkey.c b/drivers/hid/hid-ezkey.c index deb42f931b7..0a1fe054799 100644 --- a/drivers/hid/hid-ezkey.c +++ b/drivers/hid/hid-ezkey.c @@ -91,5 +91,3 @@ static void ez_exit(void) module_init(ez_init); module_exit(ez_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(ezkey); diff --git a/drivers/hid/hid-gaff.c b/drivers/hid/hid-gaff.c index 71211f6a4f0..510ad3ab8d3 100644 --- a/drivers/hid/hid-gaff.c +++ b/drivers/hid/hid-gaff.c @@ -181,5 +181,3 @@ static void __exit ga_exit(void) module_init(ga_init); module_exit(ga_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(greenasia); diff --git a/drivers/hid/hid-gyration.c b/drivers/hid/hid-gyration.c index 04a0afec52a..d42d222097a 100644 --- a/drivers/hid/hid-gyration.c +++ b/drivers/hid/hid-gyration.c @@ -94,5 +94,3 @@ static void gyration_exit(void) module_init(gyration_init); module_exit(gyration_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(gyration); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 88511970508..bdeda4c7cc1 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -67,6 +67,9 @@ #define USB_DEVICE_ID_APPLE_GEYSER4_ANSI 0x021a #define USB_DEVICE_ID_APPLE_GEYSER4_ISO 0x021b #define USB_DEVICE_ID_APPLE_GEYSER4_JIS 0x021c +#define USB_DEVICE_ID_APPLE_ALU_MINI_ANSI 0x021d +#define USB_DEVICE_ID_APPLE_ALU_MINI_ISO 0x021e +#define USB_DEVICE_ID_APPLE_ALU_MINI_JIS 0x021f #define USB_DEVICE_ID_APPLE_ALU_ANSI 0x0220 #define USB_DEVICE_ID_APPLE_ALU_ISO 0x0221 #define USB_DEVICE_ID_APPLE_ALU_JIS 0x0222 @@ -148,6 +151,8 @@ #define USB_VENDOR_ID_DMI 0x0c0b #define USB_DEVICE_ID_DMI_ENC 0x5fab +#define USB_VENDOR_ID_DRAGONRISE 0x0079 + #define USB_VENDOR_ID_ELO 0x04E7 #define USB_DEVICE_ID_ELO_TS2700 0x0020 @@ -272,6 +277,9 @@ #define USB_DEVICE_ID_LD_POWERCONTROL 0x2030 #define USB_DEVICE_ID_LD_MACHINETEST 0x2040 +#define USB_VENDOR_ID_KENSINGTON 0x047d +#define USB_DEVICE_ID_KS_SLIMBLADE 0x2041 + #define USB_VENDOR_ID_LOGITECH 0x046d #define USB_DEVICE_ID_LOGITECH_RECEIVER 0xc101 #define USB_DEVICE_ID_LOGITECH_HARMONY_FIRST 0xc110 @@ -418,6 +426,8 @@ #define USB_VENDOR_ID_ZEROPLUS 0x0c12 #define USB_VENDOR_ID_KYE 0x0458 +#define USB_DEVICE_ID_KYE_ERGO_525V 0x0087 #define USB_DEVICE_ID_KYE_GPEN_560 0x5003 + #endif diff --git a/drivers/hid/hid-kensington.c b/drivers/hid/hid-kensington.c new file mode 100644 index 00000000000..7353bd79cbe --- /dev/null +++ b/drivers/hid/hid-kensington.c @@ -0,0 +1,63 @@ +/* + * HID driver for Kensigton Slimblade Trackball + * + * Copyright (c) 2009 Jiri Kosina + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/input.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +#define ks_map_key(c) hid_map_usage(hi, usage, bit, max, EV_KEY, (c)) + +static int ks_input_mapping(struct hid_device *hdev, struct hid_input *hi, + struct hid_field *field, struct hid_usage *usage, + unsigned long **bit, int *max) +{ + if ((usage->hid & HID_USAGE_PAGE) != HID_UP_MSVENDOR) + return 0; + + switch (usage->hid & HID_USAGE) { + case 0x01: ks_map_key(BTN_MIDDLE); break; + case 0x02: ks_map_key(BTN_SIDE); break; + default: + return 0; + } + return 1; +} + +static const struct hid_device_id ks_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_KENSINGTON, USB_DEVICE_ID_KS_SLIMBLADE) }, + { } +}; +MODULE_DEVICE_TABLE(hid, ks_devices); + +static struct hid_driver ks_driver = { + .name = "kensington", + .id_table = ks_devices, + .input_mapping = ks_input_mapping, +}; + +static int ks_init(void) +{ + return hid_register_driver(&ks_driver); +} + +static void ks_exit(void) +{ + hid_unregister_driver(&ks_driver); +} + +module_init(ks_init); +module_exit(ks_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-kye.c b/drivers/hid/hid-kye.c new file mode 100644 index 00000000000..72ee3fec56d --- /dev/null +++ b/drivers/hid/hid-kye.c @@ -0,0 +1,69 @@ +/* + * HID driver for Kye/Genius devices not fully compliant with HID standard + * + * Copyright (c) 2009 Jiri Kosina + * Copyright (c) 2009 Tomas Hanak + */ + +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + */ + +#include <linux/device.h> +#include <linux/hid.h> +#include <linux/module.h> + +#include "hid-ids.h" + +/* the fixups that need to be done: + * - change led usage page to button for extra buttons + * - report size 8 count 1 must be size 1 count 8 for button bitfield + * - change the button usage range to 4-7 for the extra buttons + */ +static void kye_report_fixup(struct hid_device *hdev, __u8 *rdesc, + unsigned int rsize) +{ + if (rsize >= 74 && + rdesc[61] == 0x05 && rdesc[62] == 0x08 && + rdesc[63] == 0x19 && rdesc[64] == 0x08 && + rdesc[65] == 0x29 && rdesc[66] == 0x0f && + rdesc[71] == 0x75 && rdesc[72] == 0x08 && + rdesc[73] == 0x95 && rdesc[74] == 0x01) { + dev_info(&hdev->dev, "fixing up Kye/Genius Ergo Mouse report " + "descriptor\n"); + rdesc[62] = 0x09; + rdesc[64] = 0x04; + rdesc[66] = 0x07; + rdesc[72] = 0x01; + rdesc[74] = 0x08; + } +} + +static const struct hid_device_id kye_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_KYE, USB_DEVICE_ID_KYE_ERGO_525V) }, + { } +}; +MODULE_DEVICE_TABLE(hid, kye_devices); + +static struct hid_driver kye_driver = { + .name = "kye", + .id_table = kye_devices, + .report_fixup = kye_report_fixup, +}; + +static int kye_init(void) +{ + return hid_register_driver(&kye_driver); +} + +static void kye_exit(void) +{ + hid_unregister_driver(&kye_driver); +} + +module_init(kye_init); +module_exit(kye_exit); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/hid-lg.c b/drivers/hid/hid-lg.c index 83e07c9f414..7b80cb69498 100644 --- a/drivers/hid/hid-lg.c +++ b/drivers/hid/hid-lg.c @@ -326,5 +326,3 @@ static void lg_exit(void) module_init(lg_init); module_exit(lg_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(logitech); diff --git a/drivers/hid/hid-microsoft.c b/drivers/hid/hid-microsoft.c index 25b10dcad90..5e9e37a0506 100644 --- a/drivers/hid/hid-microsoft.c +++ b/drivers/hid/hid-microsoft.c @@ -210,5 +210,3 @@ static void ms_exit(void) module_init(ms_init); module_exit(ms_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(microsoft); diff --git a/drivers/hid/hid-monterey.c b/drivers/hid/hid-monterey.c index f3a85a065f1..240f87618be 100644 --- a/drivers/hid/hid-monterey.c +++ b/drivers/hid/hid-monterey.c @@ -78,5 +78,3 @@ static void mr_exit(void) module_init(mr_init); module_exit(mr_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(monterey); diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c index db44fbd7bdf..c5b252be9c2 100644 --- a/drivers/hid/hid-ntrig.c +++ b/drivers/hid/hid-ntrig.c @@ -78,5 +78,3 @@ static void ntrig_exit(void) module_init(ntrig_init); module_exit(ntrig_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(ntrig); diff --git a/drivers/hid/hid-petalynx.c b/drivers/hid/hid-petalynx.c index 10945fe12d5..2e83e8ff891 100644 --- a/drivers/hid/hid-petalynx.c +++ b/drivers/hid/hid-petalynx.c @@ -118,5 +118,3 @@ static void pl_exit(void) module_init(pl_init); module_exit(pl_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(petalynx); diff --git a/drivers/hid/hid-pl.c b/drivers/hid/hid-pl.c index 46941f979b9..4db9a348376 100644 --- a/drivers/hid/hid-pl.c +++ b/drivers/hid/hid-pl.c @@ -9,9 +9,12 @@ * - contains two reports, one for each port (HID_QUIRK_MULTI_INPUT) * * 0e8f:0003 "GreenAsia Inc. USB Joystick " - * - tested with K??ng Gaming gamepad + * - tested with König Gaming gamepad * - * Copyright (c) 2007 Anssi Hannula <anssi.hannula@gmail.com> + * 0e8f:0003 "GASIA USB Gamepad" + * - another version of the König gamepad + * + * Copyright (c) 2007, 2009 Anssi Hannula <anssi.hannula@gmail.com> */ /* @@ -46,6 +49,8 @@ struct plff_device { struct hid_report *report; + s32 *strong; + s32 *weak; }; static int hid_plff_play(struct input_dev *dev, void *data, @@ -62,8 +67,8 @@ static int hid_plff_play(struct input_dev *dev, void *data, left = left * 0x7f / 0xffff; right = right * 0x7f / 0xffff; - plff->report->field[0]->value[2] = left; - plff->report->field[0]->value[3] = right; + *plff->strong = left; + *plff->weak = right; debug("running with 0x%02x 0x%02x", left, right); usbhid_submit_report(hid, plff->report, USB_DIR_OUT); @@ -80,6 +85,8 @@ static int plff_init(struct hid_device *hid) struct list_head *report_ptr = report_list; struct input_dev *dev; int error; + s32 *strong; + s32 *weak; /* The device contains one output report per physical device, all containing 1 field, which contains 4 ff00.0002 usages and 4 16bit @@ -87,7 +94,12 @@ static int plff_init(struct hid_device *hid) The input reports also contain a field which contains 8 ff00.0001 usages and 8 boolean values. Their meaning is - currently unknown. */ + currently unknown. + + A version of the 0e8f:0003 exists that has all the values in + separate fields and misses the extra input field, thus resembling + Zeroplus (hid-zpff) devices. + */ if (list_empty(report_list)) { dev_err(&hid->dev, "no output reports found\n"); @@ -110,8 +122,21 @@ static int plff_init(struct hid_device *hid) return -ENODEV; } - if (report->field[0]->report_count < 4) { - dev_err(&hid->dev, "not enough values in the field\n"); + if (report->field[0]->report_count >= 4) { + report->field[0]->value[0] = 0x00; + report->field[0]->value[1] = 0x00; + strong = &report->field[0]->value[2]; + weak = &report->field[0]->value[3]; + debug("detected single-field device"); + } else if (report->maxfield >= 4 && report->field[0]->maxusage == 1 && + report->field[0]->usage[0].hid == (HID_UP_LED | 0x43)) { + report->field[0]->value[0] = 0x00; + report->field[1]->value[0] = 0x00; + strong = &report->field[2]->value[0]; + weak = &report->field[3]->value[0]; + debug("detected 4-field device"); + } else { + dev_err(&hid->dev, "not enough fields or values\n"); return -ENODEV; } @@ -130,10 +155,11 @@ static int plff_init(struct hid_device *hid) } plff->report = report; - plff->report->field[0]->value[0] = 0x00; - plff->report->field[0]->value[1] = 0x00; - plff->report->field[0]->value[2] = 0x00; - plff->report->field[0]->value[3] = 0x00; + plff->strong = strong; + plff->weak = weak; + + *strong = 0x00; + *weak = 0x00; usbhid_submit_report(hid, plff->report, USB_DIR_OUT); } @@ -180,7 +206,7 @@ static const struct hid_device_id pl_devices[] = { .driver_data = 1 }, /* Twin USB Joystick */ { HID_USB_DEVICE(USB_VENDOR_ID_GAMERON, USB_DEVICE_ID_GAMERON_DUAL_PCS_ADAPTOR), .driver_data = 1 }, /* Twin USB Joystick */ - { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, /* GreenAsia Inc. USB Joystick */ + { HID_USB_DEVICE(USB_VENDOR_ID_GREENASIA, 0x0003), }, { } }; MODULE_DEVICE_TABLE(hid, pl_devices); @@ -204,5 +230,3 @@ static void pl_exit(void) module_init(pl_init); module_exit(pl_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(pantherlord); diff --git a/drivers/hid/hid-samsung.c b/drivers/hid/hid-samsung.c index 15f3c049245..07083aa6c19 100644 --- a/drivers/hid/hid-samsung.c +++ b/drivers/hid/hid-samsung.c @@ -96,5 +96,3 @@ static void samsung_exit(void) module_init(samsung_init); module_exit(samsung_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(samsung); diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index dd5a3979a4d..c2599388a35 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -148,5 +148,3 @@ static void sony_exit(void) module_init(sony_init); module_exit(sony_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(sony); diff --git a/drivers/hid/hid-sunplus.c b/drivers/hid/hid-sunplus.c index 5ba68f7dbb7..e0a8fd36a85 100644 --- a/drivers/hid/hid-sunplus.c +++ b/drivers/hid/hid-sunplus.c @@ -78,5 +78,3 @@ static void sp_exit(void) module_init(sp_init); module_exit(sp_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(sunplus); diff --git a/drivers/hid/hid-tmff.c b/drivers/hid/hid-tmff.c index 1b7cba0f7e1..7c1f7b50330 100644 --- a/drivers/hid/hid-tmff.c +++ b/drivers/hid/hid-tmff.c @@ -265,5 +265,3 @@ static void tm_exit(void) module_init(tm_init); module_exit(tm_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(thrustmaster); diff --git a/drivers/hid/hid-topseed.c b/drivers/hid/hid-topseed.c index cca64a0564a..152ccfabeba 100644 --- a/drivers/hid/hid-topseed.c +++ b/drivers/hid/hid-topseed.c @@ -73,5 +73,3 @@ static void ts_exit(void) module_init(ts_init); module_exit(ts_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(topseed); diff --git a/drivers/hid/hid-zpff.c b/drivers/hid/hid-zpff.c index ea82f3718b2..85a198a1853 100644 --- a/drivers/hid/hid-zpff.c +++ b/drivers/hid/hid-zpff.c @@ -158,5 +158,3 @@ static void zp_exit(void) module_init(zp_init); module_exit(zp_exit); MODULE_LICENSE("GPL"); - -HID_COMPAT_LOAD_DRIVER(zeroplus); diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c index 02b19db5442..e263d473117 100644 --- a/drivers/hid/hidraw.c +++ b/drivers/hid/hidraw.c @@ -181,9 +181,17 @@ static int hidraw_open(struct inode *inode, struct file *file) dev = hidraw_table[minor]; if (!dev->open++) { + if (dev->hid->ll_driver->power) { + err = dev->hid->ll_driver->power(dev->hid, PM_HINT_FULLON); + if (err < 0) + goto out_unlock; + } err = dev->hid->ll_driver->open(dev->hid); - if (err < 0) + if (err < 0) { + if (dev->hid->ll_driver->power) + dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL); dev->open--; + } } out_unlock: @@ -209,10 +217,13 @@ static int hidraw_release(struct inode * inode, struct file * file) list_del(&list->node); dev = hidraw_table[minor]; if (!--dev->open) { - if (list->hidraw->exist) + if (list->hidraw->exist) { + if (dev->hid->ll_driver->power) + dev->hid->ll_driver->power(dev->hid, PM_HINT_NORMAL); dev->hid->ll_driver->close(dev->hid); - else + } else { kfree(list->hidraw); + } } kfree(list); diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c index f0a0f72238a..4306cb1b8ce 100644 --- a/drivers/hid/usbhid/hid-core.c +++ b/drivers/hid/usbhid/hid-core.c @@ -5,6 +5,7 @@ * Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz> * Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc * Copyright (c) 2006-2008 Jiri Kosina + * Copyright (c) 2007-2008 Oliver Neukum */ /* @@ -27,6 +28,7 @@ #include <asm/byteorder.h> #include <linux/input.h> #include <linux/wait.h> +#include <linux/workqueue.h> #include <linux/usb.h> @@ -53,6 +55,10 @@ static unsigned int hid_mousepoll_interval; module_param_named(mousepoll, hid_mousepoll_interval, uint, 0644); MODULE_PARM_DESC(mousepoll, "Polling interval of mice"); +static unsigned int ignoreled; +module_param_named(ignoreled, ignoreled, uint, 0644); +MODULE_PARM_DESC(ignoreled, "Autosuspend with active leds"); + /* Quirks specified at module load time */ static char *quirks_param[MAX_USBHID_BOOT_QUIRKS] = { [ 0 ... (MAX_USBHID_BOOT_QUIRKS - 1) ] = NULL }; module_param_array_named(quirks, quirks_param, charp, NULL, 0444); @@ -63,8 +69,13 @@ MODULE_PARM_DESC(quirks, "Add/modify USB HID quirks by specifying " /* * Input submission and I/O error handler. */ +static DEFINE_MUTEX(hid_open_mut); +static struct workqueue_struct *resumption_waker; static void hid_io_error(struct hid_device *hid); +static int hid_submit_out(struct hid_device *hid); +static int hid_submit_ctrl(struct hid_device *hid); +static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid); /* Start up the input URB */ static int hid_start_in(struct hid_device *hid) @@ -73,15 +84,16 @@ static int hid_start_in(struct hid_device *hid) int rc = 0; struct usbhid_device *usbhid = hid->driver_data; - spin_lock_irqsave(&usbhid->inlock, flags); - if (hid->open > 0 && !test_bit(HID_SUSPENDED, &usbhid->iofl) && + spin_lock_irqsave(&usbhid->lock, flags); + if (hid->open > 0 && !test_bit(HID_DISCONNECTED, &usbhid->iofl) && + !test_bit(HID_REPORTED_IDLE, &usbhid->iofl) && !test_and_set_bit(HID_IN_RUNNING, &usbhid->iofl)) { rc = usb_submit_urb(usbhid->urbin, GFP_ATOMIC); if (rc != 0) clear_bit(HID_IN_RUNNING, &usbhid->iofl); } - spin_unlock_irqrestore(&usbhid->inlock, flags); + spin_unlock_irqrestore(&usbhid->lock, flags); return rc; } @@ -145,7 +157,7 @@ static void hid_io_error(struct hid_device *hid) unsigned long flags; struct usbhid_device *usbhid = hid->driver_data; - spin_lock_irqsave(&usbhid->inlock, flags); + spin_lock_irqsave(&usbhid->lock, flags); /* Stop when disconnected */ if (test_bit(HID_DISCONNECTED, &usbhid->iofl)) @@ -175,7 +187,51 @@ static void hid_io_error(struct hid_device *hid) mod_timer(&usbhid->io_retry, jiffies + msecs_to_jiffies(usbhid->retry_delay)); done: - spin_unlock_irqrestore(&usbhid->inlock, flags); + spin_unlock_irqrestore(&usbhid->lock, flags); +} + +static void usbhid_mark_busy(struct usbhid_device *usbhid) +{ + struct usb_interface *intf = usbhid->intf; + + usb_mark_last_busy(interface_to_usbdev(intf)); +} + +static int usbhid_restart_out_queue(struct usbhid_device *usbhid) +{ + struct hid_device *hid = usb_get_intfdata(usbhid->intf); + int kicked; + + if (!hid) + return 0; + + if ((kicked = (usbhid->outhead != usbhid->outtail))) { + dbg("Kicking head %d tail %d", usbhid->outhead, usbhid->outtail); + if (hid_submit_out(hid)) { + clear_bit(HID_OUT_RUNNING, &usbhid->iofl); + wake_up(&usbhid->wait); + } + } + return kicked; +} + +static int usbhid_restart_ctrl_queue(struct usbhid_device *usbhid) +{ + struct hid_device *hid = usb_get_intfdata(usbhid->intf); + int kicked; + + WARN_ON(hid == NULL); + if (!hid) + return 0; + + if ((kicked = (usbhid->ctrlhead != usbhid->ctrltail))) { + dbg("Kicking head %d tail %d", usbhid->ctrlhead, usbhid->ctrltail); + if (hid_submit_ctrl(hid)) { + clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); + wake_up(&usbhid->wait); + } + } + return kicked; } /* @@ -190,12 +246,23 @@ static void hid_irq_in(struct urb *urb) switch (urb->status) { case 0: /* success */ + usbhid_mark_busy(usbhid); usbhid->retry_delay = 0; hid_input_report(urb->context, HID_INPUT_REPORT, urb->transfer_buffer, urb->actual_length, 1); + /* + * autosuspend refused while keys are pressed + * because most keyboards don't wake up when + * a key is released + */ + if (hid_check_keys_pressed(hid)) + set_bit(HID_KEYS_PRESSED, &usbhid->iofl); + else + clear_bit(HID_KEYS_PRESSED, &usbhid->iofl); break; case -EPIPE: /* stall */ + usbhid_mark_busy(usbhid); clear_bit(HID_IN_RUNNING, &usbhid->iofl); set_bit(HID_CLEAR_HALT, &usbhid->iofl); schedule_work(&usbhid->reset_work); @@ -209,6 +276,7 @@ static void hid_irq_in(struct urb *urb) case -EPROTO: /* protocol error or unplug */ case -ETIME: /* protocol error or unplug */ case -ETIMEDOUT: /* Should never happen, but... */ + usbhid_mark_busy(usbhid); clear_bit(HID_IN_RUNNING, &usbhid->iofl); hid_io_error(hid); return; @@ -239,16 +307,25 @@ static int hid_submit_out(struct hid_device *hid) report = usbhid->out[usbhid->outtail].report; raw_report = usbhid->out[usbhid->outtail].raw_report; - usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); - usbhid->urbout->dev = hid_to_usb_dev(hid); - memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length); - kfree(raw_report); + if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) { + usbhid->urbout->transfer_buffer_length = ((report->size - 1) >> 3) + 1 + (report->id > 0); + usbhid->urbout->dev = hid_to_usb_dev(hid); + memcpy(usbhid->outbuf, raw_report, usbhid->urbout->transfer_buffer_length); + kfree(raw_report); - dbg_hid("submitting out urb\n"); + dbg_hid("submitting out urb\n"); - if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { - err_hid("usb_submit_urb(out) failed"); - return -1; + if (usb_submit_urb(usbhid->urbout, GFP_ATOMIC)) { + err_hid("usb_submit_urb(out) failed"); + return -1; + } + } else { + /* + * queue work to wake up the device. + * as the work queue is freezeable, this is safe + * with respect to STD and STR + */ + queue_work(resumption_waker, &usbhid->restart_work); } return 0; @@ -266,41 +343,50 @@ static int hid_submit_ctrl(struct hid_device *hid) raw_report = usbhid->ctrl[usbhid->ctrltail].raw_report; dir = usbhid->ctrl[usbhid->ctrltail].dir; - len = ((report->size - 1) >> 3) + 1 + (report->id > 0); - if (dir == USB_DIR_OUT) { - usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); - usbhid->urbctrl->transfer_buffer_length = len; - memcpy(usbhid->ctrlbuf, raw_report, len); - kfree(raw_report); - } else { - int maxpacket, padlen; - - usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); - maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); - if (maxpacket > 0) { - padlen = DIV_ROUND_UP(len, maxpacket); - padlen *= maxpacket; - if (padlen > usbhid->bufsize) - padlen = usbhid->bufsize; - } else - padlen = 0; - usbhid->urbctrl->transfer_buffer_length = padlen; - } - usbhid->urbctrl->dev = hid_to_usb_dev(hid); + if (!test_bit(HID_REPORTED_IDLE, &usbhid->iofl)) { + len = ((report->size - 1) >> 3) + 1 + (report->id > 0); + if (dir == USB_DIR_OUT) { + usbhid->urbctrl->pipe = usb_sndctrlpipe(hid_to_usb_dev(hid), 0); + usbhid->urbctrl->transfer_buffer_length = len; + memcpy(usbhid->ctrlbuf, raw_report, len); + kfree(raw_report); + } else { + int maxpacket, padlen; + + usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0); + maxpacket = usb_maxpacket(hid_to_usb_dev(hid), usbhid->urbctrl->pipe, 0); + if (maxpacket > 0) { + padlen = DIV_ROUND_UP(len, maxpacket); + padlen *= maxpacket; + if (padlen > usbhid->bufsize) + padlen = usbhid->bufsize; + } else + padlen = 0; + usbhid->urbctrl->transfer_buffer_length = padlen; + } + usbhid->urbctrl->dev = hid_to_usb_dev(hid); - usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; - usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT; - usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id); - usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); - usbhid->cr->wLength = cpu_to_le16(len); + usbhid->cr->bRequestType = USB_TYPE_CLASS | USB_RECIP_INTERFACE | dir; + usbhid->cr->bRequest = (dir == USB_DIR_OUT) ? HID_REQ_SET_REPORT : HID_REQ_GET_REPORT; + usbhid->cr->wValue = cpu_to_le16(((report->type + 1) << 8) | report->id); + usbhid->cr->wIndex = cpu_to_le16(usbhid->ifnum); + usbhid->cr->wLength = cpu_to_le16(len); - dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n", - usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", - usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); + dbg_hid("submitting ctrl urb: %s wValue=0x%04x wIndex=0x%04x wLength=%u\n", + usbhid->cr->bRequest == HID_REQ_SET_REPORT ? "Set_Report" : "Get_Report", + usbhid->cr->wValue, usbhid->cr->wIndex, usbhid->cr->wLength); - if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { - err_hid("usb_submit_urb(ctrl) failed"); - return -1; + if (usb_submit_urb(usbhid->urbctrl, GFP_ATOMIC)) { + err_hid("usb_submit_urb(ctrl) failed"); + return -1; + } + } else { + /* + * queue work to wake up the device. + * as the work queue is freezeable, this is safe + * with respect to STD and STR + */ + queue_work(resumption_waker, &usbhid->restart_work); } return 0; @@ -332,7 +418,7 @@ static void hid_irq_out(struct urb *urb) "received\n", urb->status); } - spin_lock_irqsave(&usbhid->outlock, flags); + spin_lock_irqsave(&usbhid->lock, flags); if (unplug) usbhid->outtail = usbhid->outhead; @@ -344,12 +430,12 @@ static void hid_irq_out(struct urb *urb) clear_bit(HID_OUT_RUNNING, &usbhid->iofl); wake_up(&usbhid->wait); } - spin_unlock_irqrestore(&usbhid->outlock, flags); + spin_unlock_irqrestore(&usbhid->lock, flags); return; } clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - spin_unlock_irqrestore(&usbhid->outlock, flags); + spin_unlock_irqrestore(&usbhid->lock, flags); wake_up(&usbhid->wait); } @@ -361,12 +447,11 @@ static void hid_ctrl(struct urb *urb) { struct hid_device *hid = urb->context; struct usbhid_device *usbhid = hid->driver_data; - unsigned long flags; - int unplug = 0; + int unplug = 0, status = urb->status; - spin_lock_irqsave(&usbhid->ctrllock, flags); + spin_lock(&usbhid->lock); - switch (urb->status) { + switch (status) { case 0: /* success */ if (usbhid->ctrl[usbhid->ctrltail].dir == USB_DIR_IN) hid_input_report(urb->context, @@ -383,7 +468,7 @@ static void hid_ctrl(struct urb *urb) break; default: /* error */ dev_warn(&urb->dev->dev, "ctrl urb status %d " - "received\n", urb->status); + "received\n", status); } if (unplug) @@ -396,19 +481,18 @@ static void hid_ctrl(struct urb *urb) clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); wake_up(&usbhid->wait); } - spin_unlock_irqrestore(&usbhid->ctrllock, flags); + spin_unlock(&usbhid->lock); return; } clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); - spin_unlock_irqrestore(&usbhid->ctrllock, flags); + spin_unlock(&usbhid->lock); wake_up(&usbhid->wait); } -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) +void __usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) { int head; - unsigned long flags; struct usbhid_device *usbhid = hid->driver_data; int len = ((report->size - 1) >> 3) + 1 + (report->id > 0); @@ -416,18 +500,13 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns return; if (usbhid->urbout && dir == USB_DIR_OUT && report->type == HID_OUTPUT_REPORT) { - - spin_lock_irqsave(&usbhid->outlock, flags); - if ((head = (usbhid->outhead + 1) & (HID_OUTPUT_FIFO_SIZE - 1)) == usbhid->outtail) { - spin_unlock_irqrestore(&usbhid->outlock, flags); dev_warn(&hid->dev, "output queue full\n"); return; } usbhid->out[usbhid->outhead].raw_report = kmalloc(len, GFP_ATOMIC); if (!usbhid->out[usbhid->outhead].raw_report) { - spin_unlock_irqrestore(&usbhid->outlock, flags); dev_warn(&hid->dev, "output queueing failed\n"); return; } @@ -438,15 +517,10 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns if (!test_and_set_bit(HID_OUT_RUNNING, &usbhid->iofl)) if (hid_submit_out(hid)) clear_bit(HID_OUT_RUNNING, &usbhid->iofl); - - spin_unlock_irqrestore(&usbhid->outlock, flags); return; } - spin_lock_irqsave(&usbhid->ctrllock, flags); - if ((head = (usbhid->ctrlhead + 1) & (HID_CONTROL_FIFO_SIZE - 1)) == usbhid->ctrltail) { - spin_unlock_irqrestore(&usbhid->ctrllock, flags); dev_warn(&hid->dev, "control queue full\n"); return; } @@ -454,7 +528,6 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns if (dir == USB_DIR_OUT) { usbhid->ctrl[usbhid->ctrlhead].raw_report = kmalloc(len, GFP_ATOMIC); if (!usbhid->ctrl[usbhid->ctrlhead].raw_report) { - spin_unlock_irqrestore(&usbhid->ctrllock, flags); dev_warn(&hid->dev, "control queueing failed\n"); return; } @@ -467,15 +540,25 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns if (!test_and_set_bit(HID_CTRL_RUNNING, &usbhid->iofl)) if (hid_submit_ctrl(hid)) clear_bit(HID_CTRL_RUNNING, &usbhid->iofl); +} + +void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir) +{ + struct usbhid_device *usbhid = hid->driver_data; + unsigned long flags; - spin_unlock_irqrestore(&usbhid->ctrllock, flags); + spin_lock_irqsave(&usbhid->lock, flags); + __usbhid_submit_report(hid, report, dir); + spin_unlock_irqrestore(&usbhid->lock, flags); } EXPORT_SYMBOL_GPL(usbhid_submit_report); static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) { struct hid_device *hid = input_get_drvdata(dev); + struct usbhid_device *usbhid = hid->driver_data; struct hid_field *field; + unsigned long flags; int offset; if (type == EV_FF) @@ -490,6 +573,15 @@ static int usb_hidinput_input_event(struct input_dev *dev, unsigned int type, un } hid_set_field(field, offset, value); + if (value) { + spin_lock_irqsave(&usbhid->lock, flags); + usbhid->ledcount++; + spin_unlock_irqrestore(&usbhid->lock, flags); + } else { + spin_lock_irqsave(&usbhid->lock, flags); + usbhid->ledcount--; + spin_unlock_irqrestore(&usbhid->lock, flags); + } usbhid_submit_report(hid, field->report, USB_DIR_OUT); return 0; @@ -538,15 +630,22 @@ int usbhid_open(struct hid_device *hid) struct usbhid_device *usbhid = hid->driver_data; int res; + mutex_lock(&hid_open_mut); if (!hid->open++) { res = usb_autopm_get_interface(usbhid->intf); + /* the device must be awake to reliable request remote wakeup */ if (res < 0) { hid->open--; + mutex_unlock(&hid_open_mut); return -EIO; } + usbhid->intf->needs_remote_wakeup = 1; + if (hid_start_in(hid)) + hid_io_error(hid); + + usb_autopm_put_interface(usbhid->intf); } - if (hid_start_in(hid)) - hid_io_error(hid); + mutex_unlock(&hid_open_mut); return 0; } @@ -554,10 +653,22 @@ void usbhid_close(struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; + mutex_lock(&hid_open_mut); + + /* protecting hid->open to make sure we don't restart + * data acquistion due to a resumption we no longer + * care about + */ + spin_lock_irq(&usbhid->lock); if (!--hid->open) { + spin_unlock_irq(&usbhid->lock); usb_kill_urb(usbhid->urbin); - usb_autopm_put_interface(usbhid->intf); + flush_scheduled_work(); + usbhid->intf->needs_remote_wakeup = 0; + } else { + spin_unlock_irq(&usbhid->lock); } + mutex_unlock(&hid_open_mut); } /* @@ -687,6 +798,25 @@ static int usbhid_output_raw_report(struct hid_device *hid, __u8 *buf, size_t co return ret; } +static void usbhid_restart_queues(struct usbhid_device *usbhid) +{ + if (usbhid->urbout) + usbhid_restart_out_queue(usbhid); + usbhid_restart_ctrl_queue(usbhid); +} + +static void __usbhid_restart_queues(struct work_struct *work) +{ + struct usbhid_device *usbhid = + container_of(work, struct usbhid_device, restart_work); + int r; + + r = usb_autopm_get_interface(usbhid->intf); + if (r < 0) + return; + usb_autopm_put_interface(usbhid->intf); +} + static void hid_free_buffers(struct usb_device *dev, struct hid_device *hid) { struct usbhid_device *usbhid = hid->driver_data; @@ -711,6 +841,9 @@ static int usbhid_parse(struct hid_device *hid) quirks = usbhid_lookup_quirk(le16_to_cpu(dev->descriptor.idVendor), le16_to_cpu(dev->descriptor.idProduct)); + if (quirks & HID_QUIRK_IGNORE) + return -ENODEV; + /* Many keyboards and mice don't like to be polled for reports, * so we will always set the HID_QUIRK_NOGET flag for them. */ if (interface->desc.bInterfaceSubClass == USB_INTERFACE_SUBCLASS_BOOT) { @@ -850,11 +983,11 @@ static int usbhid_start(struct hid_device *hid) init_waitqueue_head(&usbhid->wait); INIT_WORK(&usbhid->reset_work, hid_reset); + INIT_WORK(&usbhid->restart_work, __usbhid_restart_queues); setup_timer(&usbhid->io_retry, hid_retry_timeout, (unsigned long) hid); - spin_lock_init(&usbhid->inlock); - spin_lock_init(&usbhid->outlock); - spin_lock_init(&usbhid->ctrllock); + spin_lock_init(&usbhid->lock); + spin_lock_init(&usbhid->lock); usbhid->intf = intf; usbhid->ifnum = interface->desc.bInterfaceNumber; @@ -906,15 +1039,14 @@ static void usbhid_stop(struct hid_device *hid) return; clear_bit(HID_STARTED, &usbhid->iofl); - spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ + spin_lock_irq(&usbhid->lock); /* Sync with error handler */ set_bit(HID_DISCONNECTED, &usbhid->iofl); - spin_unlock_irq(&usbhid->inlock); + spin_unlock_irq(&usbhid->lock); usb_kill_urb(usbhid->urbin); usb_kill_urb(usbhid->urbout); usb_kill_urb(usbhid->urbctrl); - del_timer_sync(&usbhid->io_retry); - cancel_work_sync(&usbhid->reset_work); + hid_cancel_delayed_stuff(usbhid); if (hid->claimed & HID_CLAIMED_INPUT) hidinput_disconnect(hid); @@ -935,12 +1067,28 @@ static void usbhid_stop(struct hid_device *hid) hid_free_buffers(hid_to_usb_dev(hid), hid); } +static int usbhid_power(struct hid_device *hid, int lvl) +{ + int r = 0; + + switch (lvl) { + case PM_HINT_FULLON: + r = usbhid_get_power(hid); + break; + case PM_HINT_NORMAL: + usbhid_put_power(hid); + break; + } + return r; +} + static struct hid_ll_driver usb_hid_driver = { .parse = usbhid_parse, .start = usbhid_start, .stop = usbhid_stop, .open = usbhid_open, .close = usbhid_close, + .power = usbhid_power, .hidinput_input_event = usb_hidinput_input_event, }; @@ -1049,19 +1197,126 @@ static void hid_disconnect(struct usb_interface *intf) kfree(usbhid); } +static void hid_cancel_delayed_stuff(struct usbhid_device *usbhid) +{ + del_timer_sync(&usbhid->io_retry); + cancel_work_sync(&usbhid->restart_work); + cancel_work_sync(&usbhid->reset_work); +} + +static void hid_cease_io(struct usbhid_device *usbhid) +{ + del_timer(&usbhid->io_retry); + usb_kill_urb(usbhid->urbin); + usb_kill_urb(usbhid->urbctrl); + usb_kill_urb(usbhid->urbout); +} + +/* Treat USB reset pretty much the same as suspend/resume */ +static int hid_pre_reset(struct usb_interface *intf) +{ + struct hid_device *hid = usb_get_intfdata(intf); + struct usbhid_device *usbhid = hid->driver_data; + + spin_lock_irq(&usbhid->lock); + set_bit(HID_RESET_PENDING, &usbhid->iofl); + spin_unlock_irq(&usbhid->lock); + cancel_work_sync(&usbhid->restart_work); + hid_cease_io(usbhid); + + return 0; +} + +/* Same routine used for post_reset and reset_resume */ +static int hid_post_reset(struct usb_interface *intf) +{ + struct usb_device *dev = interface_to_usbdev (intf); + struct hid_device *hid = usb_get_intfdata(intf); + struct usbhid_device *usbhid = hid->driver_data; + int status; + + spin_lock_irq(&usbhid->lock); + clear_bit(HID_RESET_PENDING, &usbhid->iofl); + spin_unlock_irq(&usbhid->lock); + hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); + /* FIXME: Any more reinitialization needed? */ + status = hid_start_in(hid); + if (status < 0) + hid_io_error(hid); + usbhid_restart_queues(usbhid); + + return 0; +} + +int usbhid_get_power(struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + return usb_autopm_get_interface(usbhid->intf); +} + +void usbhid_put_power(struct hid_device *hid) +{ + struct usbhid_device *usbhid = hid->driver_data; + + usb_autopm_put_interface(usbhid->intf); +} + + +#ifdef CONFIG_PM static int hid_suspend(struct usb_interface *intf, pm_message_t message) { - struct hid_device *hid = usb_get_intfdata (intf); + struct hid_device *hid = usb_get_intfdata(intf); struct usbhid_device *usbhid = hid->driver_data; + struct usb_device *udev = interface_to_usbdev(intf); + int status; - if (!test_bit(HID_STARTED, &usbhid->iofl)) - return 0; + if (udev->auto_pm) { + spin_lock_irq(&usbhid->lock); /* Sync with error handler */ + if (!test_bit(HID_RESET_PENDING, &usbhid->iofl) + && !test_bit(HID_CLEAR_HALT, &usbhid->iofl) + && !test_bit(HID_OUT_RUNNING, &usbhid->iofl) + && !test_bit(HID_CTRL_RUNNING, &usbhid->iofl) + && !test_bit(HID_KEYS_PRESSED, &usbhid->iofl) + && (!usbhid->ledcount || ignoreled)) + { + set_bit(HID_REPORTED_IDLE, &usbhid->iofl); + spin_unlock_irq(&usbhid->lock); + } else { + usbhid_mark_busy(usbhid); + spin_unlock_irq(&usbhid->lock); + return -EBUSY; + } - spin_lock_irq(&usbhid->inlock); /* Sync with error handler */ - set_bit(HID_SUSPENDED, &usbhid->iofl); - spin_unlock_irq(&usbhid->inlock); - del_timer_sync(&usbhid->io_retry); - usb_kill_urb(usbhid->urbin); + } else { + spin_lock_irq(&usbhid->lock); + set_bit(HID_REPORTED_IDLE, &usbhid->iofl); + spin_unlock_irq(&usbhid->lock); + if (usbhid_wait_io(hid) < 0) + return -EIO; + } + + if (!ignoreled && udev->auto_pm) { + spin_lock_irq(&usbhid->lock); + if (test_bit(HID_LED_ON, &usbhid->iofl)) { + spin_unlock_irq(&usbhid->lock); + usbhid_mark_busy(usbhid); + return -EBUSY; + } + spin_unlock_irq(&usbhid->lock); + } + + hid_cancel_delayed_stuff(usbhid); + hid_cease_io(usbhid); + + if (udev->auto_pm && test_bit(HID_KEYS_PRESSED, &usbhid->iofl)) { + /* lost race against keypresses */ + status = hid_start_in(hid); + if (status < 0) + hid_io_error(hid); + usbhid_mark_busy(usbhid); + return -EBUSY; + } dev_dbg(&intf->dev, "suspend\n"); return 0; } @@ -1075,32 +1330,33 @@ static int hid_resume(struct usb_interface *intf) if (!test_bit(HID_STARTED, &usbhid->iofl)) return 0; - clear_bit(HID_SUSPENDED, &usbhid->iofl); + clear_bit(HID_REPORTED_IDLE, &usbhid->iofl); + usbhid_mark_busy(usbhid); + + if (test_bit(HID_CLEAR_HALT, &usbhid->iofl) || + test_bit(HID_RESET_PENDING, &usbhid->iofl)) + schedule_work(&usbhid->reset_work); usbhid->retry_delay = 0; status = hid_start_in(hid); - dev_dbg(&intf->dev, "resume status %d\n", status); - return status; -} + if (status < 0) + hid_io_error(hid); + usbhid_restart_queues(usbhid); -/* Treat USB reset pretty much the same as suspend/resume */ -static int hid_pre_reset(struct usb_interface *intf) -{ - /* FIXME: What if the interface is already suspended? */ - hid_suspend(intf, PMSG_ON); + dev_dbg(&intf->dev, "resume status %d\n", status); return 0; } -/* Same routine used for post_reset and reset_resume */ -static int hid_post_reset(struct usb_interface *intf) +static int hid_reset_resume(struct usb_interface *intf) { - struct usb_device *dev = interface_to_usbdev (intf); - - hid_set_idle(dev, intf->cur_altsetting->desc.bInterfaceNumber, 0, 0); - /* FIXME: Any more reinitialization needed? */ + struct hid_device *hid = usb_get_intfdata(intf); + struct usbhid_device *usbhid = hid->driver_data; - return hid_resume(intf); + clear_bit(HID_REPORTED_IDLE, &usbhid->iofl); + return hid_post_reset(intf); } +#endif /* CONFIG_PM */ + static struct usb_device_id hid_usb_ids [] = { { .match_flags = USB_DEVICE_ID_MATCH_INT_CLASS, .bInterfaceClass = USB_INTERFACE_CLASS_HID }, @@ -1113,9 +1369,11 @@ static struct usb_driver hid_driver = { .name = "usbhid", .probe = hid_probe, .disconnect = hid_disconnect, +#ifdef CONFIG_PM .suspend = hid_suspend, .resume = hid_resume, - .reset_resume = hid_post_reset, + .reset_resume = hid_reset_resume, +#endif .pre_reset = hid_pre_reset, .post_reset = hid_post_reset, .id_table = hid_usb_ids, @@ -1134,7 +1392,11 @@ static struct hid_driver hid_usb_driver = { static int __init hid_init(void) { - int retval; + int retval = -ENOMEM; + + resumption_waker = create_freezeable_workqueue("usbhid_resumer"); + if (!resumption_waker) + goto no_queue; retval = hid_register_driver(&hid_usb_driver); if (retval) goto hid_register_fail; @@ -1158,6 +1420,8 @@ hiddev_init_fail: usbhid_quirks_init_fail: hid_unregister_driver(&hid_usb_driver); hid_register_fail: + destroy_workqueue(resumption_waker); +no_queue: return retval; } @@ -1167,6 +1431,7 @@ static void __exit hid_exit(void) hiddev_exit(); usbhid_quirks_exit(); hid_unregister_driver(&hid_usb_driver); + destroy_workqueue(resumption_waker); } module_init(hid_init); diff --git a/drivers/hid/usbhid/hiddev.c b/drivers/hid/usbhid/hiddev.c index aa214170baf..e9b436d2d94 100644 --- a/drivers/hid/usbhid/hiddev.c +++ b/drivers/hid/usbhid/hiddev.c @@ -246,10 +246,12 @@ static int hiddev_release(struct inode * inode, struct file * file) spin_unlock_irqrestore(&list->hiddev->list_lock, flags); if (!--list->hiddev->open) { - if (list->hiddev->exist) + if (list->hiddev->exist) { usbhid_close(list->hiddev->hid); - else + usbhid_put_power(list->hiddev->hid); + } else { kfree(list->hiddev); + } } kfree(list); @@ -300,6 +302,17 @@ static int hiddev_open(struct inode *inode, struct file *file) list_add_tail(&list->node, &hiddev_table[i]->list); spin_unlock_irq(&list->hiddev->list_lock); + if (!list->hiddev->open++) + if (list->hiddev->exist) { + struct hid_device *hid = hiddev_table[i]->hid; + res = usbhid_get_power(hid); + if (res < 0) { + res = -EIO; + goto bail; + } + usbhid_open(hid); + } + return 0; bail: file->private_data = NULL; @@ -875,16 +888,21 @@ int hiddev_connect(struct hid_device *hid, unsigned int force) hiddev->hid = hid; hiddev->exist = 1; + /* when lock_kernel() usage is fixed in usb_open(), + * we could also fix it here */ + lock_kernel(); retval = usb_register_dev(usbhid->intf, &hiddev_class); if (retval) { err_hid("Not able to get a minor for this device."); hid->hiddev = NULL; + unlock_kernel(); kfree(hiddev); return -1; } else { hid->minor = usbhid->intf->minor; hiddev_table[usbhid->intf->minor - HIDDEV_MINOR_BASE] = hiddev; } + unlock_kernel(); return 0; } diff --git a/drivers/hid/usbhid/usbhid.h b/drivers/hid/usbhid/usbhid.h index 9eb30564be9..08f505ca2e3 100644 --- a/drivers/hid/usbhid/usbhid.h +++ b/drivers/hid/usbhid/usbhid.h @@ -38,7 +38,10 @@ int usbhid_wait_io(struct hid_device* hid); void usbhid_close(struct hid_device *hid); int usbhid_open(struct hid_device *hid); void usbhid_init_reports(struct hid_device *hid); -void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, unsigned char dir); +void usbhid_submit_report +(struct hid_device *hid, struct hid_report *report, unsigned char dir); +int usbhid_get_power(struct hid_device *hid); +void usbhid_put_power(struct hid_device *hid); /* iofl flags */ #define HID_CTRL_RUNNING 1 @@ -49,6 +52,9 @@ void usbhid_submit_report(struct hid_device *hid, struct hid_report *report, uns #define HID_CLEAR_HALT 6 #define HID_DISCONNECTED 7 #define HID_STARTED 8 +#define HID_REPORTED_IDLE 9 +#define HID_KEYS_PRESSED 10 +#define HID_LED_ON 11 /* * USB-specific HID struct, to be pointed to @@ -66,7 +72,6 @@ struct usbhid_device { struct urb *urbin; /* Input URB */ char *inbuf; /* Input buffer */ dma_addr_t inbuf_dma; /* Input buffer dma */ - spinlock_t inlock; /* Input fifo spinlock */ struct urb *urbctrl; /* Control URB */ struct usb_ctrlrequest *cr; /* Control request struct */ @@ -75,21 +80,22 @@ struct usbhid_device { unsigned char ctrlhead, ctrltail; /* Control fifo head & tail */ char *ctrlbuf; /* Control buffer */ dma_addr_t ctrlbuf_dma; /* Control buffer dma */ - spinlock_t ctrllock; /* Control fifo spinlock */ struct urb *urbout; /* Output URB */ struct hid_output_fifo out[HID_CONTROL_FIFO_SIZE]; /* Output pipe fifo */ unsigned char outhead, outtail; /* Output pipe fifo head & tail */ char *outbuf; /* Output buffer */ dma_addr_t outbuf_dma; /* Output buffer dma */ - spinlock_t outlock; /* Output fifo spinlock */ + spinlock_t lock; /* fifo spinlock */ unsigned long iofl; /* I/O flags (CTRL_RUNNING, OUT_RUNNING) */ struct timer_list io_retry; /* Retry timer */ unsigned long stop_retry; /* Time to give up, in jiffies */ unsigned int retry_delay; /* Delay length in ms */ struct work_struct reset_work; /* Task context for resets */ + struct work_struct restart_work; /* waking up for output to be done in a task */ wait_queue_head_t wait; /* For sleeping */ + int ledcount; /* counting the number of active leds */ }; #define hid_to_usb_dev(hid_dev) \ diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index 18a1ba88816..e2107e533ed 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -1,7 +1,7 @@ /* * f75375s.c - driver for the Fintek F75375/SP and F75373 * hardware monitoring features - * Copyright (C) 2006-2007 Riku Voipio <riku.voipio@movial.fi> + * Copyright (C) 2006-2007 Riku Voipio * * Datasheets available at: * @@ -721,7 +721,7 @@ static void __exit sensors_f75375_exit(void) i2c_del_driver(&f75375_driver); } -MODULE_AUTHOR("Riku Voipio <riku.voipio@movial.fi>"); +MODULE_AUTHOR("Riku Voipio"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("F75373/F75375 hardware monitoring driver"); diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig index 5f9d860925a..cd50c00ab20 100644 --- a/drivers/input/Kconfig +++ b/drivers/input/Kconfig @@ -143,7 +143,7 @@ config INPUT_APMPOWER ---help--- Say Y here if you want suspend key events to trigger a user requested suspend through APM. This is useful on embedded - systems where such behviour is desired without userspace + systems where such behaviour is desired without userspace interaction. If unsure, say N. To compile this driver as a module, choose M here: the diff --git a/drivers/input/mouse/gpio_mouse.c b/drivers/input/mouse/gpio_mouse.c index 0db8d16c5ed..5e5eb88d8d1 100644 --- a/drivers/input/mouse/gpio_mouse.c +++ b/drivers/input/mouse/gpio_mouse.c @@ -18,7 +18,7 @@ /* * Timer function which is run every scan_ms ms when the device is opened. - * The dev input varaible is set to the the input_dev pointer. + * The dev input variable is set to the the input_dev pointer. */ static void gpio_mouse_scan(struct input_polled_dev *dev) { diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 7c27c8b9b6d..056ac77e2cf 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -295,7 +295,7 @@ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); -/* Sysfs conventions report temperatures in millidegrees Celcius. +/* Sysfs conventions report temperatures in millidegrees Celsius. * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high * accuracy scheme without calibration data. For now we won't try either; * userspace sees raw sensor values, and must scale/calibrate appropriately. diff --git a/drivers/isdn/mISDN/Kconfig b/drivers/isdn/mISDN/Kconfig index 4938355c407..1747a02a019 100644 --- a/drivers/isdn/mISDN/Kconfig +++ b/drivers/isdn/mISDN/Kconfig @@ -14,13 +14,15 @@ config MISDN_DSP depends on MISDN help Enable support for digital audio processing capability. + This module may be used for special applications that require - cross connecting of bchannels, conferencing, dtmf decoding + cross connecting of bchannels, conferencing, dtmf decoding, echo cancelation, tone generation, and Blowfish encryption and - decryption. - It may use hardware features if available. + decryption. It may use hardware features if available. + E.g. it is required for PBX4Linux. Go to http://isdn.eversberg.eu - and get more informations about this module and it's usage. + and get more information about this module and its usage. + If unsure, say 'N'. config MISDN_L1OIP diff --git a/drivers/isdn/mISDN/l1oip_codec.c b/drivers/isdn/mISDN/l1oip_codec.c index 2ec4b28d9ed..e4ecba3d48d 100644 --- a/drivers/isdn/mISDN/l1oip_codec.c +++ b/drivers/isdn/mISDN/l1oip_codec.c @@ -331,7 +331,7 @@ l1oip_4bit_alloc(int ulaw) /* alloc conversion tables */ table_com = vmalloc(65536); table_dec = vmalloc(512); - if (!table_com | !table_dec) { + if (!table_com || !table_dec) { l1oip_4bit_free(); return -ENOMEM; } diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 556aeca0d86..d9db17624f1 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -100,7 +100,7 @@ config LEDS_HP6XX tristate "LED Support for the HP Jornada 6xx" depends on LEDS_CLASS && SH_HP6XX help - This option enables led support for the handheld + This option enables LED support for the handheld HP Jornada 620/660/680/690. config LEDS_PCA9532 @@ -108,7 +108,7 @@ config LEDS_PCA9532 depends on LEDS_CLASS && I2C && INPUT && EXPERIMENTAL help This option enables support for NXP pca9532 - led controller. It is generally only usefull + LED controller. It is generally only useful as a platform driver config LEDS_GPIO @@ -144,7 +144,7 @@ config LEDS_CLEVO_MAIL Positivo Mobile (Clevo M5x0V) If your model is not listed here you can try the "nodetect" - module paramter. + module parameter. To compile this driver as a module, choose M here: the module will be called leds-clevo-mail. diff --git a/drivers/leds/leds-pca9532.c b/drivers/leds/leds-pca9532.c index 76ec7498e2d..bd3b431c971 100644 --- a/drivers/leds/leds-pca9532.c +++ b/drivers/leds/leds-pca9532.c @@ -1,7 +1,7 @@ /* * pca9532.c - 16-bit Led dimmer * - * Copyright (C) 2008 Riku Voipio <riku.voipio@movial.fi> + * Copyright (C) 2008 Riku Voipio * * 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 @@ -367,7 +367,7 @@ static void __exit pca9532_exit(void) i2c_del_driver(&pca9532_driver); } -MODULE_AUTHOR("Riku Voipio <riku.voipio@movial.fi>"); +MODULE_AUTHOR("Riku Voipio"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("PCA 9532 LED dimmer"); diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 52c3f65b12d..607d319ce8e 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -148,7 +148,7 @@ config MEDIA_TUNER_XC5000 default m if MEDIA_TUNER_CUSTOMISE help A driver for the silicon tuner XC5000 from Xceive. - This device is only used inside a SiP called togther with a + This device is only used inside a SiP called together with a demodulator for now. config MEDIA_TUNER_MXL5005S diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index a206cee23f7..a486a7f81fa 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -479,7 +479,7 @@ config DVB_TUNER_DIB0070 default m if DVB_FE_CUSTOMISE help A driver for the silicon baseband tuner DiB0070 from DiBcom. - This device is only used inside a SiP called togther with a + This device is only used inside a SiP called together with a demodulator for now. comment "SEC control devices for DVB-S" diff --git a/drivers/media/dvb/frontends/drx397xD.c b/drivers/media/dvb/frontends/drx397xD.c index 1e81e713df6..172f1f928f0 100644 --- a/drivers/media/dvb/frontends/drx397xD.c +++ b/drivers/media/dvb/frontends/drx397xD.c @@ -74,7 +74,7 @@ static struct { } fw[] = { #define _FW_ENTRY(a, b, c) { \ .name = a, \ - .file = 0, \ + .file = NULL, \ .lock = __RW_LOCK_UNLOCKED(fw[c].lock), \ .refcnt = 0, \ .data = { } } diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 06a2b0f7737..75f35dbb11d 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -88,7 +88,7 @@ config MENELAUS help If you say yes here you get support for the Texas Instruments TWL92330/Menelaus Power Management chip. This include voltage - regulators, Dual slot memory card tranceivers, real-time clock + regulators, Dual slot memory card transceivers, real-time clock and other features that are often used in portable devices like cell phones and PDAs. diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 0b92b2f6ea6..6d1ac180f6e 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -18,8 +18,8 @@ config ATMEL_PWM depends on AVR32 || ARCH_AT91SAM9263 || ARCH_AT91SAM9RL || ARCH_AT91CAP9 help This option enables device driver support for the PWM channels - on certain Atmel prcoessors. Pulse Width Modulation is used for - purposes including software controlled power-efficent backlights + on certain Atmel processors. Pulse Width Modulation is used for + purposes including software controlled power-efficient backlights on LCD displays, motor control, and waveform generation. config ATMEL_TCLIB @@ -142,7 +142,7 @@ config ATMEL_SSC tristate "Device driver for Atmel SSC peripheral" depends on AVR32 || ARCH_AT91 ---help--- - This option enables device driver support for Atmel Syncronized + This option enables device driver support for Atmel Synchronized Serial Communication peripheral (SSC). The SSC peripheral supports a wide variety of serial frame based diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 513eb09a638..fe8041e619e 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -41,6 +41,8 @@ #include "queue.h" +MODULE_ALIAS("mmc:block"); + /* * max 8 partitions per card */ diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index f210a8ee686..bdb165f9304 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -84,6 +84,14 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) } retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card)); + if (retval) + return retval; + + /* + * Request the mmc_block device. Note: that this is a direct request + * for the module it carries no information as to what is inserted. + */ + retval = add_uevent_var(env, "MODALIAS=mmc:block"); return retval; } diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 1445ea8f10a..fa073ab3fa3 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -298,6 +298,21 @@ void mmc_set_data_timeout(struct mmc_data *data, const struct mmc_card *card) data->timeout_clks = 0; } } + /* + * Some cards need very high timeouts if driven in SPI mode. + * The worst observed timeout was 900ms after writing a + * continuous stream of data until the internal logic + * overflowed. + */ + if (mmc_host_is_spi(card->host)) { + if (data->flags & MMC_DATA_WRITE) { + if (data->timeout_ns < 1000000000) + data->timeout_ns = 1000000000; /* 1s */ + } else { + if (data->timeout_ns < 100000000) + data->timeout_ns = 100000000; /* 100ms */ + } + } } EXPORT_SYMBOL(mmc_set_data_timeout); @@ -915,6 +930,7 @@ void mmc_stop_host(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); #endif + cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); mmc_bus_get(host); @@ -942,6 +958,7 @@ void mmc_stop_host(struct mmc_host *host) */ int mmc_suspend_host(struct mmc_host *host, pm_message_t state) { + cancel_delayed_work(&host->detect); mmc_flush_scheduled_work(); mmc_bus_get(host); @@ -975,6 +992,7 @@ int mmc_resume_host(struct mmc_host *host) mmc_bus_get(host); if (host->bus_ops && !host->bus_dead) { mmc_power_up(host); + mmc_select_voltage(host, host->ocr); BUG_ON(!host->bus_ops->resume); host->bus_ops->resume(host); } diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 1237bb4c722..610dbd1fcc8 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -184,6 +184,68 @@ static int mmc_dbg_card_status_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(mmc_dbg_card_status_fops, mmc_dbg_card_status_get, NULL, "%08llx\n"); +#define EXT_CSD_STR_LEN 1025 + +static int mmc_ext_csd_open(struct inode *inode, struct file *filp) +{ + struct mmc_card *card = inode->i_private; + char *buf; + ssize_t n = 0; + u8 *ext_csd; + int err, i; + + buf = kmalloc(EXT_CSD_STR_LEN + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ext_csd = kmalloc(512, GFP_KERNEL); + if (!ext_csd) { + err = -ENOMEM; + goto out_free; + } + + mmc_claim_host(card->host); + err = mmc_send_ext_csd(card, ext_csd); + mmc_release_host(card->host); + if (err) + goto out_free; + + for (i = 511; i >= 0; i--) + n += sprintf(buf + n, "%02x", ext_csd[i]); + n += sprintf(buf + n, "\n"); + BUG_ON(n != EXT_CSD_STR_LEN); + + filp->private_data = buf; + kfree(ext_csd); + return 0; + +out_free: + kfree(buf); + kfree(ext_csd); + return err; +} + +static ssize_t mmc_ext_csd_read(struct file *filp, char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + char *buf = filp->private_data; + + return simple_read_from_buffer(ubuf, cnt, ppos, + buf, EXT_CSD_STR_LEN); +} + +static int mmc_ext_csd_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static struct file_operations mmc_dbg_ext_csd_fops = { + .open = mmc_ext_csd_open, + .read = mmc_ext_csd_read, + .release = mmc_ext_csd_release, +}; + void mmc_add_card_debugfs(struct mmc_card *card) { struct mmc_host *host = card->host; @@ -211,6 +273,11 @@ void mmc_add_card_debugfs(struct mmc_card *card) &mmc_dbg_card_status_fops)) goto err; + if (mmc_card_mmc(card)) + if (!debugfs_create_file("ext_csd", S_IRUSR, root, card, + &mmc_dbg_ext_csd_fops)) + goto err; + return; err: diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index 956bd767750..963f2937c5e 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -223,10 +223,18 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func) if (tpl_code == 0xff) break; + /* null entries have no link field or data */ + if (tpl_code == 0x00) + continue; + ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_link); if (ret) break; + /* a size of 0xff also means we're done */ + if (tpl_link == 0xff) + break; + this = kmalloc(sizeof(*this) + tpl_link, GFP_KERNEL); if (!this) return -ENOMEM; diff --git a/drivers/mmc/core/sdio_ops.c b/drivers/mmc/core/sdio_ops.c index c8fa095a448..4eb7825fd1a 100644 --- a/drivers/mmc/core/sdio_ops.c +++ b/drivers/mmc/core/sdio_ops.c @@ -76,6 +76,10 @@ int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, BUG_ON(!card); BUG_ON(fn > 7); + /* sanity check */ + if (addr & ~0x1FFFF) + return -EINVAL; + memset(&cmd, 0, sizeof(struct mmc_command)); cmd.opcode = SD_IO_RW_DIRECT; @@ -125,6 +129,10 @@ int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn, WARN_ON(blocks == 0); WARN_ON(blksz == 0); + /* sanity check */ + if (addr & ~0x1FFFF) + return -EINVAL; + memset(&mrq, 0, sizeof(struct mmc_request)); memset(&cmd, 0, sizeof(struct mmc_command)); memset(&data, 0, sizeof(struct mmc_data)); diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 99d4b28d52e..b4cf691f3f6 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -37,6 +37,13 @@ config MMC_SDHCI If unsure, say N. +config MMC_SDHCI_IO_ACCESSORS + bool + depends on MMC_SDHCI + help + This is silent Kconfig symbol that is selected by the drivers that + need to overwrite SDHCI IO memory accessors. + config MMC_SDHCI_PCI tristate "SDHCI support on PCI bus" depends on MMC_SDHCI && PCI @@ -65,6 +72,17 @@ config MMC_RICOH_MMC If unsure, say Y. +config MMC_SDHCI_OF + tristate "SDHCI support on OpenFirmware platforms" + depends on MMC_SDHCI && PPC_OF + select MMC_SDHCI_IO_ACCESSORS + help + This selects the OF support for Secure Digital Host Controller + Interfaces. So far, only the Freescale eSDHC controller is known + to exist on OF platforms. + + If unsure, say N. + config MMC_OMAP tristate "TI OMAP Multimedia Card Interface support" depends on ARCH_OMAP @@ -171,13 +189,24 @@ config MMC_TIFM_SD To compile this driver as a module, choose M here: the module will be called tifm_sd. +config MMC_MVSDIO + tristate "Marvell MMC/SD/SDIO host driver" + depends on PLAT_ORION + ---help--- + This selects the Marvell SDIO host driver. + SDIO may currently be found on the Kirkwood 88F6281 and 88F6192 + SoC controllers. + + To compile this driver as a module, choose M here: the + module will be called mvsdio. + config MMC_SPI tristate "MMC/SD/SDIO over SPI" depends on SPI_MASTER && !HIGHMEM && HAS_DMA select CRC7 select CRC_ITU_T help - Some systems accss MMC/SD/SDIO cards using a SPI controller + Some systems access MMC/SD/SDIO cards using a SPI controller instead of using a "native" MMC/SD/SDIO controller. This has a disadvantage of being relatively high overhead, but a compensating advantage of working on many systems without dedicated MMC/SD/SDIO diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index dedec55861d..970a997620e 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_MMC_MXC) += mxcmmc.o obj-$(CONFIG_MMC_SDHCI) += sdhci.o obj-$(CONFIG_MMC_SDHCI_PCI) += sdhci-pci.o obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o +obj-$(CONFIG_MMC_SDHCI_OF) += sdhci-of.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o @@ -20,6 +21,7 @@ obj-$(CONFIG_MMC_OMAP_HS) += omap_hsmmc.o obj-$(CONFIG_MMC_AT91) += at91_mci.o obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o +obj-$(CONFIG_MMC_MVSDIO) += mvsdio.o obj-$(CONFIG_MMC_SPI) += mmc_spi.o ifeq ($(CONFIG_OF),y) obj-$(CONFIG_MMC_SPI) += of_mmc_spi.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index e94e92001e7..cf6a100bb38 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -812,7 +812,7 @@ static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) slot->sdc_reg |= MCI_SDCBUS_1BIT; break; case MMC_BUS_WIDTH_4: - slot->sdc_reg = MCI_SDCBUS_4BIT; + slot->sdc_reg |= MCI_SDCBUS_4BIT; break; } diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c index 87e211df68a..72f8bde4877 100644 --- a/drivers/mmc/host/mmc_spi.c +++ b/drivers/mmc/host/mmc_spi.c @@ -279,8 +279,11 @@ static int mmc_spi_response_get(struct mmc_spi_host *host, * so it can always DMA directly into the target buffer. * It'd probably be better to memcpy() the first chunk and * avoid extra i/o calls... + * + * Note we check for more than 8 bytes, because in practice, + * some SD cards are slow... */ - for (i = 2; i < 9; i++) { + for (i = 2; i < 16; i++) { value = mmc_spi_readbytes(host, 1); if (value < 0) goto done; @@ -609,6 +612,7 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, struct spi_device *spi = host->spi; int status, i; struct scratch *scratch = host->data; + u32 pattern; if (host->mmc->use_spi_crc) scratch->crc_val = cpu_to_be16( @@ -636,8 +640,27 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, * doesn't necessarily tell whether the write operation succeeded; * it just says if the transmission was ok and whether *earlier* * writes succeeded; see the standard. + * + * In practice, there are (even modern SDHC-)cards which are late + * in sending the response, and miss the time frame by a few bits, + * so we have to cope with this situation and check the response + * bit-by-bit. Arggh!!! */ - switch (SPI_MMC_RESPONSE_CODE(scratch->status[0])) { + pattern = scratch->status[0] << 24; + pattern |= scratch->status[1] << 16; + pattern |= scratch->status[2] << 8; + pattern |= scratch->status[3]; + + /* First 3 bit of pattern are undefined */ + pattern |= 0xE0000000; + + /* left-adjust to leading 0 bit */ + while (pattern & 0x80000000) + pattern <<= 1; + /* right-adjust for pattern matching. Code is in bit 4..0 now. */ + pattern >>= 27; + + switch (pattern) { case SPI_RESPONSE_ACCEPTED: status = 0; break; @@ -668,8 +691,9 @@ mmc_spi_writeblock(struct mmc_spi_host *host, struct spi_transfer *t, /* Return when not busy. If we didn't collect that status yet, * we'll need some more I/O. */ - for (i = 1; i < sizeof(scratch->status); i++) { - if (scratch->status[i] != 0) + for (i = 4; i < sizeof(scratch->status); i++) { + /* card is non-busy if the most recent bit is 1 */ + if (scratch->status[i] & 0x01) return 0; } return mmc_spi_wait_unbusy(host, timeout); @@ -1204,10 +1228,12 @@ static int mmc_spi_probe(struct spi_device *spi) /* MMC and SD specs only seem to care that sampling is on the * rising edge ... meaning SPI modes 0 or 3. So either SPI mode - * should be legit. We'll use mode 0 since it seems to be a - * bit less troublesome on some hardware ... unclear why. + * should be legit. We'll use mode 0 since the steady state is 0, + * which is appropriate for hotplugging, unless the platform data + * specify mode 3 (if hardware is not compatible to mode 0). */ - spi->mode = SPI_MODE_0; + if (spi->mode != SPI_MODE_3) + spi->mode = SPI_MODE_0; spi->bits_per_word = 8; status = spi_setup(spi); diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c new file mode 100644 index 00000000000..b5c375d94ab --- /dev/null +++ b/drivers/mmc/host/mvsdio.c @@ -0,0 +1,885 @@ +/* + * Marvell MMC/SD/SDIO driver + * + * Authors: Maen Suleiman, Nicolas Pitre + * Copyright (C) 2008-2009 Marvell Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/platform_device.h> +#include <linux/mbus.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/scatterlist.h> +#include <linux/irq.h> +#include <linux/gpio.h> +#include <linux/mmc/host.h> + +#include <asm/sizes.h> +#include <asm/unaligned.h> +#include <plat/mvsdio.h> + +#include "mvsdio.h" + +#define DRIVER_NAME "mvsdio" + +static int maxfreq = MVSD_CLOCKRATE_MAX; +static int nodma; + +struct mvsd_host { + void __iomem *base; + struct mmc_request *mrq; + spinlock_t lock; + unsigned int xfer_mode; + unsigned int intr_en; + unsigned int ctrl; + unsigned int pio_size; + void *pio_ptr; + unsigned int sg_frags; + unsigned int ns_per_clk; + unsigned int clock; + unsigned int base_clock; + struct timer_list timer; + struct mmc_host *mmc; + struct device *dev; + struct resource *res; + int irq; + int gpio_card_detect; + int gpio_write_protect; +}; + +#define mvsd_write(offs, val) writel(val, iobase + (offs)) +#define mvsd_read(offs) readl(iobase + (offs)) + +static int mvsd_setup_data(struct mvsd_host *host, struct mmc_data *data) +{ + void __iomem *iobase = host->base; + unsigned int tmout; + int tmout_index; + + /* If timeout=0 then maximum timeout index is used. */ + tmout = DIV_ROUND_UP(data->timeout_ns, host->ns_per_clk); + tmout += data->timeout_clks; + tmout_index = fls(tmout - 1) - 12; + if (tmout_index < 0) + tmout_index = 0; + if (tmout_index > MVSD_HOST_CTRL_TMOUT_MAX) + tmout_index = MVSD_HOST_CTRL_TMOUT_MAX; + + dev_dbg(host->dev, "data %s at 0x%08x: blocks=%d blksz=%d tmout=%u (%d)\n", + (data->flags & MMC_DATA_READ) ? "read" : "write", + (u32)sg_virt(data->sg), data->blocks, data->blksz, + tmout, tmout_index); + + host->ctrl &= ~MVSD_HOST_CTRL_TMOUT_MASK; + host->ctrl |= MVSD_HOST_CTRL_TMOUT(tmout_index); + mvsd_write(MVSD_HOST_CTRL, host->ctrl); + mvsd_write(MVSD_BLK_COUNT, data->blocks); + mvsd_write(MVSD_BLK_SIZE, data->blksz); + + if (nodma || (data->blksz | data->sg->offset) & 3) { + /* + * We cannot do DMA on a buffer which offset or size + * is not aligned on a 4-byte boundary. + */ + host->pio_size = data->blocks * data->blksz; + host->pio_ptr = sg_virt(data->sg); + if (!nodma) + printk(KERN_DEBUG "%s: fallback to PIO for data " + "at 0x%p size %d\n", + mmc_hostname(host->mmc), + host->pio_ptr, host->pio_size); + return 1; + } else { + dma_addr_t phys_addr; + int dma_dir = (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE; + host->sg_frags = dma_map_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, dma_dir); + phys_addr = sg_dma_address(data->sg); + mvsd_write(MVSD_SYS_ADDR_LOW, (u32)phys_addr & 0xffff); + mvsd_write(MVSD_SYS_ADDR_HI, (u32)phys_addr >> 16); + return 0; + } +} + +static void mvsd_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct mvsd_host *host = mmc_priv(mmc); + void __iomem *iobase = host->base; + struct mmc_command *cmd = mrq->cmd; + u32 cmdreg = 0, xfer = 0, intr = 0; + unsigned long flags; + + BUG_ON(host->mrq != NULL); + host->mrq = mrq; + + dev_dbg(host->dev, "cmd %d (hw state 0x%04x)\n", + cmd->opcode, mvsd_read(MVSD_HW_STATE)); + + cmdreg = MVSD_CMD_INDEX(cmd->opcode); + + if (cmd->flags & MMC_RSP_BUSY) + cmdreg |= MVSD_CMD_RSP_48BUSY; + else if (cmd->flags & MMC_RSP_136) + cmdreg |= MVSD_CMD_RSP_136; + else if (cmd->flags & MMC_RSP_PRESENT) + cmdreg |= MVSD_CMD_RSP_48; + else + cmdreg |= MVSD_CMD_RSP_NONE; + + if (cmd->flags & MMC_RSP_CRC) + cmdreg |= MVSD_CMD_CHECK_CMDCRC; + + if (cmd->flags & MMC_RSP_OPCODE) + cmdreg |= MVSD_CMD_INDX_CHECK; + + if (cmd->flags & MMC_RSP_PRESENT) { + cmdreg |= MVSD_UNEXPECTED_RESP; + intr |= MVSD_NOR_UNEXP_RSP; + } + + if (mrq->data) { + struct mmc_data *data = mrq->data; + int pio; + + cmdreg |= MVSD_CMD_DATA_PRESENT | MVSD_CMD_CHECK_DATACRC16; + xfer |= MVSD_XFER_MODE_HW_WR_DATA_EN; + if (data->flags & MMC_DATA_READ) + xfer |= MVSD_XFER_MODE_TO_HOST; + + pio = mvsd_setup_data(host, data); + if (pio) { + xfer |= MVSD_XFER_MODE_PIO; + /* PIO section of mvsd_irq has comments on those bits */ + if (data->flags & MMC_DATA_WRITE) + intr |= MVSD_NOR_TX_AVAIL; + else if (host->pio_size > 32) + intr |= MVSD_NOR_RX_FIFO_8W; + else + intr |= MVSD_NOR_RX_READY; + } + + if (data->stop) { + struct mmc_command *stop = data->stop; + u32 cmd12reg = 0; + + mvsd_write(MVSD_AUTOCMD12_ARG_LOW, stop->arg & 0xffff); + mvsd_write(MVSD_AUTOCMD12_ARG_HI, stop->arg >> 16); + + if (stop->flags & MMC_RSP_BUSY) + cmd12reg |= MVSD_AUTOCMD12_BUSY; + if (stop->flags & MMC_RSP_OPCODE) + cmd12reg |= MVSD_AUTOCMD12_INDX_CHECK; + cmd12reg |= MVSD_AUTOCMD12_INDEX(stop->opcode); + mvsd_write(MVSD_AUTOCMD12_CMD, cmd12reg); + + xfer |= MVSD_XFER_MODE_AUTO_CMD12; + intr |= MVSD_NOR_AUTOCMD12_DONE; + } else { + intr |= MVSD_NOR_XFER_DONE; + } + } else { + intr |= MVSD_NOR_CMD_DONE; + } + + mvsd_write(MVSD_ARG_LOW, cmd->arg & 0xffff); + mvsd_write(MVSD_ARG_HI, cmd->arg >> 16); + + spin_lock_irqsave(&host->lock, flags); + + host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN; + host->xfer_mode |= xfer; + mvsd_write(MVSD_XFER_MODE, host->xfer_mode); + + mvsd_write(MVSD_NOR_INTR_STATUS, ~MVSD_NOR_CARD_INT); + mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff); + mvsd_write(MVSD_CMD, cmdreg); + + host->intr_en &= MVSD_NOR_CARD_INT; + host->intr_en |= intr | MVSD_NOR_ERROR; + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + mvsd_write(MVSD_ERR_INTR_EN, 0xffff); + + mod_timer(&host->timer, jiffies + 5 * HZ); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static u32 mvsd_finish_cmd(struct mvsd_host *host, struct mmc_command *cmd, + u32 err_status) +{ + void __iomem *iobase = host->base; + + if (cmd->flags & MMC_RSP_136) { + unsigned int response[8], i; + for (i = 0; i < 8; i++) + response[i] = mvsd_read(MVSD_RSP(i)); + cmd->resp[0] = ((response[0] & 0x03ff) << 22) | + ((response[1] & 0xffff) << 6) | + ((response[2] & 0xfc00) >> 10); + cmd->resp[1] = ((response[2] & 0x03ff) << 22) | + ((response[3] & 0xffff) << 6) | + ((response[4] & 0xfc00) >> 10); + cmd->resp[2] = ((response[4] & 0x03ff) << 22) | + ((response[5] & 0xffff) << 6) | + ((response[6] & 0xfc00) >> 10); + cmd->resp[3] = ((response[6] & 0x03ff) << 22) | + ((response[7] & 0x3fff) << 8); + } else if (cmd->flags & MMC_RSP_PRESENT) { + unsigned int response[3], i; + for (i = 0; i < 3; i++) + response[i] = mvsd_read(MVSD_RSP(i)); + cmd->resp[0] = ((response[2] & 0x003f) << (8 - 8)) | + ((response[1] & 0xffff) << (14 - 8)) | + ((response[0] & 0x03ff) << (30 - 8)); + cmd->resp[1] = ((response[0] & 0xfc00) >> 10); + cmd->resp[2] = 0; + cmd->resp[3] = 0; + } + + if (err_status & MVSD_ERR_CMD_TIMEOUT) { + cmd->error = -ETIMEDOUT; + } else if (err_status & (MVSD_ERR_CMD_CRC | MVSD_ERR_CMD_ENDBIT | + MVSD_ERR_CMD_INDEX | MVSD_ERR_CMD_STARTBIT)) { + cmd->error = -EILSEQ; + } + err_status &= ~(MVSD_ERR_CMD_TIMEOUT | MVSD_ERR_CMD_CRC | + MVSD_ERR_CMD_ENDBIT | MVSD_ERR_CMD_INDEX | + MVSD_ERR_CMD_STARTBIT); + + return err_status; +} + +static u32 mvsd_finish_data(struct mvsd_host *host, struct mmc_data *data, + u32 err_status) +{ + void __iomem *iobase = host->base; + + if (host->pio_ptr) { + host->pio_ptr = NULL; + host->pio_size = 0; + } else { + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_frags, + (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + } + + if (err_status & MVSD_ERR_DATA_TIMEOUT) + data->error = -ETIMEDOUT; + else if (err_status & (MVSD_ERR_DATA_CRC | MVSD_ERR_DATA_ENDBIT)) + data->error = -EILSEQ; + else if (err_status & MVSD_ERR_XFER_SIZE) + data->error = -EBADE; + err_status &= ~(MVSD_ERR_DATA_TIMEOUT | MVSD_ERR_DATA_CRC | + MVSD_ERR_DATA_ENDBIT | MVSD_ERR_XFER_SIZE); + + dev_dbg(host->dev, "data done: blocks_left=%d, bytes_left=%d\n", + mvsd_read(MVSD_CURR_BLK_LEFT), mvsd_read(MVSD_CURR_BYTE_LEFT)); + data->bytes_xfered = + (data->blocks - mvsd_read(MVSD_CURR_BLK_LEFT)) * data->blksz; + /* We can't be sure about the last block when errors are detected */ + if (data->bytes_xfered && data->error) + data->bytes_xfered -= data->blksz; + + /* Handle Auto cmd 12 response */ + if (data->stop) { + unsigned int response[3], i; + for (i = 0; i < 3; i++) + response[i] = mvsd_read(MVSD_AUTO_RSP(i)); + data->stop->resp[0] = ((response[2] & 0x003f) << (8 - 8)) | + ((response[1] & 0xffff) << (14 - 8)) | + ((response[0] & 0x03ff) << (30 - 8)); + data->stop->resp[1] = ((response[0] & 0xfc00) >> 10); + data->stop->resp[2] = 0; + data->stop->resp[3] = 0; + + if (err_status & MVSD_ERR_AUTOCMD12) { + u32 err_cmd12 = mvsd_read(MVSD_AUTOCMD12_ERR_STATUS); + dev_dbg(host->dev, "c12err 0x%04x\n", err_cmd12); + if (err_cmd12 & MVSD_AUTOCMD12_ERR_NOTEXE) + data->stop->error = -ENOEXEC; + else if (err_cmd12 & MVSD_AUTOCMD12_ERR_TIMEOUT) + data->stop->error = -ETIMEDOUT; + else if (err_cmd12) + data->stop->error = -EILSEQ; + err_status &= ~MVSD_ERR_AUTOCMD12; + } + } + + return err_status; +} + +static irqreturn_t mvsd_irq(int irq, void *dev) +{ + struct mvsd_host *host = dev; + void __iomem *iobase = host->base; + u32 intr_status, intr_done_mask; + int irq_handled = 0; + + intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); + dev_dbg(host->dev, "intr 0x%04x intr_en 0x%04x hw_state 0x%04x\n", + intr_status, mvsd_read(MVSD_NOR_INTR_EN), + mvsd_read(MVSD_HW_STATE)); + + spin_lock(&host->lock); + + /* PIO handling, if needed. Messy business... */ + if (host->pio_size && + (intr_status & host->intr_en & + (MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W))) { + u16 *p = host->pio_ptr; + int s = host->pio_size; + while (s >= 32 && (intr_status & MVSD_NOR_RX_FIFO_8W)) { + readsw(iobase + MVSD_FIFO, p, 16); + p += 16; + s -= 32; + intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); + } + /* + * Normally we'd use < 32 here, but the RX_FIFO_8W bit + * doesn't appear to assert when there is exactly 32 bytes + * (8 words) left to fetch in a transfer. + */ + if (s <= 32) { + while (s >= 4 && (intr_status & MVSD_NOR_RX_READY)) { + put_unaligned(mvsd_read(MVSD_FIFO), p++); + put_unaligned(mvsd_read(MVSD_FIFO), p++); + s -= 4; + intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); + } + if (s && s < 4 && (intr_status & MVSD_NOR_RX_READY)) { + u16 val[2] = {0, 0}; + val[0] = mvsd_read(MVSD_FIFO); + val[1] = mvsd_read(MVSD_FIFO); + memcpy(p, &val, s); + s = 0; + intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); + } + if (s == 0) { + host->intr_en &= + ~(MVSD_NOR_RX_READY | MVSD_NOR_RX_FIFO_8W); + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + } else if (host->intr_en & MVSD_NOR_RX_FIFO_8W) { + host->intr_en &= ~MVSD_NOR_RX_FIFO_8W; + host->intr_en |= MVSD_NOR_RX_READY; + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + } + } + dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n", + s, intr_status, mvsd_read(MVSD_HW_STATE)); + host->pio_ptr = p; + host->pio_size = s; + irq_handled = 1; + } else if (host->pio_size && + (intr_status & host->intr_en & + (MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W))) { + u16 *p = host->pio_ptr; + int s = host->pio_size; + /* + * The TX_FIFO_8W bit is unreliable. When set, bursting + * 16 halfwords all at once in the FIFO drops data. Actually + * TX_AVAIL does go off after only one word is pushed even if + * TX_FIFO_8W remains set. + */ + while (s >= 4 && (intr_status & MVSD_NOR_TX_AVAIL)) { + mvsd_write(MVSD_FIFO, get_unaligned(p++)); + mvsd_write(MVSD_FIFO, get_unaligned(p++)); + s -= 4; + intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); + } + if (s < 4) { + if (s && (intr_status & MVSD_NOR_TX_AVAIL)) { + u16 val[2] = {0, 0}; + memcpy(&val, p, s); + mvsd_write(MVSD_FIFO, val[0]); + mvsd_write(MVSD_FIFO, val[1]); + s = 0; + intr_status = mvsd_read(MVSD_NOR_INTR_STATUS); + } + if (s == 0) { + host->intr_en &= + ~(MVSD_NOR_TX_AVAIL | MVSD_NOR_TX_FIFO_8W); + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + } + } + dev_dbg(host->dev, "pio %d intr 0x%04x hw_state 0x%04x\n", + s, intr_status, mvsd_read(MVSD_HW_STATE)); + host->pio_ptr = p; + host->pio_size = s; + irq_handled = 1; + } + + mvsd_write(MVSD_NOR_INTR_STATUS, intr_status); + + intr_done_mask = MVSD_NOR_CARD_INT | MVSD_NOR_RX_READY | + MVSD_NOR_RX_FIFO_8W | MVSD_NOR_TX_FIFO_8W; + if (intr_status & host->intr_en & ~intr_done_mask) { + struct mmc_request *mrq = host->mrq; + struct mmc_command *cmd = mrq->cmd; + u32 err_status = 0; + + del_timer(&host->timer); + host->mrq = NULL; + + host->intr_en &= MVSD_NOR_CARD_INT; + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + mvsd_write(MVSD_ERR_INTR_EN, 0); + + spin_unlock(&host->lock); + + if (intr_status & MVSD_NOR_UNEXP_RSP) { + cmd->error = -EPROTO; + } else if (intr_status & MVSD_NOR_ERROR) { + err_status = mvsd_read(MVSD_ERR_INTR_STATUS); + dev_dbg(host->dev, "err 0x%04x\n", err_status); + } + + err_status = mvsd_finish_cmd(host, cmd, err_status); + if (mrq->data) + err_status = mvsd_finish_data(host, mrq->data, err_status); + if (err_status) { + printk(KERN_ERR "%s: unhandled error status %#04x\n", + mmc_hostname(host->mmc), err_status); + cmd->error = -ENOMSG; + } + + mmc_request_done(host->mmc, mrq); + irq_handled = 1; + } else + spin_unlock(&host->lock); + + if (intr_status & MVSD_NOR_CARD_INT) { + mmc_signal_sdio_irq(host->mmc); + irq_handled = 1; + } + + if (irq_handled) + return IRQ_HANDLED; + + printk(KERN_ERR "%s: unhandled interrupt status=0x%04x en=0x%04x " + "pio=%d\n", mmc_hostname(host->mmc), intr_status, + host->intr_en, host->pio_size); + return IRQ_NONE; +} + +static void mvsd_timeout_timer(unsigned long data) +{ + struct mvsd_host *host = (struct mvsd_host *)data; + void __iomem *iobase = host->base; + struct mmc_request *mrq; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + mrq = host->mrq; + if (mrq) { + printk(KERN_ERR "%s: Timeout waiting for hardware interrupt.\n", + mmc_hostname(host->mmc)); + printk(KERN_ERR "%s: hw_state=0x%04x, intr_status=0x%04x " + "intr_en=0x%04x\n", mmc_hostname(host->mmc), + mvsd_read(MVSD_HW_STATE), + mvsd_read(MVSD_NOR_INTR_STATUS), + mvsd_read(MVSD_NOR_INTR_EN)); + + host->mrq = NULL; + + mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW); + + host->xfer_mode &= MVSD_XFER_MODE_INT_CHK_EN; + mvsd_write(MVSD_XFER_MODE, host->xfer_mode); + + host->intr_en &= MVSD_NOR_CARD_INT; + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + mvsd_write(MVSD_ERR_INTR_EN, 0); + mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff); + + mrq->cmd->error = -ETIMEDOUT; + mvsd_finish_cmd(host, mrq->cmd, 0); + if (mrq->data) { + mrq->data->error = -ETIMEDOUT; + mvsd_finish_data(host, mrq->data, 0); + } + } + spin_unlock_irqrestore(&host->lock, flags); + + if (mrq) + mmc_request_done(host->mmc, mrq); +} + +static irqreturn_t mvsd_card_detect_irq(int irq, void *dev) +{ + struct mvsd_host *host = dev; + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + return IRQ_HANDLED; +} + +static void mvsd_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct mvsd_host *host = mmc_priv(mmc); + void __iomem *iobase = host->base; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (enable) { + host->xfer_mode |= MVSD_XFER_MODE_INT_CHK_EN; + host->intr_en |= MVSD_NOR_CARD_INT; + } else { + host->xfer_mode &= ~MVSD_XFER_MODE_INT_CHK_EN; + host->intr_en &= ~MVSD_NOR_CARD_INT; + } + mvsd_write(MVSD_XFER_MODE, host->xfer_mode); + mvsd_write(MVSD_NOR_INTR_EN, host->intr_en); + spin_unlock_irqrestore(&host->lock, flags); +} + +static int mvsd_get_ro(struct mmc_host *mmc) +{ + struct mvsd_host *host = mmc_priv(mmc); + + if (host->gpio_write_protect) + return gpio_get_value(host->gpio_write_protect); + + /* + * Board doesn't support read only detection; let the mmc core + * decide what to do. + */ + return -ENOSYS; +} + +static void mvsd_power_up(struct mvsd_host *host) +{ + void __iomem *iobase = host->base; + dev_dbg(host->dev, "power up\n"); + mvsd_write(MVSD_NOR_INTR_EN, 0); + mvsd_write(MVSD_ERR_INTR_EN, 0); + mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW); + mvsd_write(MVSD_XFER_MODE, 0); + mvsd_write(MVSD_NOR_STATUS_EN, 0xffff); + mvsd_write(MVSD_ERR_STATUS_EN, 0xffff); + mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff); + mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff); +} + +static void mvsd_power_down(struct mvsd_host *host) +{ + void __iomem *iobase = host->base; + dev_dbg(host->dev, "power down\n"); + mvsd_write(MVSD_NOR_INTR_EN, 0); + mvsd_write(MVSD_ERR_INTR_EN, 0); + mvsd_write(MVSD_SW_RESET, MVSD_SW_RESET_NOW); + mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK); + mvsd_write(MVSD_NOR_STATUS_EN, 0); + mvsd_write(MVSD_ERR_STATUS_EN, 0); + mvsd_write(MVSD_NOR_INTR_STATUS, 0xffff); + mvsd_write(MVSD_ERR_INTR_STATUS, 0xffff); +} + +static void mvsd_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mvsd_host *host = mmc_priv(mmc); + void __iomem *iobase = host->base; + u32 ctrl_reg = 0; + + if (ios->power_mode == MMC_POWER_UP) + mvsd_power_up(host); + + if (ios->clock == 0) { + mvsd_write(MVSD_XFER_MODE, MVSD_XFER_MODE_STOP_CLK); + mvsd_write(MVSD_CLK_DIV, MVSD_BASE_DIV_MAX); + host->clock = 0; + dev_dbg(host->dev, "clock off\n"); + } else if (ios->clock != host->clock) { + u32 m = DIV_ROUND_UP(host->base_clock, ios->clock) - 1; + if (m > MVSD_BASE_DIV_MAX) + m = MVSD_BASE_DIV_MAX; + mvsd_write(MVSD_CLK_DIV, m); + host->clock = ios->clock; + host->ns_per_clk = 1000000000 / (host->base_clock / (m+1)); + dev_dbg(host->dev, "clock=%d (%d), div=0x%04x\n", + ios->clock, host->base_clock / (m+1), m); + } + + /* default transfer mode */ + ctrl_reg |= MVSD_HOST_CTRL_BIG_ENDIAN; + ctrl_reg &= ~MVSD_HOST_CTRL_LSB_FIRST; + + /* default to maximum timeout */ + ctrl_reg |= MVSD_HOST_CTRL_TMOUT_MASK; + ctrl_reg |= MVSD_HOST_CTRL_TMOUT_EN; + + if (ios->bus_mode == MMC_BUSMODE_PUSHPULL) + ctrl_reg |= MVSD_HOST_CTRL_PUSH_PULL_EN; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + ctrl_reg |= MVSD_HOST_CTRL_DATA_WIDTH_4_BITS; + + if (ios->timing == MMC_TIMING_MMC_HS || + ios->timing == MMC_TIMING_SD_HS) + ctrl_reg |= MVSD_HOST_CTRL_HI_SPEED_EN; + + host->ctrl = ctrl_reg; + mvsd_write(MVSD_HOST_CTRL, ctrl_reg); + dev_dbg(host->dev, "ctrl 0x%04x: %s %s %s\n", ctrl_reg, + (ctrl_reg & MVSD_HOST_CTRL_PUSH_PULL_EN) ? + "push-pull" : "open-drain", + (ctrl_reg & MVSD_HOST_CTRL_DATA_WIDTH_4_BITS) ? + "4bit-width" : "1bit-width", + (ctrl_reg & MVSD_HOST_CTRL_HI_SPEED_EN) ? + "high-speed" : ""); + + if (ios->power_mode == MMC_POWER_OFF) + mvsd_power_down(host); +} + +static const struct mmc_host_ops mvsd_ops = { + .request = mvsd_request, + .get_ro = mvsd_get_ro, + .set_ios = mvsd_set_ios, + .enable_sdio_irq = mvsd_enable_sdio_irq, +}; + +static void __init mv_conf_mbus_windows(struct mvsd_host *host, + struct mbus_dram_target_info *dram) +{ + void __iomem *iobase = host->base; + int i; + + for (i = 0; i < 4; i++) { + writel(0, iobase + MVSD_WINDOW_CTRL(i)); + writel(0, iobase + MVSD_WINDOW_BASE(i)); + } + + for (i = 0; i < dram->num_cs; i++) { + struct mbus_dram_window *cs = dram->cs + i; + writel(((cs->size - 1) & 0xffff0000) | + (cs->mbus_attr << 8) | + (dram->mbus_dram_target_id << 4) | 1, + iobase + MVSD_WINDOW_CTRL(i)); + writel(cs->base, iobase + MVSD_WINDOW_BASE(i)); + } +} + +static int __init mvsd_probe(struct platform_device *pdev) +{ + struct mmc_host *mmc = NULL; + struct mvsd_host *host = NULL; + const struct mvsdio_platform_data *mvsd_data; + struct resource *r; + int ret, irq; + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + irq = platform_get_irq(pdev, 0); + mvsd_data = pdev->dev.platform_data; + if (!r || irq < 0 || !mvsd_data) + return -ENXIO; + + r = request_mem_region(r->start, SZ_1K, DRIVER_NAME); + if (!r) + return -EBUSY; + + mmc = mmc_alloc_host(sizeof(struct mvsd_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + host->dev = &pdev->dev; + host->res = r; + host->base_clock = mvsd_data->clock / 2; + + mmc->ops = &mvsd_ops; + + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ | + MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + + mmc->f_min = DIV_ROUND_UP(host->base_clock, MVSD_BASE_DIV_MAX); + mmc->f_max = maxfreq; + + mmc->max_blk_size = 2048; + mmc->max_blk_count = 65535; + + mmc->max_hw_segs = 1; + mmc->max_phys_segs = 1; + mmc->max_seg_size = mmc->max_blk_size * mmc->max_blk_count; + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + + spin_lock_init(&host->lock); + + host->base = ioremap(r->start, SZ_4K); + if (!host->base) { + ret = -ENOMEM; + goto out; + } + + /* (Re-)program MBUS remapping windows if we are asked to. */ + if (mvsd_data->dram != NULL) + mv_conf_mbus_windows(host, mvsd_data->dram); + + mvsd_power_down(host); + + ret = request_irq(irq, mvsd_irq, 0, DRIVER_NAME, host); + if (ret) { + printk(KERN_ERR "%s: cannot assign irq %d\n", DRIVER_NAME, irq); + goto out; + } else + host->irq = irq; + + if (mvsd_data->gpio_card_detect) { + ret = gpio_request(mvsd_data->gpio_card_detect, + DRIVER_NAME " cd"); + if (ret == 0) { + gpio_direction_input(mvsd_data->gpio_card_detect); + irq = gpio_to_irq(mvsd_data->gpio_card_detect); + ret = request_irq(irq, mvsd_card_detect_irq, + IRQ_TYPE_EDGE_RISING | IRQ_TYPE_EDGE_FALLING, + DRIVER_NAME " cd", host); + if (ret == 0) + host->gpio_card_detect = + mvsd_data->gpio_card_detect; + else + gpio_free(mvsd_data->gpio_card_detect); + } + } + if (!host->gpio_card_detect) + mmc->caps |= MMC_CAP_NEEDS_POLL; + + if (mvsd_data->gpio_write_protect) { + ret = gpio_request(mvsd_data->gpio_write_protect, + DRIVER_NAME " wp"); + if (ret == 0) { + gpio_direction_input(mvsd_data->gpio_write_protect); + host->gpio_write_protect = + mvsd_data->gpio_write_protect; + } + } + + setup_timer(&host->timer, mvsd_timeout_timer, (unsigned long)host); + platform_set_drvdata(pdev, mmc); + ret = mmc_add_host(mmc); + if (ret) + goto out; + + printk(KERN_NOTICE "%s: %s driver initialized, ", + mmc_hostname(mmc), DRIVER_NAME); + if (host->gpio_card_detect) + printk("using GPIO %d for card detection\n", + host->gpio_card_detect); + else + printk("lacking card detect (fall back to polling)\n"); + return 0; + +out: + if (host) { + if (host->irq) + free_irq(host->irq, host); + if (host->gpio_card_detect) { + free_irq(gpio_to_irq(host->gpio_card_detect), host); + gpio_free(host->gpio_card_detect); + } + if (host->gpio_write_protect) + gpio_free(host->gpio_write_protect); + if (host->base) + iounmap(host->base); + } + if (r) + release_resource(r); + if (mmc) + mmc_free_host(mmc); + + return ret; +} + +static int __exit mvsd_remove(struct platform_device *pdev) +{ + struct mmc_host *mmc = platform_get_drvdata(pdev); + + if (mmc) { + struct mvsd_host *host = mmc_priv(mmc); + + if (host->gpio_card_detect) { + free_irq(gpio_to_irq(host->gpio_card_detect), host); + gpio_free(host->gpio_card_detect); + } + mmc_remove_host(mmc); + free_irq(host->irq, host); + if (host->gpio_write_protect) + gpio_free(host->gpio_write_protect); + del_timer_sync(&host->timer); + mvsd_power_down(host); + iounmap(host->base); + release_resource(host->res); + mmc_free_host(mmc); + } + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int mvsd_suspend(struct platform_device *dev, pm_message_t state, + u32 level) +{ + struct mmc_host *mmc = platform_get_drvdata(dev); + int ret = 0; + + if (mmc && level == SUSPEND_DISABLE) + ret = mmc_suspend_host(mmc, state); + + return ret; +} + +static int mvsd_resume(struct platform_device *dev, u32 level) +{ + struct mmc_host *mmc = platform_dev_get_drvdata(dev); + int ret = 0; + + if (mmc && level == RESUME_ENABLE) + ret = mmc_resume_host(mmc); + + return ret; +} +#else +#define mvsd_suspend NULL +#define mvsd_resume NULL +#endif + +static struct platform_driver mvsd_driver = { + .remove = __exit_p(mvsd_remove), + .suspend = mvsd_suspend, + .resume = mvsd_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init mvsd_init(void) +{ + return platform_driver_probe(&mvsd_driver, mvsd_probe); +} + +static void __exit mvsd_exit(void) +{ + platform_driver_unregister(&mvsd_driver); +} + +module_init(mvsd_init); +module_exit(mvsd_exit); + +/* maximum card clock frequency (default 50MHz) */ +module_param(maxfreq, int, 0); + +/* force PIO transfers all the time */ +module_param(nodma, int, 0); + +MODULE_AUTHOR("Maen Suleiman, Nicolas Pitre"); +MODULE_DESCRIPTION("Marvell MMC,SD,SDIO Host Controller driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/mvsdio.h b/drivers/mmc/host/mvsdio.h new file mode 100644 index 00000000000..7d9727b9f5a --- /dev/null +++ b/drivers/mmc/host/mvsdio.h @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2008 Marvell Semiconductors, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __MVSDIO_H +#define __MVSDIO_H + +/* + * Clock rates + */ + +#define MVSD_CLOCKRATE_MAX 50000000 +#define MVSD_BASE_DIV_MAX 0x7ff + + +/* + * Register offsets + */ + +#define MVSD_SYS_ADDR_LOW 0x000 +#define MVSD_SYS_ADDR_HI 0x004 +#define MVSD_BLK_SIZE 0x008 +#define MVSD_BLK_COUNT 0x00c +#define MVSD_ARG_LOW 0x010 +#define MVSD_ARG_HI 0x014 +#define MVSD_XFER_MODE 0x018 +#define MVSD_CMD 0x01c +#define MVSD_RSP(i) (0x020 + ((i)<<2)) +#define MVSD_RSP0 0x020 +#define MVSD_RSP1 0x024 +#define MVSD_RSP2 0x028 +#define MVSD_RSP3 0x02c +#define MVSD_RSP4 0x030 +#define MVSD_RSP5 0x034 +#define MVSD_RSP6 0x038 +#define MVSD_RSP7 0x03c +#define MVSD_FIFO 0x040 +#define MVSD_RSP_CRC7 0x044 +#define MVSD_HW_STATE 0x048 +#define MVSD_HOST_CTRL 0x050 +#define MVSD_BLK_GAP_CTRL 0x054 +#define MVSD_CLK_CTRL 0x058 +#define MVSD_SW_RESET 0x05c +#define MVSD_NOR_INTR_STATUS 0x060 +#define MVSD_ERR_INTR_STATUS 0x064 +#define MVSD_NOR_STATUS_EN 0x068 +#define MVSD_ERR_STATUS_EN 0x06c +#define MVSD_NOR_INTR_EN 0x070 +#define MVSD_ERR_INTR_EN 0x074 +#define MVSD_AUTOCMD12_ERR_STATUS 0x078 +#define MVSD_CURR_BYTE_LEFT 0x07c +#define MVSD_CURR_BLK_LEFT 0x080 +#define MVSD_AUTOCMD12_ARG_LOW 0x084 +#define MVSD_AUTOCMD12_ARG_HI 0x088 +#define MVSD_AUTOCMD12_CMD 0x08c +#define MVSD_AUTO_RSP(i) (0x090 + ((i)<<2)) +#define MVSD_AUTO_RSP0 0x090 +#define MVSD_AUTO_RSP1 0x094 +#define MVSD_AUTO_RSP2 0x098 +#define MVSD_CLK_DIV 0x128 + +#define MVSD_WINDOW_CTRL(i) (0x108 + ((i) << 3)) +#define MVSD_WINDOW_BASE(i) (0x10c + ((i) << 3)) + + +/* + * MVSD_CMD + */ + +#define MVSD_CMD_RSP_NONE (0 << 0) +#define MVSD_CMD_RSP_136 (1 << 0) +#define MVSD_CMD_RSP_48 (2 << 0) +#define MVSD_CMD_RSP_48BUSY (3 << 0) + +#define MVSD_CMD_CHECK_DATACRC16 (1 << 2) +#define MVSD_CMD_CHECK_CMDCRC (1 << 3) +#define MVSD_CMD_INDX_CHECK (1 << 4) +#define MVSD_CMD_DATA_PRESENT (1 << 5) +#define MVSD_UNEXPECTED_RESP (1 << 7) +#define MVSD_CMD_INDEX(x) ((x) << 8) + + +/* + * MVSD_AUTOCMD12_CMD + */ + +#define MVSD_AUTOCMD12_BUSY (1 << 0) +#define MVSD_AUTOCMD12_INDX_CHECK (1 << 1) +#define MVSD_AUTOCMD12_INDEX(x) ((x) << 8) + +/* + * MVSD_XFER_MODE + */ + +#define MVSD_XFER_MODE_WR_DATA_START (1 << 0) +#define MVSD_XFER_MODE_HW_WR_DATA_EN (1 << 1) +#define MVSD_XFER_MODE_AUTO_CMD12 (1 << 2) +#define MVSD_XFER_MODE_INT_CHK_EN (1 << 3) +#define MVSD_XFER_MODE_TO_HOST (1 << 4) +#define MVSD_XFER_MODE_STOP_CLK (1 << 5) +#define MVSD_XFER_MODE_PIO (1 << 6) + + +/* + * MVSD_HOST_CTRL + */ + +#define MVSD_HOST_CTRL_PUSH_PULL_EN (1 << 0) + +#define MVSD_HOST_CTRL_CARD_TYPE_MEM_ONLY (0 << 1) +#define MVSD_HOST_CTRL_CARD_TYPE_IO_ONLY (1 << 1) +#define MVSD_HOST_CTRL_CARD_TYPE_IO_MEM_COMBO (2 << 1) +#define MVSD_HOST_CTRL_CARD_TYPE_IO_MMC (3 << 1) +#define MVSD_HOST_CTRL_CARD_TYPE_MASK (3 << 1) + +#define MVSD_HOST_CTRL_BIG_ENDIAN (1 << 3) +#define MVSD_HOST_CTRL_LSB_FIRST (1 << 4) +#define MVSD_HOST_CTRL_DATA_WIDTH_4_BITS (1 << 9) +#define MVSD_HOST_CTRL_HI_SPEED_EN (1 << 10) + +#define MVSD_HOST_CTRL_TMOUT_MAX 0xf +#define MVSD_HOST_CTRL_TMOUT_MASK (0xf << 11) +#define MVSD_HOST_CTRL_TMOUT(x) ((x) << 11) +#define MVSD_HOST_CTRL_TMOUT_EN (1 << 15) + + +/* + * MVSD_SW_RESET + */ + +#define MVSD_SW_RESET_NOW (1 << 8) + + +/* + * Normal interrupt status bits + */ + +#define MVSD_NOR_CMD_DONE (1 << 0) +#define MVSD_NOR_XFER_DONE (1 << 1) +#define MVSD_NOR_BLK_GAP_EVT (1 << 2) +#define MVSD_NOR_DMA_DONE (1 << 3) +#define MVSD_NOR_TX_AVAIL (1 << 4) +#define MVSD_NOR_RX_READY (1 << 5) +#define MVSD_NOR_CARD_INT (1 << 8) +#define MVSD_NOR_READ_WAIT_ON (1 << 9) +#define MVSD_NOR_RX_FIFO_8W (1 << 10) +#define MVSD_NOR_TX_FIFO_8W (1 << 11) +#define MVSD_NOR_SUSPEND_ON (1 << 12) +#define MVSD_NOR_AUTOCMD12_DONE (1 << 13) +#define MVSD_NOR_UNEXP_RSP (1 << 14) +#define MVSD_NOR_ERROR (1 << 15) + + +/* + * Error status bits + */ + +#define MVSD_ERR_CMD_TIMEOUT (1 << 0) +#define MVSD_ERR_CMD_CRC (1 << 1) +#define MVSD_ERR_CMD_ENDBIT (1 << 2) +#define MVSD_ERR_CMD_INDEX (1 << 3) +#define MVSD_ERR_DATA_TIMEOUT (1 << 4) +#define MVSD_ERR_DATA_CRC (1 << 5) +#define MVSD_ERR_DATA_ENDBIT (1 << 6) +#define MVSD_ERR_AUTOCMD12 (1 << 8) +#define MVSD_ERR_CMD_STARTBIT (1 << 9) +#define MVSD_ERR_XFER_SIZE (1 << 10) +#define MVSD_ERR_RESP_T_BIT (1 << 11) +#define MVSD_ERR_CRC_ENDBIT (1 << 12) +#define MVSD_ERR_CRC_STARTBIT (1 << 13) +#define MVSD_ERR_CRC_STATUS (1 << 14) + + +/* + * CMD12 error status bits + */ + +#define MVSD_AUTOCMD12_ERR_NOTEXE (1 << 0) +#define MVSD_AUTOCMD12_ERR_TIMEOUT (1 << 1) +#define MVSD_AUTOCMD12_ERR_CRC (1 << 2) +#define MVSD_AUTOCMD12_ERR_ENDBIT (1 << 3) +#define MVSD_AUTOCMD12_ERR_INDEX (1 << 4) +#define MVSD_AUTOCMD12_ERR_RESP_T_BIT (1 << 5) +#define MVSD_AUTOCMD12_ERR_RESP_STARTBIT (1 << 6) + +#endif diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c index 3916a5618e2..d183be6f2a5 100644 --- a/drivers/mmc/host/omap_hsmmc.c +++ b/drivers/mmc/host/omap_hsmmc.c @@ -56,6 +56,7 @@ #define SDVS18 (0x5 << 9) #define SDVS30 (0x6 << 9) #define SDVS33 (0x7 << 9) +#define SDVS_MASK 0x00000E00 #define SDVSCLR 0xFFFFF1FF #define SDVSDET 0x00000400 #define AUTOIDLE 0x1 @@ -76,6 +77,7 @@ #define MSBS (1 << 5) #define BCE (1 << 1) #define FOUR_BIT (1 << 1) +#define DW8 (1 << 5) #define CC 0x1 #define TC 0x02 #define OD 0x1 @@ -98,10 +100,8 @@ */ #define OMAP_MMC1_DEVID 0 #define OMAP_MMC2_DEVID 1 +#define OMAP_MMC3_DEVID 2 -#define OMAP_MMC_DATADIR_NONE 0 -#define OMAP_MMC_DATADIR_READ 1 -#define OMAP_MMC_DATADIR_WRITE 2 #define MMC_TIMEOUT_MS 20 #define OMAP_MMC_MASTER_CLOCK 96000000 #define DRIVER_NAME "mmci-omap-hs" @@ -137,18 +137,18 @@ struct mmc_omap_host { resource_size_t mapbase; unsigned int id; unsigned int dma_len; - unsigned int dma_dir; + unsigned int dma_sg_idx; unsigned char bus_mode; - unsigned char datadir; u32 *buffer; u32 bytesleft; int suspended; int irq; int carddetect; int use_dma, dma_ch; - int initstr; + int dma_line_tx, dma_line_rx; int slot_id; int dbclk_enabled; + int response_busy; struct omap_mmc_platform_data *pdata; }; @@ -218,7 +218,7 @@ mmc_omap_show_slot_name(struct device *dev, struct device_attribute *attr, struct mmc_omap_host *host = mmc_priv(mmc); struct omap_mmc_slot_data slot = host->pdata->slots[host->slot_id]; - return sprintf(buf, "slot:%s\n", slot.name); + return sprintf(buf, "%s\n", slot.name); } static DEVICE_ATTR(slot_name, S_IRUGO, mmc_omap_show_slot_name, NULL); @@ -243,10 +243,14 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, OMAP_HSMMC_WRITE(host->base, ISE, INT_EN_MASK); OMAP_HSMMC_WRITE(host->base, IE, INT_EN_MASK); + host->response_busy = 0; if (cmd->flags & MMC_RSP_PRESENT) { if (cmd->flags & MMC_RSP_136) resptype = 1; - else + else if (cmd->flags & MMC_RSP_BUSY) { + resptype = 3; + host->response_busy = 1; + } else resptype = 2; } @@ -275,19 +279,35 @@ mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd, OMAP_HSMMC_WRITE(host->base, CMD, cmdreg); } +static int +mmc_omap_get_dma_dir(struct mmc_omap_host *host, struct mmc_data *data) +{ + if (data->flags & MMC_DATA_WRITE) + return DMA_TO_DEVICE; + else + return DMA_FROM_DEVICE; +} + /* * Notify the transfer complete to MMC core */ static void mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) { + if (!data) { + struct mmc_request *mrq = host->mrq; + + host->mrq = NULL; + mmc_omap_fclk_lazy_disable(host); + mmc_request_done(host->mmc, mrq); + return; + } + host->data = NULL; if (host->use_dma && host->dma_ch != -1) dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->dma_len, - host->dma_dir); - - host->datadir = OMAP_MMC_DATADIR_NONE; + mmc_omap_get_dma_dir(host, data)); if (!data->error) data->bytes_xfered += data->blocks * (data->blksz); @@ -322,7 +342,7 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) cmd->resp[0] = OMAP_HSMMC_READ(host->base, RSP10); } } - if (host->data == NULL || cmd->error) { + if ((host->data == NULL && !host->response_busy) || cmd->error) { host->mrq = NULL; mmc_request_done(host->mmc, cmd->mrq); } @@ -331,19 +351,18 @@ mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd) /* * DMA clean up for command errors */ -static void mmc_dma_cleanup(struct mmc_omap_host *host) +static void mmc_dma_cleanup(struct mmc_omap_host *host, int errno) { - host->data->error = -ETIMEDOUT; + host->data->error = errno; if (host->use_dma && host->dma_ch != -1) { dma_unmap_sg(mmc_dev(host->mmc), host->data->sg, host->dma_len, - host->dma_dir); + mmc_omap_get_dma_dir(host, host->data)); omap_free_dma(host->dma_ch); host->dma_ch = -1; up(&host->sem); } host->data = NULL; - host->datadir = OMAP_MMC_DATADIR_NONE; } /* @@ -412,7 +431,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) struct mmc_data *data; int end_cmd = 0, end_trans = 0, status; - if (host->cmd == NULL && host->data == NULL) { + if (host->mrq == NULL) { OMAP_HSMMC_WRITE(host->base, STAT, OMAP_HSMMC_READ(host->base, STAT)); return IRQ_HANDLED; @@ -437,18 +456,24 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) } end_cmd = 1; } - if (host->data) { - mmc_dma_cleanup(host); + if (host->data || host->response_busy) { + if (host->data) + mmc_dma_cleanup(host, -ETIMEDOUT); + host->response_busy = 0; mmc_omap_reset_controller_fsm(host, SRD); } } if ((status & DATA_TIMEOUT) || (status & DATA_CRC)) { - if (host->data) { - if (status & DATA_TIMEOUT) - mmc_dma_cleanup(host); + if (host->data || host->response_busy) { + int err = (status & DATA_TIMEOUT) ? + -ETIMEDOUT : -EILSEQ; + + if (host->data) + mmc_dma_cleanup(host, err); else - host->data->error = -EILSEQ; + host->mrq->cmd->error = err; + host->response_busy = 0; mmc_omap_reset_controller_fsm(host, SRD); end_trans = 1; } @@ -473,6 +498,19 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id) return IRQ_HANDLED; } +static void set_sd_bus_power(struct mmc_omap_host *host) +{ + unsigned long i; + + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) | SDBP); + for (i = 0; i < loops_per_jiffy; i++) { + if (OMAP_HSMMC_READ(host->base, HCTL) & SDBP) + break; + cpu_relax(); + } +} + /* * Switch MMC interface voltage ... only relevant for MMC1. * @@ -485,9 +523,6 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) u32 reg_val = 0; int ret; - if (host->id != OMAP_MMC1_DEVID) - return 0; - /* Disable the clocks */ clk_disable(host->fclk); clk_disable(host->iclk); @@ -532,9 +567,7 @@ static int omap_mmc_switch_opcond(struct mmc_omap_host *host, int vdd) reg_val |= SDVS30; OMAP_HSMMC_WRITE(host->base, HCTL, reg_val); - - OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) | SDBP); + set_sd_bus_power(host); return 0; err: @@ -551,7 +584,10 @@ static void mmc_omap_detect(struct work_struct *work) mmc_carddetect_work); struct omap_mmc_slot_data *slot = &mmc_slot(host); - host->carddetect = slot->card_detect(slot->card_detect_irq); + if (mmc_slot(host).card_detect) + host->carddetect = slot->card_detect(slot->card_detect_irq); + else + host->carddetect = -ENOSYS; sysfs_notify(&host->mmc->class_dev.kobj, NULL, "cover_switch"); if (host->carddetect) { @@ -574,6 +610,48 @@ static irqreturn_t omap_mmc_cd_handler(int irq, void *dev_id) return IRQ_HANDLED; } +static int mmc_omap_get_dma_sync_dev(struct mmc_omap_host *host, + struct mmc_data *data) +{ + int sync_dev; + + if (data->flags & MMC_DATA_WRITE) + sync_dev = host->dma_line_tx; + else + sync_dev = host->dma_line_rx; + return sync_dev; +} + +static void mmc_omap_config_dma_params(struct mmc_omap_host *host, + struct mmc_data *data, + struct scatterlist *sgl) +{ + int blksz, nblk, dma_ch; + + dma_ch = host->dma_ch; + if (data->flags & MMC_DATA_WRITE) { + omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sgl), 0, 0); + } else { + omap_set_dma_src_params(dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, + (host->mapbase + OMAP_HSMMC_DATA), 0, 0); + omap_set_dma_dest_params(dma_ch, 0, OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sgl), 0, 0); + } + + blksz = host->data->blksz; + nblk = sg_dma_len(sgl) / blksz; + + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, + blksz / 4, nblk, OMAP_DMA_SYNC_FRAME, + mmc_omap_get_dma_sync_dev(host, data), + !(data->flags & MMC_DATA_WRITE)); + + omap_start_dma(dma_ch); +} + /* * DMA call back function */ @@ -587,6 +665,14 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) if (host->dma_ch < 0) return; + host->dma_sg_idx++; + if (host->dma_sg_idx < host->dma_len) { + /* Fire up the next transfer. */ + mmc_omap_config_dma_params(host, host->data, + host->data->sg + host->dma_sg_idx); + return; + } + omap_free_dma(host->dma_ch); host->dma_ch = -1; /* @@ -597,38 +683,28 @@ static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) } /* - * Configure dma src and destination parameters - */ -static int mmc_omap_config_dma_param(int sync_dir, struct mmc_omap_host *host, - struct mmc_data *data) -{ - if (sync_dir == 0) { - omap_set_dma_dest_params(host->dma_ch, 0, - OMAP_DMA_AMODE_CONSTANT, - (host->mapbase + OMAP_HSMMC_DATA), 0, 0); - omap_set_dma_src_params(host->dma_ch, 0, - OMAP_DMA_AMODE_POST_INC, - sg_dma_address(&data->sg[0]), 0, 0); - } else { - omap_set_dma_src_params(host->dma_ch, 0, - OMAP_DMA_AMODE_CONSTANT, - (host->mapbase + OMAP_HSMMC_DATA), 0, 0); - omap_set_dma_dest_params(host->dma_ch, 0, - OMAP_DMA_AMODE_POST_INC, - sg_dma_address(&data->sg[0]), 0, 0); - } - return 0; -} -/* * Routine to configure and start DMA for the MMC card */ static int mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) { - int sync_dev, sync_dir = 0; - int dma_ch = 0, ret = 0, err = 1; + int dma_ch = 0, ret = 0, err = 1, i; struct mmc_data *data = req->data; + /* Sanity check: all the SG entries must be aligned by block size. */ + for (i = 0; i < host->dma_len; i++) { + struct scatterlist *sgl; + + sgl = data->sg + i; + if (sgl->length % data->blksz) + return -EINVAL; + } + if ((data->blksz % 4) != 0) + /* REVISIT: The MMC buffer increments only when MSB is written. + * Return error for blksz which is non multiple of four. + */ + return -EINVAL; + /* * If for some reason the DMA transfer is still active, * we wait for timeout period and free the dma @@ -647,49 +723,22 @@ mmc_omap_start_dma_transfer(struct mmc_omap_host *host, struct mmc_request *req) return err; } - if (!(data->flags & MMC_DATA_WRITE)) { - host->dma_dir = DMA_FROM_DEVICE; - if (host->id == OMAP_MMC1_DEVID) - sync_dev = OMAP24XX_DMA_MMC1_RX; - else - sync_dev = OMAP24XX_DMA_MMC2_RX; - } else { - host->dma_dir = DMA_TO_DEVICE; - if (host->id == OMAP_MMC1_DEVID) - sync_dev = OMAP24XX_DMA_MMC1_TX; - else - sync_dev = OMAP24XX_DMA_MMC2_TX; - } - - ret = omap_request_dma(sync_dev, "MMC/SD", mmc_omap_dma_cb, - host, &dma_ch); + ret = omap_request_dma(mmc_omap_get_dma_sync_dev(host, data), "MMC/SD", + mmc_omap_dma_cb,host, &dma_ch); if (ret != 0) { - dev_dbg(mmc_dev(host->mmc), + dev_err(mmc_dev(host->mmc), "%s: omap_request_dma() failed with %d\n", mmc_hostname(host->mmc), ret); return ret; } host->dma_len = dma_map_sg(mmc_dev(host->mmc), data->sg, - data->sg_len, host->dma_dir); + data->sg_len, mmc_omap_get_dma_dir(host, data)); host->dma_ch = dma_ch; + host->dma_sg_idx = 0; - if (!(data->flags & MMC_DATA_WRITE)) - mmc_omap_config_dma_param(1, host, data); - else - mmc_omap_config_dma_param(0, host, data); - - if ((data->blksz % 4) == 0) - omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, - (data->blksz / 4), data->blocks, OMAP_DMA_SYNC_FRAME, - sync_dev, sync_dir); - else - /* REVISIT: The MMC buffer increments only when MSB is written. - * Return error for blksz which is non multiple of four. - */ - return -EINVAL; + mmc_omap_config_dma_params(host, data, data->sg); - omap_start_dma(dma_ch); return 0; } @@ -739,7 +788,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) host->data = req->data; if (req->data == NULL) { - host->datadir = OMAP_MMC_DATADIR_NONE; OMAP_HSMMC_WRITE(host->base, BLK, 0); return 0; } @@ -748,9 +796,6 @@ mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) | (req->data->blocks << 16)); set_data_timeout(host, req); - host->datadir = (req->data->flags & MMC_DATA_WRITE) ? - OMAP_MMC_DATADIR_WRITE : OMAP_MMC_DATADIR_READ; - if (host->use_dma) { ret = mmc_omap_start_dma_transfer(host, req); if (ret != 0) { @@ -782,36 +827,29 @@ static void omap_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) u16 dsor = 0; unsigned long regval; unsigned long timeout; + u32 con; switch (ios->power_mode) { case MMC_POWER_OFF: mmc_slot(host).set_power(host->dev, host->slot_id, 0, 0); - /* - * Reset interface voltage to 3V if it's 1.8V now; - * only relevant on MMC-1, the others always use 1.8V. - * - * REVISIT: If we are able to detect cards after unplugging - * a 1.8V card, this code should not be needed. - */ - if (host->id != OMAP_MMC1_DEVID) - break; - if (!(OMAP_HSMMC_READ(host->base, HCTL) & SDVSDET)) { - int vdd = fls(host->mmc->ocr_avail) - 1; - if (omap_mmc_switch_opcond(host, vdd) != 0) - host->mmc->ios.vdd = vdd; - } break; case MMC_POWER_UP: mmc_slot(host).set_power(host->dev, host->slot_id, 1, ios->vdd); break; } + con = OMAP_HSMMC_READ(host->base, CON); switch (mmc->ios.bus_width) { + case MMC_BUS_WIDTH_8: + OMAP_HSMMC_WRITE(host->base, CON, con | DW8); + break; case MMC_BUS_WIDTH_4: + OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) | FOUR_BIT); break; case MMC_BUS_WIDTH_1: + OMAP_HSMMC_WRITE(host->base, CON, con & ~DW8); OMAP_HSMMC_WRITE(host->base, HCTL, OMAP_HSMMC_READ(host->base, HCTL) & ~FOUR_BIT); break; @@ -891,6 +929,33 @@ static int omap_hsmmc_get_ro(struct mmc_host *mmc) return pdata->slots[0].get_ro(host->dev, 0); } +static void omap_hsmmc_init(struct mmc_omap_host *host) +{ + u32 hctl, capa, value; + + /* Only MMC1 supports 3.0V */ + if (host->id == OMAP_MMC1_DEVID) { + hctl = SDVS30; + capa = VS30 | VS18; + } else { + hctl = SDVS18; + capa = VS18; + } + + value = OMAP_HSMMC_READ(host->base, HCTL) & ~SDVS_MASK; + OMAP_HSMMC_WRITE(host->base, HCTL, value | hctl); + + value = OMAP_HSMMC_READ(host->base, CAPA); + OMAP_HSMMC_WRITE(host->base, CAPA, value | capa); + + /* Set the controller to AUTO IDLE mode */ + value = OMAP_HSMMC_READ(host->base, SYSCONFIG); + OMAP_HSMMC_WRITE(host->base, SYSCONFIG, value | AUTOIDLE); + + /* Set SD bus power bit */ + set_sd_bus_power(host); +} + static struct mmc_host_ops mmc_omap_ops = { .request = omap_mmc_request, .set_ios = omap_mmc_set_ios, @@ -906,7 +971,6 @@ static int __init omap_mmc_probe(struct platform_device *pdev) struct mmc_omap_host *host = NULL; struct resource *res; int ret = 0, irq; - u32 hctl, capa; if (pdata == NULL) { dev_err(&pdev->dev, "Platform Data is missing\n"); @@ -996,10 +1060,11 @@ static int __init omap_mmc_probe(struct platform_device *pdev) else host->dbclk_enabled = 1; -#ifdef CONFIG_MMC_BLOCK_BOUNCE - mmc->max_phys_segs = 1; - mmc->max_hw_segs = 1; -#endif + /* Since we do only SG emulation, we can have as many segs + * as we want. */ + mmc->max_phys_segs = 1024; + mmc->max_hw_segs = 1024; + mmc->max_blk_size = 512; /* Block Length at max can be 1024 */ mmc->max_blk_count = 0xFFFF; /* No. of Blocks is 16 bits */ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; @@ -1008,31 +1073,31 @@ static int __init omap_mmc_probe(struct platform_device *pdev) mmc->ocr_avail = mmc_slot(host).ocr_mask; mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED; - if (pdata->slots[host->slot_id].wires >= 4) + if (pdata->slots[host->slot_id].wires >= 8) + mmc->caps |= MMC_CAP_8_BIT_DATA; + else if (pdata->slots[host->slot_id].wires >= 4) mmc->caps |= MMC_CAP_4_BIT_DATA; - /* Only MMC1 supports 3.0V */ - if (host->id == OMAP_MMC1_DEVID) { - hctl = SDVS30; - capa = VS30 | VS18; - } else { - hctl = SDVS18; - capa = VS18; - } - - OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) | hctl); - - OMAP_HSMMC_WRITE(host->base, CAPA, - OMAP_HSMMC_READ(host->base, CAPA) | capa); - - /* Set the controller to AUTO IDLE mode */ - OMAP_HSMMC_WRITE(host->base, SYSCONFIG, - OMAP_HSMMC_READ(host->base, SYSCONFIG) | AUTOIDLE); + omap_hsmmc_init(host); - /* Set SD bus power bit */ - OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) | SDBP); + /* Select DMA lines */ + switch (host->id) { + case OMAP_MMC1_DEVID: + host->dma_line_tx = OMAP24XX_DMA_MMC1_TX; + host->dma_line_rx = OMAP24XX_DMA_MMC1_RX; + break; + case OMAP_MMC2_DEVID: + host->dma_line_tx = OMAP24XX_DMA_MMC2_TX; + host->dma_line_rx = OMAP24XX_DMA_MMC2_RX; + break; + case OMAP_MMC3_DEVID: + host->dma_line_tx = OMAP34XX_DMA_MMC3_TX; + host->dma_line_rx = OMAP34XX_DMA_MMC3_RX; + break; + default: + dev_err(mmc_dev(host->mmc), "Invalid MMC id\n"); + goto err_irq; + } /* Request IRQ for MMC operations */ ret = request_irq(host->irq, mmc_omap_irq, IRQF_DISABLED, @@ -1051,7 +1116,7 @@ static int __init omap_mmc_probe(struct platform_device *pdev) } /* Request IRQ for card detect */ - if ((mmc_slot(host).card_detect_irq) && (mmc_slot(host).card_detect)) { + if ((mmc_slot(host).card_detect_irq)) { ret = request_irq(mmc_slot(host).card_detect_irq, omap_mmc_cd_handler, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING @@ -1074,8 +1139,8 @@ static int __init omap_mmc_probe(struct platform_device *pdev) if (ret < 0) goto err_slot_name; } - if (mmc_slot(host).card_detect_irq && mmc_slot(host).card_detect && - host->pdata->slots[host->slot_id].get_cover_state) { + if (mmc_slot(host).card_detect_irq && + host->pdata->slots[host->slot_id].get_cover_state) { ret = device_create_file(&mmc->class_dev, &dev_attr_cover_switch); if (ret < 0) @@ -1173,20 +1238,8 @@ static int omap_mmc_suspend(struct platform_device *pdev, pm_message_t state) " level suspend\n"); } - if (host->id == OMAP_MMC1_DEVID - && !(OMAP_HSMMC_READ(host->base, HCTL) - & SDVSDET)) { - OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) - & SDVSCLR); - OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) - | SDVS30); - OMAP_HSMMC_WRITE(host->base, HCTL, - OMAP_HSMMC_READ(host->base, HCTL) - | SDBP); - } - + OMAP_HSMMC_WRITE(host->base, HCTL, + OMAP_HSMMC_READ(host->base, HCTL) & ~SDBP); clk_disable(host->fclk); clk_disable(host->iclk); clk_disable(host->dbclk); @@ -1222,6 +1275,8 @@ static int omap_mmc_resume(struct platform_device *pdev) dev_dbg(mmc_dev(host->mmc), "Enabling debounce clk failed\n"); + omap_hsmmc_init(host); + if (host->pdata->resume) { ret = host->pdata->resume(&pdev->dev, host->slot_id); if (ret) diff --git a/drivers/mmc/host/sdhci-of.c b/drivers/mmc/host/sdhci-of.c new file mode 100644 index 00000000000..3ff4ac3abe8 --- /dev/null +++ b/drivers/mmc/host/sdhci-of.c @@ -0,0 +1,309 @@ +/* + * OpenFirmware bindings for Secure Digital Host Controller Interface. + * + * Copyright (c) 2007 Freescale Semiconductor, Inc. + * Copyright (c) 2009 MontaVista Software, Inc. + * + * Authors: Xiaobo Xie <X.Xie@freescale.com> + * Anton Vorontsov <avorontsov@ru.mvista.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/mmc/host.h> +#include "sdhci.h" + +struct sdhci_of_data { + unsigned int quirks; + struct sdhci_ops ops; +}; + +struct sdhci_of_host { + unsigned int clock; + u16 xfer_mode_shadow; +}; + +/* + * Ops and quirks for the Freescale eSDHC controller. + */ + +#define ESDHC_DMA_SYSCTL 0x40c +#define ESDHC_DMA_SNOOP 0x00000040 + +#define ESDHC_SYSTEM_CONTROL 0x2c +#define ESDHC_CLOCK_MASK 0x0000fff0 +#define ESDHC_PREDIV_SHIFT 8 +#define ESDHC_DIVIDER_SHIFT 4 +#define ESDHC_CLOCK_PEREN 0x00000004 +#define ESDHC_CLOCK_HCKEN 0x00000002 +#define ESDHC_CLOCK_IPGEN 0x00000001 + +static u32 esdhc_readl(struct sdhci_host *host, int reg) +{ + return in_be32(host->ioaddr + reg); +} + +static u16 esdhc_readw(struct sdhci_host *host, int reg) +{ + return in_be16(host->ioaddr + (reg ^ 0x2)); +} + +static u8 esdhc_readb(struct sdhci_host *host, int reg) +{ + return in_8(host->ioaddr + (reg ^ 0x3)); +} + +static void esdhc_writel(struct sdhci_host *host, u32 val, int reg) +{ + out_be32(host->ioaddr + reg, val); +} + +static void esdhc_writew(struct sdhci_host *host, u16 val, int reg) +{ + struct sdhci_of_host *of_host = sdhci_priv(host); + int base = reg & ~0x3; + int shift = (reg & 0x2) * 8; + + switch (reg) { + case SDHCI_TRANSFER_MODE: + /* + * Postpone this write, we must do it together with a + * command write that is down below. + */ + of_host->xfer_mode_shadow = val; + return; + case SDHCI_COMMAND: + esdhc_writel(host, val << 16 | of_host->xfer_mode_shadow, + SDHCI_TRANSFER_MODE); + return; + case SDHCI_BLOCK_SIZE: + /* + * Two last DMA bits are reserved, and first one is used for + * non-standard blksz of 4096 bytes that we don't support + * yet. So clear the DMA boundary bits. + */ + val &= ~SDHCI_MAKE_BLKSZ(0x7, 0); + /* fall through */ + } + clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift); +} + +static void esdhc_writeb(struct sdhci_host *host, u8 val, int reg) +{ + int base = reg & ~0x3; + int shift = (reg & 0x3) * 8; + + clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift); +} + +static void esdhc_set_clock(struct sdhci_host *host, unsigned int clock) +{ + int div; + int pre_div = 2; + + clrbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN | + ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK); + + if (clock == 0) + goto out; + + if (host->max_clk / 16 > clock) { + for (; pre_div < 256; pre_div *= 2) { + if (host->max_clk / pre_div < clock * 16) + break; + } + } + + for (div = 1; div <= 16; div++) { + if (host->max_clk / (div * pre_div) <= clock) + break; + } + + pre_div >>= 1; + + setbits32(host->ioaddr + ESDHC_SYSTEM_CONTROL, ESDHC_CLOCK_IPGEN | + ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN | + div << ESDHC_DIVIDER_SHIFT | pre_div << ESDHC_PREDIV_SHIFT); + mdelay(100); +out: + host->clock = clock; +} + +static int esdhc_enable_dma(struct sdhci_host *host) +{ + setbits32(host->ioaddr + ESDHC_DMA_SYSCTL, ESDHC_DMA_SNOOP); + return 0; +} + +static unsigned int esdhc_get_max_clock(struct sdhci_host *host) +{ + struct sdhci_of_host *of_host = sdhci_priv(host); + + return of_host->clock; +} + +static unsigned int esdhc_get_timeout_clock(struct sdhci_host *host) +{ + struct sdhci_of_host *of_host = sdhci_priv(host); + + return of_host->clock / 1000; +} + +static struct sdhci_of_data sdhci_esdhc = { + .quirks = SDHCI_QUIRK_FORCE_BLK_SZ_2048 | + SDHCI_QUIRK_BROKEN_CARD_DETECTION | + SDHCI_QUIRK_INVERTED_WRITE_PROTECT | + SDHCI_QUIRK_NO_BUSY_IRQ | + SDHCI_QUIRK_NONSTANDARD_CLOCK | + SDHCI_QUIRK_PIO_NEEDS_DELAY | + SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET | + SDHCI_QUIRK_NO_CARD_NO_RESET, + .ops = { + .readl = esdhc_readl, + .readw = esdhc_readw, + .readb = esdhc_readb, + .writel = esdhc_writel, + .writew = esdhc_writew, + .writeb = esdhc_writeb, + .set_clock = esdhc_set_clock, + .enable_dma = esdhc_enable_dma, + .get_max_clock = esdhc_get_max_clock, + .get_timeout_clock = esdhc_get_timeout_clock, + }, +}; + +#ifdef CONFIG_PM + +static int sdhci_of_suspend(struct of_device *ofdev, pm_message_t state) +{ + struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); + + return mmc_suspend_host(host->mmc, state); +} + +static int sdhci_of_resume(struct of_device *ofdev) +{ + struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); + + return mmc_resume_host(host->mmc); +} + +#else + +#define sdhci_of_suspend NULL +#define sdhci_of_resume NULL + +#endif + +static int __devinit sdhci_of_probe(struct of_device *ofdev, + const struct of_device_id *match) +{ + struct device_node *np = ofdev->node; + struct sdhci_of_data *sdhci_of_data = match->data; + struct sdhci_host *host; + struct sdhci_of_host *of_host; + const u32 *clk; + int size; + int ret; + + if (!of_device_is_available(np)) + return -ENODEV; + + host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host)); + if (!host) + return -ENOMEM; + + of_host = sdhci_priv(host); + dev_set_drvdata(&ofdev->dev, host); + + host->ioaddr = of_iomap(np, 0); + if (!host->ioaddr) { + ret = -ENOMEM; + goto err_addr_map; + } + + host->irq = irq_of_parse_and_map(np, 0); + if (!host->irq) { + ret = -EINVAL; + goto err_no_irq; + } + + host->hw_name = dev_name(&ofdev->dev); + if (sdhci_of_data) { + host->quirks = sdhci_of_data->quirks; + host->ops = &sdhci_of_data->ops; + } + + clk = of_get_property(np, "clock-frequency", &size); + if (clk && size == sizeof(*clk) && *clk) + of_host->clock = *clk; + + ret = sdhci_add_host(host); + if (ret) + goto err_add_host; + + return 0; + +err_add_host: + irq_dispose_mapping(host->irq); +err_no_irq: + iounmap(host->ioaddr); +err_addr_map: + sdhci_free_host(host); + return ret; +} + +static int __devexit sdhci_of_remove(struct of_device *ofdev) +{ + struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); + + sdhci_remove_host(host, 0); + sdhci_free_host(host); + irq_dispose_mapping(host->irq); + iounmap(host->ioaddr); + return 0; +} + +static const struct of_device_id sdhci_of_match[] = { + { .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, }, + { .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, }, + { .compatible = "generic-sdhci", }, + {}, +}; +MODULE_DEVICE_TABLE(of, sdhci_of_match); + +static struct of_platform_driver sdhci_of_driver = { + .driver.name = "sdhci-of", + .match_table = sdhci_of_match, + .probe = sdhci_of_probe, + .remove = __devexit_p(sdhci_of_remove), + .suspend = sdhci_of_suspend, + .resume = sdhci_of_resume, +}; + +static int __init sdhci_of_init(void) +{ + return of_register_platform_driver(&sdhci_of_driver); +} +module_init(sdhci_of_init); + +static void __exit sdhci_of_exit(void) +{ + of_unregister_platform_driver(&sdhci_of_driver); +} +module_exit(sdhci_of_exit); + +MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver"); +MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, " + "Anton Vorontsov <avorontsov@ru.mvista.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index accb592764e..30d8e3d4e6f 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -48,35 +48,35 @@ static void sdhci_dumpregs(struct sdhci_host *host) printk(KERN_DEBUG DRIVER_NAME ": ============== REGISTER DUMP ==============\n"); printk(KERN_DEBUG DRIVER_NAME ": Sys addr: 0x%08x | Version: 0x%08x\n", - readl(host->ioaddr + SDHCI_DMA_ADDRESS), - readw(host->ioaddr + SDHCI_HOST_VERSION)); + sdhci_readl(host, SDHCI_DMA_ADDRESS), + sdhci_readw(host, SDHCI_HOST_VERSION)); printk(KERN_DEBUG DRIVER_NAME ": Blk size: 0x%08x | Blk cnt: 0x%08x\n", - readw(host->ioaddr + SDHCI_BLOCK_SIZE), - readw(host->ioaddr + SDHCI_BLOCK_COUNT)); + sdhci_readw(host, SDHCI_BLOCK_SIZE), + sdhci_readw(host, SDHCI_BLOCK_COUNT)); printk(KERN_DEBUG DRIVER_NAME ": Argument: 0x%08x | Trn mode: 0x%08x\n", - readl(host->ioaddr + SDHCI_ARGUMENT), - readw(host->ioaddr + SDHCI_TRANSFER_MODE)); + sdhci_readl(host, SDHCI_ARGUMENT), + sdhci_readw(host, SDHCI_TRANSFER_MODE)); printk(KERN_DEBUG DRIVER_NAME ": Present: 0x%08x | Host ctl: 0x%08x\n", - readl(host->ioaddr + SDHCI_PRESENT_STATE), - readb(host->ioaddr + SDHCI_HOST_CONTROL)); + sdhci_readl(host, SDHCI_PRESENT_STATE), + sdhci_readb(host, SDHCI_HOST_CONTROL)); printk(KERN_DEBUG DRIVER_NAME ": Power: 0x%08x | Blk gap: 0x%08x\n", - readb(host->ioaddr + SDHCI_POWER_CONTROL), - readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL)); + sdhci_readb(host, SDHCI_POWER_CONTROL), + sdhci_readb(host, SDHCI_BLOCK_GAP_CONTROL)); printk(KERN_DEBUG DRIVER_NAME ": Wake-up: 0x%08x | Clock: 0x%08x\n", - readb(host->ioaddr + SDHCI_WAKE_UP_CONTROL), - readw(host->ioaddr + SDHCI_CLOCK_CONTROL)); + sdhci_readb(host, SDHCI_WAKE_UP_CONTROL), + sdhci_readw(host, SDHCI_CLOCK_CONTROL)); printk(KERN_DEBUG DRIVER_NAME ": Timeout: 0x%08x | Int stat: 0x%08x\n", - readb(host->ioaddr + SDHCI_TIMEOUT_CONTROL), - readl(host->ioaddr + SDHCI_INT_STATUS)); + sdhci_readb(host, SDHCI_TIMEOUT_CONTROL), + sdhci_readl(host, SDHCI_INT_STATUS)); printk(KERN_DEBUG DRIVER_NAME ": Int enab: 0x%08x | Sig enab: 0x%08x\n", - readl(host->ioaddr + SDHCI_INT_ENABLE), - readl(host->ioaddr + SDHCI_SIGNAL_ENABLE)); + sdhci_readl(host, SDHCI_INT_ENABLE), + sdhci_readl(host, SDHCI_SIGNAL_ENABLE)); printk(KERN_DEBUG DRIVER_NAME ": AC12 err: 0x%08x | Slot int: 0x%08x\n", - readw(host->ioaddr + SDHCI_ACMD12_ERR), - readw(host->ioaddr + SDHCI_SLOT_INT_STATUS)); + sdhci_readw(host, SDHCI_ACMD12_ERR), + sdhci_readw(host, SDHCI_SLOT_INT_STATUS)); printk(KERN_DEBUG DRIVER_NAME ": Caps: 0x%08x | Max curr: 0x%08x\n", - readl(host->ioaddr + SDHCI_CAPABILITIES), - readl(host->ioaddr + SDHCI_MAX_CURRENT)); + sdhci_readl(host, SDHCI_CAPABILITIES), + sdhci_readl(host, SDHCI_MAX_CURRENT)); printk(KERN_DEBUG DRIVER_NAME ": ===========================================\n"); } @@ -87,17 +87,65 @@ static void sdhci_dumpregs(struct sdhci_host *host) * * \*****************************************************************************/ +static void sdhci_clear_set_irqs(struct sdhci_host *host, u32 clear, u32 set) +{ + u32 ier; + + ier = sdhci_readl(host, SDHCI_INT_ENABLE); + ier &= ~clear; + ier |= set; + sdhci_writel(host, ier, SDHCI_INT_ENABLE); + sdhci_writel(host, ier, SDHCI_SIGNAL_ENABLE); +} + +static void sdhci_unmask_irqs(struct sdhci_host *host, u32 irqs) +{ + sdhci_clear_set_irqs(host, 0, irqs); +} + +static void sdhci_mask_irqs(struct sdhci_host *host, u32 irqs) +{ + sdhci_clear_set_irqs(host, irqs, 0); +} + +static void sdhci_set_card_detection(struct sdhci_host *host, bool enable) +{ + u32 irqs = SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT; + + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + return; + + if (enable) + sdhci_unmask_irqs(host, irqs); + else + sdhci_mask_irqs(host, irqs); +} + +static void sdhci_enable_card_detection(struct sdhci_host *host) +{ + sdhci_set_card_detection(host, true); +} + +static void sdhci_disable_card_detection(struct sdhci_host *host) +{ + sdhci_set_card_detection(host, false); +} + static void sdhci_reset(struct sdhci_host *host, u8 mask) { unsigned long timeout; + u32 uninitialized_var(ier); if (host->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & + if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) return; } - writeb(mask, host->ioaddr + SDHCI_SOFTWARE_RESET); + if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) + ier = sdhci_readl(host, SDHCI_INT_ENABLE); + + sdhci_writeb(host, mask, SDHCI_SOFTWARE_RESET); if (mask & SDHCI_RESET_ALL) host->clock = 0; @@ -106,7 +154,7 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) timeout = 100; /* hw clears the bit when it's done */ - while (readb(host->ioaddr + SDHCI_SOFTWARE_RESET) & mask) { + while (sdhci_readb(host, SDHCI_SOFTWARE_RESET) & mask) { if (timeout == 0) { printk(KERN_ERR "%s: Reset 0x%x never completed.\n", mmc_hostname(host->mmc), (int)mask); @@ -116,42 +164,44 @@ static void sdhci_reset(struct sdhci_host *host, u8 mask) timeout--; mdelay(1); } + + if (host->quirks & SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET) + sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, ier); } static void sdhci_init(struct sdhci_host *host) { - u32 intmask; - sdhci_reset(host, SDHCI_RESET_ALL); - intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | + sdhci_clear_set_irqs(host, SDHCI_INT_ALL_MASK, + SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC | SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC | SDHCI_INT_TIMEOUT | - SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT | - SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | - SDHCI_INT_DMA_END | SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE | - SDHCI_INT_ADMA_ERROR; + SDHCI_INT_DATA_END | SDHCI_INT_RESPONSE); +} - writel(intmask, host->ioaddr + SDHCI_INT_ENABLE); - writel(intmask, host->ioaddr + SDHCI_SIGNAL_ENABLE); +static void sdhci_reinit(struct sdhci_host *host) +{ + sdhci_init(host); + sdhci_enable_card_detection(host); } static void sdhci_activate_led(struct sdhci_host *host) { u8 ctrl; - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl |= SDHCI_CTRL_LED; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } static void sdhci_deactivate_led(struct sdhci_host *host) { u8 ctrl; - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_LED; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } #ifdef SDHCI_USE_LEDS_CLASS @@ -205,7 +255,7 @@ static void sdhci_read_block_pio(struct sdhci_host *host) while (len) { if (chunk == 0) { - scratch = readl(host->ioaddr + SDHCI_BUFFER); + scratch = sdhci_readl(host, SDHCI_BUFFER); chunk = 4; } @@ -257,7 +307,7 @@ static void sdhci_write_block_pio(struct sdhci_host *host) len--; if ((chunk == 4) || ((len == 0) && (blksize == 0))) { - writel(scratch, host->ioaddr + SDHCI_BUFFER); + sdhci_writel(host, scratch, SDHCI_BUFFER); chunk = 0; scratch = 0; } @@ -292,7 +342,10 @@ static void sdhci_transfer_pio(struct sdhci_host *host) (host->data->blocks == 1)) mask = ~0; - while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { + while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { + if (host->quirks & SDHCI_QUIRK_PIO_NEEDS_DELAY) + udelay(100); + if (host->data->flags & MMC_DATA_READ) sdhci_read_block_pio(host); else @@ -561,6 +614,17 @@ static u8 sdhci_calc_timeout(struct sdhci_host *host, struct mmc_data *data) return count; } +static void sdhci_set_transfer_irqs(struct sdhci_host *host) +{ + u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; + u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; + + if (host->flags & SDHCI_REQ_USE_DMA) + sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); + else + sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); +} + static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) { u8 count; @@ -581,7 +645,7 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) host->data_early = 0; count = sdhci_calc_timeout(host, data); - writeb(count, host->ioaddr + SDHCI_TIMEOUT_CONTROL); + sdhci_writeb(host, count, SDHCI_TIMEOUT_CONTROL); if (host->flags & SDHCI_USE_DMA) host->flags |= SDHCI_REQ_USE_DMA; @@ -661,8 +725,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) WARN_ON(1); host->flags &= ~SDHCI_REQ_USE_DMA; } else { - writel(host->adma_addr, - host->ioaddr + SDHCI_ADMA_ADDRESS); + sdhci_writel(host, host->adma_addr, + SDHCI_ADMA_ADDRESS); } } else { int sg_cnt; @@ -681,8 +745,8 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) host->flags &= ~SDHCI_REQ_USE_DMA; } else { WARN_ON(sg_cnt != 1); - writel(sg_dma_address(data->sg), - host->ioaddr + SDHCI_DMA_ADDRESS); + sdhci_writel(host, sg_dma_address(data->sg), + SDHCI_DMA_ADDRESS); } } } @@ -693,14 +757,14 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) * is ADMA. */ if (host->version >= SDHCI_SPEC_200) { - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; if ((host->flags & SDHCI_REQ_USE_DMA) && (host->flags & SDHCI_USE_ADMA)) ctrl |= SDHCI_CTRL_ADMA32; else ctrl |= SDHCI_CTRL_SDMA; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); } if (!(host->flags & SDHCI_REQ_USE_DMA)) { @@ -709,10 +773,11 @@ static void sdhci_prepare_data(struct sdhci_host *host, struct mmc_data *data) host->blocks = data->blocks; } + sdhci_set_transfer_irqs(host); + /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - writew(SDHCI_MAKE_BLKSZ(7, data->blksz), - host->ioaddr + SDHCI_BLOCK_SIZE); - writew(data->blocks, host->ioaddr + SDHCI_BLOCK_COUNT); + sdhci_writew(host, SDHCI_MAKE_BLKSZ(7, data->blksz), SDHCI_BLOCK_SIZE); + sdhci_writew(host, data->blocks, SDHCI_BLOCK_COUNT); } static void sdhci_set_transfer_mode(struct sdhci_host *host, @@ -733,7 +798,7 @@ static void sdhci_set_transfer_mode(struct sdhci_host *host, if (host->flags & SDHCI_REQ_USE_DMA) mode |= SDHCI_TRNS_DMA; - writew(mode, host->ioaddr + SDHCI_TRANSFER_MODE); + sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); } static void sdhci_finish_data(struct sdhci_host *host) @@ -802,7 +867,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) if (host->mrq->data && (cmd == host->mrq->data->stop)) mask &= ~SDHCI_DATA_INHIBIT; - while (readl(host->ioaddr + SDHCI_PRESENT_STATE) & mask) { + while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { if (timeout == 0) { printk(KERN_ERR "%s: Controller never released " "inhibit bit(s).\n", mmc_hostname(host->mmc)); @@ -821,7 +886,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) sdhci_prepare_data(host, cmd->data); - writel(cmd->arg, host->ioaddr + SDHCI_ARGUMENT); + sdhci_writel(host, cmd->arg, SDHCI_ARGUMENT); sdhci_set_transfer_mode(host, cmd->data); @@ -849,8 +914,7 @@ static void sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) if (cmd->data) flags |= SDHCI_CMD_DATA; - writew(SDHCI_MAKE_CMD(cmd->opcode, flags), - host->ioaddr + SDHCI_COMMAND); + sdhci_writew(host, SDHCI_MAKE_CMD(cmd->opcode, flags), SDHCI_COMMAND); } static void sdhci_finish_command(struct sdhci_host *host) @@ -863,15 +927,15 @@ static void sdhci_finish_command(struct sdhci_host *host) if (host->cmd->flags & MMC_RSP_136) { /* CRC is stripped so we need to do some shifting. */ for (i = 0;i < 4;i++) { - host->cmd->resp[i] = readl(host->ioaddr + + host->cmd->resp[i] = sdhci_readl(host, SDHCI_RESPONSE + (3-i)*4) << 8; if (i != 3) host->cmd->resp[i] |= - readb(host->ioaddr + + sdhci_readb(host, SDHCI_RESPONSE + (3-i)*4-1); } } else { - host->cmd->resp[0] = readl(host->ioaddr + SDHCI_RESPONSE); + host->cmd->resp[0] = sdhci_readl(host, SDHCI_RESPONSE); } } @@ -895,7 +959,13 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) if (clock == host->clock) return; - writew(0, host->ioaddr + SDHCI_CLOCK_CONTROL); + if (host->ops->set_clock) { + host->ops->set_clock(host, clock); + if (host->quirks & SDHCI_QUIRK_NONSTANDARD_CLOCK) + return; + } + + sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); if (clock == 0) goto out; @@ -908,11 +978,11 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) clk = div << SDHCI_DIVIDER_SHIFT; clk |= SDHCI_CLOCK_INT_EN; - writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); /* Wait max 10 ms */ timeout = 10; - while (!((clk = readw(host->ioaddr + SDHCI_CLOCK_CONTROL)) + while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) & SDHCI_CLOCK_INT_STABLE)) { if (timeout == 0) { printk(KERN_ERR "%s: Internal clock never " @@ -925,7 +995,7 @@ static void sdhci_set_clock(struct sdhci_host *host, unsigned int clock) } clk |= SDHCI_CLOCK_CARD_EN; - writew(clk, host->ioaddr + SDHCI_CLOCK_CONTROL); + sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); out: host->clock = clock; @@ -939,7 +1009,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) return; if (power == (unsigned short)-1) { - writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); goto out; } @@ -948,7 +1018,7 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) * a new value. Some controllers don't seem to like this though. */ if (!(host->quirks & SDHCI_QUIRK_SINGLE_POWER_WRITE)) - writeb(0, host->ioaddr + SDHCI_POWER_CONTROL); + sdhci_writeb(host, 0, SDHCI_POWER_CONTROL); pwr = SDHCI_POWER_ON; @@ -973,10 +1043,9 @@ static void sdhci_set_power(struct sdhci_host *host, unsigned short power) * and set turn on power at the same time, so set the voltage first. */ if ((host->quirks & SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER)) - writeb(pwr & ~SDHCI_POWER_ON, - host->ioaddr + SDHCI_POWER_CONTROL); + sdhci_writeb(host, pwr & ~SDHCI_POWER_ON, SDHCI_POWER_CONTROL); - writeb(pwr, host->ioaddr + SDHCI_POWER_CONTROL); + sdhci_writeb(host, pwr, SDHCI_POWER_CONTROL); out: host->power = power; @@ -991,6 +1060,7 @@ out: static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct sdhci_host *host; + bool present; unsigned long flags; host = mmc_priv(mmc); @@ -1005,8 +1075,14 @@ static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq) host->mrq = mrq; - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT) - || (host->flags & SDHCI_DEVICE_DEAD)) { + /* If polling, assume that the card is always present. */ + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + present = true; + else + present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; + + if (!present || host->flags & SDHCI_DEVICE_DEAD) { host->mrq->cmd->error = -ENOMEDIUM; tasklet_schedule(&host->finish_tasklet); } else @@ -1034,8 +1110,8 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) * Should clear out any weird states. */ if (ios->power_mode == MMC_POWER_OFF) { - writel(0, host->ioaddr + SDHCI_SIGNAL_ENABLE); - sdhci_init(host); + sdhci_writel(host, 0, SDHCI_SIGNAL_ENABLE); + sdhci_reinit(host); } sdhci_set_clock(host, ios->clock); @@ -1045,7 +1121,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else sdhci_set_power(host, ios->vdd); - ctrl = readb(host->ioaddr + SDHCI_HOST_CONTROL); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (ios->bus_width == MMC_BUS_WIDTH_4) ctrl |= SDHCI_CTRL_4BITBUS; @@ -1057,7 +1133,7 @@ static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) else ctrl &= ~SDHCI_CTRL_HISPD; - writeb(ctrl, host->ioaddr + SDHCI_HOST_CONTROL); + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); /* * Some (ENE) controllers go apeshit on some ios operation, @@ -1085,10 +1161,12 @@ static int sdhci_get_ro(struct mmc_host *mmc) if (host->flags & SDHCI_DEVICE_DEAD) present = 0; else - present = readl(host->ioaddr + SDHCI_PRESENT_STATE); + present = sdhci_readl(host, SDHCI_PRESENT_STATE); spin_unlock_irqrestore(&host->lock, flags); + if (host->quirks & SDHCI_QUIRK_INVERTED_WRITE_PROTECT) + return !!(present & SDHCI_WRITE_PROTECT); return !(present & SDHCI_WRITE_PROTECT); } @@ -1096,7 +1174,6 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct sdhci_host *host; unsigned long flags; - u32 ier; host = mmc_priv(mmc); @@ -1105,15 +1182,10 @@ static void sdhci_enable_sdio_irq(struct mmc_host *mmc, int enable) if (host->flags & SDHCI_DEVICE_DEAD) goto out; - ier = readl(host->ioaddr + SDHCI_INT_ENABLE); - - ier &= ~SDHCI_INT_CARD_INT; if (enable) - ier |= SDHCI_INT_CARD_INT; - - writel(ier, host->ioaddr + SDHCI_INT_ENABLE); - writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); - + sdhci_unmask_irqs(host, SDHCI_INT_CARD_INT); + else + sdhci_mask_irqs(host, SDHCI_INT_CARD_INT); out: mmiowb(); @@ -1142,7 +1214,7 @@ static void sdhci_tasklet_card(unsigned long param) spin_lock_irqsave(&host->lock, flags); - if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { + if (!(sdhci_readl(host, SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)) { if (host->mrq) { printk(KERN_ERR "%s: Card removed during transfer!\n", mmc_hostname(host->mmc)); @@ -1346,8 +1418,8 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) * we need to at least restart the transfer. */ if (intmask & SDHCI_INT_DMA_END) - writel(readl(host->ioaddr + SDHCI_DMA_ADDRESS), - host->ioaddr + SDHCI_DMA_ADDRESS); + sdhci_writel(host, sdhci_readl(host, SDHCI_DMA_ADDRESS), + SDHCI_DMA_ADDRESS); if (intmask & SDHCI_INT_DATA_END) { if (host->cmd) { @@ -1373,7 +1445,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) spin_lock(&host->lock); - intmask = readl(host->ioaddr + SDHCI_INT_STATUS); + intmask = sdhci_readl(host, SDHCI_INT_STATUS); if (!intmask || intmask == 0xffffffff) { result = IRQ_NONE; @@ -1384,22 +1456,22 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) mmc_hostname(host->mmc), intmask); if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) { - writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE), - host->ioaddr + SDHCI_INT_STATUS); + sdhci_writel(host, intmask & (SDHCI_INT_CARD_INSERT | + SDHCI_INT_CARD_REMOVE), SDHCI_INT_STATUS); tasklet_schedule(&host->card_tasklet); } intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE); if (intmask & SDHCI_INT_CMD_MASK) { - writel(intmask & SDHCI_INT_CMD_MASK, - host->ioaddr + SDHCI_INT_STATUS); + sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, + SDHCI_INT_STATUS); sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK); } if (intmask & SDHCI_INT_DATA_MASK) { - writel(intmask & SDHCI_INT_DATA_MASK, - host->ioaddr + SDHCI_INT_STATUS); + sdhci_writel(host, intmask & SDHCI_INT_DATA_MASK, + SDHCI_INT_STATUS); sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK); } @@ -1410,7 +1482,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) if (intmask & SDHCI_INT_BUS_POWER) { printk(KERN_ERR "%s: Card is consuming too much power!\n", mmc_hostname(host->mmc)); - writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS); + sdhci_writel(host, SDHCI_INT_BUS_POWER, SDHCI_INT_STATUS); } intmask &= ~SDHCI_INT_BUS_POWER; @@ -1425,7 +1497,7 @@ static irqreturn_t sdhci_irq(int irq, void *dev_id) mmc_hostname(host->mmc), intmask); sdhci_dumpregs(host); - writel(intmask, host->ioaddr + SDHCI_INT_STATUS); + sdhci_writel(host, intmask, SDHCI_INT_STATUS); } result = IRQ_HANDLED; @@ -1455,6 +1527,8 @@ int sdhci_suspend_host(struct sdhci_host *host, pm_message_t state) { int ret; + sdhci_disable_card_detection(host); + ret = mmc_suspend_host(host->mmc, state); if (ret) return ret; @@ -1487,6 +1561,8 @@ int sdhci_resume_host(struct sdhci_host *host) if (ret) return ret; + sdhci_enable_card_detection(host); + return 0; } @@ -1537,7 +1613,7 @@ int sdhci_add_host(struct sdhci_host *host) sdhci_reset(host, SDHCI_RESET_ALL); - host->version = readw(host->ioaddr + SDHCI_HOST_VERSION); + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); host->version = (host->version & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT; if (host->version > SDHCI_SPEC_200) { @@ -1546,7 +1622,7 @@ int sdhci_add_host(struct sdhci_host *host) host->version); } - caps = readl(host->ioaddr + SDHCI_CAPABILITIES); + caps = sdhci_readl(host, SDHCI_CAPABILITIES); if (host->quirks & SDHCI_QUIRK_FORCE_DMA) host->flags |= SDHCI_USE_DMA; @@ -1614,19 +1690,27 @@ int sdhci_add_host(struct sdhci_host *host) host->max_clk = (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; + host->max_clk *= 1000000; if (host->max_clk == 0) { - printk(KERN_ERR "%s: Hardware doesn't specify base clock " - "frequency.\n", mmc_hostname(mmc)); - return -ENODEV; + if (!host->ops->get_max_clock) { + printk(KERN_ERR + "%s: Hardware doesn't specify base clock " + "frequency.\n", mmc_hostname(mmc)); + return -ENODEV; + } + host->max_clk = host->ops->get_max_clock(host); } - host->max_clk *= 1000000; host->timeout_clk = (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT; if (host->timeout_clk == 0) { - printk(KERN_ERR "%s: Hardware doesn't specify timeout clock " - "frequency.\n", mmc_hostname(mmc)); - return -ENODEV; + if (!host->ops->get_timeout_clock) { + printk(KERN_ERR + "%s: Hardware doesn't specify timeout clock " + "frequency.\n", mmc_hostname(mmc)); + return -ENODEV; + } + host->timeout_clk = host->ops->get_timeout_clock(host); } if (caps & SDHCI_TIMEOUT_CLK_UNIT) host->timeout_clk *= 1000; @@ -1642,6 +1726,9 @@ int sdhci_add_host(struct sdhci_host *host) if (caps & SDHCI_CAN_DO_HISPD) mmc->caps |= MMC_CAP_SD_HIGHSPEED; + if (host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION) + mmc->caps |= MMC_CAP_NEEDS_POLL; + mmc->ocr_avail = 0; if (caps & SDHCI_CAN_VDD_330) mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34; @@ -1690,13 +1777,19 @@ int sdhci_add_host(struct sdhci_host *host) * Maximum block size. This varies from controller to controller and * is specified in the capabilities register. */ - mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT; - if (mmc->max_blk_size >= 3) { - printk(KERN_WARNING "%s: Invalid maximum block size, " - "assuming 512 bytes\n", mmc_hostname(mmc)); - mmc->max_blk_size = 512; - } else - mmc->max_blk_size = 512 << mmc->max_blk_size; + if (host->quirks & SDHCI_QUIRK_FORCE_BLK_SZ_2048) { + mmc->max_blk_size = 2; + } else { + mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> + SDHCI_MAX_BLOCK_SHIFT; + if (mmc->max_blk_size >= 3) { + printk(KERN_WARNING "%s: Invalid maximum block size, " + "assuming 512 bytes\n", mmc_hostname(mmc)); + mmc->max_blk_size = 0; + } + } + + mmc->max_blk_size = 512 << mmc->max_blk_size; /* * Maximum block count. @@ -1746,6 +1839,8 @@ int sdhci_add_host(struct sdhci_host *host) (host->flags & SDHCI_USE_ADMA)?"A":"", (host->flags & SDHCI_USE_DMA)?"DMA":"PIO"); + sdhci_enable_card_detection(host); + return 0; #ifdef SDHCI_USE_LEDS_CLASS @@ -1782,6 +1877,8 @@ void sdhci_remove_host(struct sdhci_host *host, int dead) spin_unlock_irqrestore(&host->lock, flags); } + sdhci_disable_card_detection(host); + mmc_remove_host(host->mmc); #ifdef SDHCI_USE_LEDS_CLASS diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index 43c37c68d07..f20a834f430 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -10,6 +10,9 @@ */ #include <linux/scatterlist.h> +#include <linux/compiler.h> +#include <linux/types.h> +#include <linux/io.h> /* * Controller registers @@ -123,6 +126,7 @@ SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL | \ SDHCI_INT_DATA_TIMEOUT | SDHCI_INT_DATA_CRC | \ SDHCI_INT_DATA_END_BIT) +#define SDHCI_INT_ALL_MASK ((unsigned int)-1) #define SDHCI_ACMD12_ERR 0x3C @@ -210,6 +214,18 @@ struct sdhci_host { #define SDHCI_QUIRK_BROKEN_SMALL_PIO (1<<13) /* Controller does not provide transfer-complete interrupt when not busy */ #define SDHCI_QUIRK_NO_BUSY_IRQ (1<<14) +/* Controller has unreliable card detection */ +#define SDHCI_QUIRK_BROKEN_CARD_DETECTION (1<<15) +/* Controller reports inverted write-protect state */ +#define SDHCI_QUIRK_INVERTED_WRITE_PROTECT (1<<16) +/* Controller has nonstandard clock management */ +#define SDHCI_QUIRK_NONSTANDARD_CLOCK (1<<17) +/* Controller does not like fast PIO transfers */ +#define SDHCI_QUIRK_PIO_NEEDS_DELAY (1<<18) +/* Controller losing signal/interrupt enable states after reset */ +#define SDHCI_QUIRK_RESTORE_IRQS_AFTER_RESET (1<<19) +/* Controller has to be forced to use block size of 2048 bytes */ +#define SDHCI_QUIRK_FORCE_BLK_SZ_2048 (1<<20) int irq; /* Device IRQ */ void __iomem * ioaddr; /* Mapped address */ @@ -267,9 +283,105 @@ struct sdhci_host { struct sdhci_ops { +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + u32 (*readl)(struct sdhci_host *host, int reg); + u16 (*readw)(struct sdhci_host *host, int reg); + u8 (*readb)(struct sdhci_host *host, int reg); + void (*writel)(struct sdhci_host *host, u32 val, int reg); + void (*writew)(struct sdhci_host *host, u16 val, int reg); + void (*writeb)(struct sdhci_host *host, u8 val, int reg); +#endif + + void (*set_clock)(struct sdhci_host *host, unsigned int clock); + int (*enable_dma)(struct sdhci_host *host); + unsigned int (*get_max_clock)(struct sdhci_host *host); + unsigned int (*get_timeout_clock)(struct sdhci_host *host); }; +#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS + +static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + if (unlikely(host->ops->writel)) + host->ops->writel(host, val, reg); + else + writel(val, host->ioaddr + reg); +} + +static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + if (unlikely(host->ops->writew)) + host->ops->writew(host, val, reg); + else + writew(val, host->ioaddr + reg); +} + +static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + if (unlikely(host->ops->writeb)) + host->ops->writeb(host, val, reg); + else + writeb(val, host->ioaddr + reg); +} + +static inline u32 sdhci_readl(struct sdhci_host *host, int reg) +{ + if (unlikely(host->ops->readl)) + return host->ops->readl(host, reg); + else + return readl(host->ioaddr + reg); +} + +static inline u16 sdhci_readw(struct sdhci_host *host, int reg) +{ + if (unlikely(host->ops->readw)) + return host->ops->readw(host, reg); + else + return readw(host->ioaddr + reg); +} + +static inline u8 sdhci_readb(struct sdhci_host *host, int reg) +{ + if (unlikely(host->ops->readb)) + return host->ops->readb(host, reg); + else + return readb(host->ioaddr + reg); +} + +#else + +static inline void sdhci_writel(struct sdhci_host *host, u32 val, int reg) +{ + writel(val, host->ioaddr + reg); +} + +static inline void sdhci_writew(struct sdhci_host *host, u16 val, int reg) +{ + writew(val, host->ioaddr + reg); +} + +static inline void sdhci_writeb(struct sdhci_host *host, u8 val, int reg) +{ + writeb(val, host->ioaddr + reg); +} + +static inline u32 sdhci_readl(struct sdhci_host *host, int reg) +{ + return readl(host->ioaddr + reg); +} + +static inline u16 sdhci_readw(struct sdhci_host *host, int reg) +{ + return readw(host->ioaddr + reg); +} + +static inline u8 sdhci_readb(struct sdhci_host *host, int reg) +{ + return readb(host->ioaddr + reg); +} + +#endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */ extern struct sdhci_host *sdhci_alloc_host(struct device *dev, size_t priv_size); diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index 6a7a6190483..63fbd5b7d31 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -568,11 +568,11 @@ static int __devinit tmio_mmc_probe(struct platform_device *dev) host->mmc = mmc; platform_set_drvdata(dev, mmc); - host->ctl = ioremap(res_ctl->start, res_ctl->end - res_ctl->start); + host->ctl = ioremap(res_ctl->start, resource_size(res_ctl)); if (!host->ctl) goto host_free; - host->cnf = ioremap(res_cnf->start, res_cnf->end - res_cnf->start); + host->cnf = ioremap(res_cnf->start, resource_size(res_cnf)); if (!host->cnf) goto unmap_ctl; @@ -650,10 +650,10 @@ static int __devexit tmio_mmc_remove(struct platform_device *dev) if (mmc) { struct tmio_mmc_host *host = mmc_priv(mmc); mmc_remove_host(mmc); - mmc_free_host(mmc); free_irq(host->irq, host); iounmap(host->ctl); iounmap(host->cnf); + mmc_free_host(mmc); } return 0; diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index ba2b4240a86..9c831ab2ece 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -8,6 +8,9 @@ * published by the Free Software Foundation. * */ + +#include <linux/highmem.h> + #define CNF_CMD 0x04 #define CNF_CTL_BASE 0x10 #define CNF_INT_PIN 0x3d diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c index e4226e02d63..e51c1ed7ac1 100644 --- a/drivers/mtd/nand/diskonchip.c +++ b/drivers/mtd/nand/diskonchip.c @@ -1773,4 +1773,4 @@ module_exit(cleanup_nanddoc); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n"); +MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver"); diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 0c3afccde8a..5f71371eb1b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2720,14 +2720,14 @@ int nand_scan_tail(struct mtd_info *mtd) return chip->scan_bbt(mtd); } -/* module_text_address() isn't exported, and it's mostly a pointless +/* is_module_text_address() isn't exported, and it's mostly a pointless test if this is a module _anyway_ -- they'd have to try _really_ hard to call us from in-kernel code if the core NAND support is modular. */ #ifdef MODULE #define caller_is_module() (1) #else #define caller_is_module() \ - module_text_address((unsigned long)__builtin_return_address(0)) + is_module_text_address((unsigned long)__builtin_return_address(0)) #endif /** diff --git a/drivers/mtd/tests/mtd_oobtest.c b/drivers/mtd/tests/mtd_oobtest.c index afbc3f8126d..a18e8d2f255 100644 --- a/drivers/mtd/tests/mtd_oobtest.c +++ b/drivers/mtd/tests/mtd_oobtest.c @@ -136,7 +136,7 @@ static int write_eraseblock(int ebnum) ops.ooblen = use_len; ops.oobretlen = 0; ops.ooboffs = use_offset; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = writebuf; err = mtd->write_oob(mtd, addr, &ops); if (err || ops.oobretlen != use_len) { @@ -189,7 +189,7 @@ static int verify_eraseblock(int ebnum) ops.ooblen = use_len; ops.oobretlen = 0; ops.ooboffs = use_offset; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd->read_oob(mtd, addr, &ops); if (err || ops.oobretlen != use_len) { @@ -216,7 +216,7 @@ static int verify_eraseblock(int ebnum) ops.ooblen = mtd->ecclayout->oobavail; ops.oobretlen = 0; ops.ooboffs = 0; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd->read_oob(mtd, addr, &ops); if (err || ops.oobretlen != mtd->ecclayout->oobavail) { @@ -281,7 +281,7 @@ static int verify_eraseblock_in_one_go(int ebnum) ops.ooblen = len; ops.oobretlen = 0; ops.ooboffs = 0; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd->read_oob(mtd, addr, &ops); if (err || ops.oobretlen != len) { @@ -522,7 +522,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = 1; ops.oobretlen = 0; ops.ooboffs = mtd->ecclayout->oobavail; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = writebuf; printk(PRINT_PREF "attempting to start write past end of OOB\n"); printk(PRINT_PREF "an error is expected...\n"); @@ -542,7 +542,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = 1; ops.oobretlen = 0; ops.ooboffs = mtd->ecclayout->oobavail; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = readbuf; printk(PRINT_PREF "attempting to start read past end of OOB\n"); printk(PRINT_PREF "an error is expected...\n"); @@ -566,7 +566,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = mtd->ecclayout->oobavail + 1; ops.oobretlen = 0; ops.ooboffs = 0; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = writebuf; printk(PRINT_PREF "attempting to write past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); @@ -586,7 +586,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = mtd->ecclayout->oobavail + 1; ops.oobretlen = 0; ops.ooboffs = 0; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = readbuf; printk(PRINT_PREF "attempting to read past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); @@ -610,7 +610,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = mtd->ecclayout->oobavail; ops.oobretlen = 0; ops.ooboffs = 1; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = writebuf; printk(PRINT_PREF "attempting to write past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); @@ -630,7 +630,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = mtd->ecclayout->oobavail; ops.oobretlen = 0; ops.ooboffs = 1; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = readbuf; printk(PRINT_PREF "attempting to read past end of device\n"); printk(PRINT_PREF "an error is expected...\n"); @@ -670,7 +670,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = sz; ops.oobretlen = 0; ops.ooboffs = 0; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = writebuf; err = mtd->write_oob(mtd, addr, &ops); if (err) @@ -698,7 +698,7 @@ static int __init mtd_oobtest_init(void) ops.ooblen = mtd->ecclayout->oobavail * 2; ops.oobretlen = 0; ops.ooboffs = 0; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = readbuf; err = mtd->read_oob(mtd, addr, &ops); if (err) diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 645e77fdc63..79fc4530987 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c @@ -71,7 +71,7 @@ static int read_eraseblock_by_page(int ebnum) ops.ooblen = mtd->oobsize; ops.oobretlen = 0; ops.ooboffs = 0; - ops.datbuf = 0; + ops.datbuf = NULL; ops.oobbuf = oobbuf; ret = mtd->read_oob(mtd, addr, &ops); if (ret || ops.oobretlen != mtd->oobsize) { diff --git a/drivers/net/sb1250-mac.c b/drivers/net/sb1250-mac.c index 88dd2e09832..ce7551e17ba 100644 --- a/drivers/net/sb1250-mac.c +++ b/drivers/net/sb1250-mac.c @@ -2299,7 +2299,7 @@ static int sbmac_init(struct platform_device *pldev, long long base) eaddr = sc->sbm_hwaddr; /* - * Read the ethernet address. The firwmare left this programmed + * Read the ethernet address. The firmware left this programmed * for us in the ethernet address register for each mac. */ diff --git a/drivers/net/skfp/h/hwmtm.h b/drivers/net/skfp/h/hwmtm.h index 1a606d4bfe5..e1a7e5f683d 100644 --- a/drivers/net/skfp/h/hwmtm.h +++ b/drivers/net/skfp/h/hwmtm.h @@ -145,7 +145,7 @@ struct hw_modul { int leave_isr ; /* leave fddi_isr immedeately if set */ int isr_flag ; /* set, when HWM is entered from isr */ /* - * varaibles for the current transmit frame + * variables for the current transmit frame */ struct s_smt_tx_queue *tx_p ; /* pointer to the transmit queue */ u_long tx_descr ; /* tx descriptor for FORMAC+ */ diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c index 1205c2a2265..437683aab32 100644 --- a/drivers/net/tg3.c +++ b/drivers/net/tg3.c @@ -11225,7 +11225,7 @@ static int __devinit tg3_phy_probe(struct tg3 *tp) return tg3_phy_init(tp); /* Reading the PHY ID register can conflict with ASF - * firwmare access to the PHY hardware. + * firmware access to the PHY hardware. */ err = 0; if ((tp->tg3_flags & TG3_FLAG_ENABLE_ASF) || diff --git a/drivers/net/wan/farsync.c b/drivers/net/wan/farsync.c index 00945f7c1e9..25c9ef6a181 100644 --- a/drivers/net/wan/farsync.c +++ b/drivers/net/wan/farsync.c @@ -69,7 +69,7 @@ MODULE_LICENSE("GPL"); #endif /* - * Modules parameters and associated varaibles + * Modules parameters and associated variables */ static int fst_txq_low = FST_LOW_WATER_MARK; static int fst_txq_high = FST_HIGH_WATER_MARK; diff --git a/drivers/net/wireless/ipw2x00/ipw2100.c b/drivers/net/wireless/ipw2x00/ipw2100.c index 115b7048750..f4e963ba768 100644 --- a/drivers/net/wireless/ipw2x00/ipw2100.c +++ b/drivers/net/wireless/ipw2x00/ipw2100.c @@ -2362,7 +2362,7 @@ static void ipw2100_corruption_detected(struct ipw2100_priv *priv, int i) i * sizeof(struct ipw2100_status)); #ifdef IPW2100_DEBUG_C3 - /* Halt the fimrware so we can get a good image */ + /* Halt the firmware so we can get a good image */ write_register(priv->net_dev, IPW_REG_RESET_REG, IPW_AUX_HOST_RESET_REG_STOP_MASTER); j = 5; diff --git a/drivers/net/wireless/ipw2x00/ipw2200.c b/drivers/net/wireless/ipw2x00/ipw2200.c index 4a92af1d787..e17a4593e1f 100644 --- a/drivers/net/wireless/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/ipw2x00/ipw2200.c @@ -8844,7 +8844,7 @@ static int ipw_wx_set_mode(struct net_device *dev, #endif /* CONFIG_IPW2200_MONITOR */ /* Free the existing firmware and reset the fw_loaded - * flag so ipw_load() will bring in the new firmawre */ + * flag so ipw_load() will bring in the new firmware */ free_firmware(); priv->ieee->iw_mode = wrqu->mode; diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c index 663dc83be50..3889158b359 100644 --- a/drivers/net/wireless/iwlwifi/iwl-agn.c +++ b/drivers/net/wireless/iwlwifi/iwl-agn.c @@ -1337,7 +1337,7 @@ static int iwl_read_ucode(struct iwl_priv *priv) /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely - * on the API version read from firware header from here on forward */ + * on the API version read from firmware header from here on forward */ if (api_ver < api_min || api_ver > api_max) { IWL_ERR(priv, "Driver unable to support your firmware API. " diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c index a71b08ca7c7..9d5f97dd7c7 100644 --- a/drivers/net/wireless/iwlwifi/iwl3945-base.c +++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c @@ -2562,7 +2562,7 @@ static int iwl3945_read_ucode(struct iwl_priv *priv) /* api_ver should match the api version forming part of the * firmware filename ... but we don't check for that and only rely - * on the API version read from firware header from here on forward */ + * on the API version read from firmware header from here on forward */ if (api_ver < api_min || api_ver > api_max) { IWL_ERR(priv, "Driver unable to support your firmware API. " diff --git a/drivers/net/wireless/libertas/cmd.c b/drivers/net/wireless/libertas/cmd.c index 639dd02d3d3..8c3605cdc64 100644 --- a/drivers/net/wireless/libertas/cmd.c +++ b/drivers/net/wireless/libertas/cmd.c @@ -1649,7 +1649,7 @@ static struct cmd_ctrl_node *lbs_get_cmd_ctrl_node(struct lbs_private *priv) /** * @brief This function executes next command in command - * pending queue. It will put fimware back to PS mode + * pending queue. It will put firmware back to PS mode * if applicable. * * @param priv A pointer to struct lbs_private structure diff --git a/drivers/oprofile/buffer_sync.c b/drivers/oprofile/buffer_sync.c index 2c9aa49e43c..8574622e36a 100644 --- a/drivers/oprofile/buffer_sync.c +++ b/drivers/oprofile/buffer_sync.c @@ -154,6 +154,10 @@ int sync_start(void) { int err; + if (!alloc_cpumask_var(&marked_cpus, GFP_KERNEL)) + return -ENOMEM; + cpumask_clear(marked_cpus); + start_cpu_work(); err = task_handoff_register(&task_free_nb); @@ -179,6 +183,7 @@ out2: task_handoff_unregister(&task_free_nb); out1: end_sync(); + free_cpumask_var(marked_cpus); goto out; } @@ -190,6 +195,7 @@ void sync_stop(void) profile_event_unregister(PROFILE_TASK_EXIT, &task_exit_nb); task_handoff_unregister(&task_free_nb); end_sync(); + free_cpumask_var(marked_cpus); } @@ -565,20 +571,6 @@ void sync_buffer(int cpu) mutex_unlock(&buffer_mutex); } -int __init buffer_sync_init(void) -{ - if (!alloc_cpumask_var(&marked_cpus, GFP_KERNEL)) - return -ENOMEM; - - cpumask_clear(marked_cpus); - return 0; -} - -void buffer_sync_cleanup(void) -{ - free_cpumask_var(marked_cpus); -} - /* The function can be used to add a buffer worth of data directly to * the kernel buffer. The buffer is assumed to be a circular buffer. * Take the entries from index start and end at index end, wrapping diff --git a/drivers/oprofile/buffer_sync.h b/drivers/oprofile/buffer_sync.h index 0ebf5db6267..3110732c183 100644 --- a/drivers/oprofile/buffer_sync.h +++ b/drivers/oprofile/buffer_sync.h @@ -19,8 +19,4 @@ void sync_stop(void); /* sync the given CPU's buffer */ void sync_buffer(int cpu); -/* initialize/destroy the buffer system. */ -int buffer_sync_init(void); -void buffer_sync_cleanup(void); - #endif /* OPROFILE_BUFFER_SYNC_H */ diff --git a/drivers/oprofile/cpu_buffer.c b/drivers/oprofile/cpu_buffer.c index e76d715e434..f0e99d4c066 100644 --- a/drivers/oprofile/cpu_buffer.c +++ b/drivers/oprofile/cpu_buffer.c @@ -161,7 +161,7 @@ struct op_sample { entry->event = ring_buffer_lock_reserve (op_ring_buffer_write, sizeof(struct op_sample) + - size * sizeof(entry->sample->data[0]), &entry->irq_flags); + size * sizeof(entry->sample->data[0])); if (entry->event) entry->sample = ring_buffer_event_data(entry->event); else @@ -178,8 +178,7 @@ struct op_sample int op_cpu_buffer_write_commit(struct op_entry *entry) { - return ring_buffer_unlock_commit(op_ring_buffer_write, entry->event, - entry->irq_flags); + return ring_buffer_unlock_commit(op_ring_buffer_write, entry->event); } struct op_sample *op_cpu_buffer_read_entry(struct op_entry *entry, int cpu) diff --git a/drivers/oprofile/oprof.c b/drivers/oprofile/oprof.c index ced39f60229..3cffce90f82 100644 --- a/drivers/oprofile/oprof.c +++ b/drivers/oprofile/oprof.c @@ -183,10 +183,6 @@ static int __init oprofile_init(void) { int err; - err = buffer_sync_init(); - if (err) - return err; - err = oprofile_arch_init(&oprofile_ops); if (err < 0 || timer) { @@ -195,10 +191,8 @@ static int __init oprofile_init(void) } err = oprofilefs_register(); - if (err) { + if (err) oprofile_arch_exit(); - buffer_sync_cleanup(); - } return err; } @@ -208,7 +202,6 @@ static void __exit oprofile_exit(void) { oprofilefs_unregister(); oprofile_arch_exit(); - buffer_sync_cleanup(); } diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c0cbbb5a245..d76c4c85367 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -277,10 +277,9 @@ static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, node = dev_to_node(&dev->dev); if (node >= 0) { int cpu; - node_to_cpumask_ptr(nodecpumask, node); get_online_cpus(); - cpu = cpumask_any_and(nodecpumask, cpu_online_mask); + cpu = cpumask_any_and(cpumask_of_node(node), cpu_online_mask); if (cpu < nr_cpu_ids) error = work_on_cpu(cpu, local_pci_probe, &ddi); else diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index fe7ac2cea7c..445fb6f7ea3 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -593,7 +593,7 @@ EXPORT_SYMBOL_GPL(__pci_complete_power_transition); * @dev: PCI device to handle. * @state: PCI power state (D0, D1, D2, D3hot) to put the device into. * - * Transition a device to a new power state, using the platform formware and/or + * Transition a device to a new power state, using the platform firmware and/or * the device's PCI PM registers. * * RETURN VALUE: diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 3dad27a385d..d99f1cd435a 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -5811,7 +5811,7 @@ static struct ibm_struct volume_driver_data = { * ThinkPads from this same time period (and earlier) probably lack the * tachometer as well. * - * Unfortunately a lot of ThinkPads with new-style ECs but whose firwmare + * Unfortunately a lot of ThinkPads with new-style ECs but whose firmware * was never fixed by IBM to report the EC firmware version string * probably support the tachometer (like the early X models), so * detecting it is quite hard. We need more data to know for sure. diff --git a/drivers/power/bq27x00_battery.c b/drivers/power/bq27x00_battery.c index 0c056fcc01c..62bb98124e2 100644 --- a/drivers/power/bq27x00_battery.c +++ b/drivers/power/bq27x00_battery.c @@ -83,7 +83,7 @@ static int bq27x00_read(u8 reg, int *rt_value, int b_single, } /* - * Return the battery temperature in Celcius degrees + * Return the battery temperature in Celsius degrees * Or < 0 if something fails. */ static int bq27x00_battery_temperature(struct bq27x00_device_info *di) diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index e2f44e6c0bc..20297c521e5 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -1380,7 +1380,7 @@ config SCSI_LPFC_DEBUG_FS bool "Emulex LightPulse Fibre Channel debugfs Support" depends on SCSI_LPFC && DEBUG_FS help - This makes debugging infomation from the lpfc driver + This makes debugging information from the lpfc driver available via the debugfs filesystem. config SCSI_SIM710 @@ -1388,7 +1388,7 @@ config SCSI_SIM710 depends on (EISA || MCA) && SCSI select SCSI_SPI_ATTRS ---help--- - This driver for NCR53c710 based SCSI host adapters. + This driver is for NCR53c710 based SCSI host adapters. It currently supports Compaq EISA cards and NCR MCA cards diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 9be11b0963f..aa9d3a4c2d5 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig @@ -1374,7 +1374,7 @@ config SERIAL_BFIN_SPORT depends on BLACKFIN && EXPERIMENTAL select SERIAL_CORE help - Enble support SPORT emulate UART on Blackfin series. + Enable SPORT emulate UART on Blackfin series. To compile this driver as a module, choose M here: the module will be called bfin_sport_uart. diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 7ea9d918387..0dcf9ca0b0a 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -4,7 +4,7 @@ menuconfig STAGING ---help--- This option allows you to select a number of drivers that are not of the "normal" Linux kernel quality level. These drivers - are placed here in order to get a wider audience for use of + are placed here in order to get a wider audience to make use of them. Please note that these drivers are under heavy development, may or may not work, and may contain userspace interfaces that most likely will be changed in the near @@ -12,7 +12,7 @@ menuconfig STAGING Using any of these drivers will taint your kernel which might affect support options from both the community, and various - commercial support orginizations. + commercial support organizations. If you wish to work on these drivers, to help improve them, or to report problems you have with them, please see the diff --git a/drivers/staging/comedi/Kconfig b/drivers/staging/comedi/Kconfig index 6365a2023d5..2d819d278b0 100644 --- a/drivers/staging/comedi/Kconfig +++ b/drivers/staging/comedi/Kconfig @@ -1,9 +1,9 @@ config COMEDI - tristate "Data Acquision support (comedi)" + tristate "Data acquisition support (comedi)" default N depends on m ---help--- - Enable support a wide range of data acquision devices + Enable support a wide range of data acquisition devices for Linux. config COMEDI_DEBUG diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/go7007/Kconfig index f2cf7f66ae0..ca6ade6c4b4 100644 --- a/drivers/staging/go7007/Kconfig +++ b/drivers/staging/go7007/Kconfig @@ -10,7 +10,7 @@ config VIDEO_GO7007 select CRC32 default N ---help--- - This is a video4linux driver for some wierd device... + This is a video4linux driver for some weird device... To compile this driver as a module, choose M here: the module will be called go7007 @@ -20,7 +20,7 @@ config VIDEO_GO7007_USB depends on VIDEO_GO7007 && USB default N ---help--- - This is a video4linux driver for some wierd device... + This is a video4linux driver for some weird device... To compile this driver as a module, choose M here: the module will be called go7007-usb diff --git a/drivers/staging/otus/hal/hpmain.c b/drivers/staging/otus/hal/hpmain.c index 72c9e89979c..322585be2c8 100644 --- a/drivers/staging/otus/hal/hpmain.c +++ b/drivers/staging/otus/hal/hpmain.c @@ -152,7 +152,7 @@ u16_t zfHpInit(zdev_t* dev, u32_t frequency) else { #ifndef ZM_OTUS_LINUX_PHASE_2 - /* donwload the normal frimware */ + /* download the normal firmware */ if ((ret = zfFirmwareDownload(dev, (u32_t*)zcFwImage, (u32_t)zcFwImageSize, ZM_FIRMWARE_WLAN_ADDR)) != ZM_SUCCESS) { diff --git a/drivers/staging/panel/Kconfig b/drivers/staging/panel/Kconfig index c4b30f2a549..3abe7c9d558 100644 --- a/drivers/staging/panel/Kconfig +++ b/drivers/staging/panel/Kconfig @@ -110,7 +110,7 @@ config PANEL_LCD_BWIDTH ---help--- Most LCDs use a standard controller which supports hardware lines of 40 characters, although sometimes only 16, 20 or 24 of them are really wired - to the terminal. This results in some non-visible but adressable characters, + to the terminal. This results in some non-visible but addressable characters, and is the case for most parallel LCDs. Other LCDs, and some serial ones, however, use the same line width internally as what is visible. The KS0074 for example, uses 16 characters per line for 16 visible characters per line. diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index b6483dd98ac..9cf9ff69e3e 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -626,7 +626,7 @@ static void uea_upload_pre_firmware(const struct firmware *fw_entry, void *conte goto err_fw_corrupted; /* - * Start to upload formware : send reset + * Start to upload firmware : send reset */ value = 1; ret = uea_send_modem_cmd(usb, F8051_USBCS, sizeof(value), &value); diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 770b3eaa918..080bb1e4b84 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -392,7 +392,7 @@ config USB_GADGET_FSL_QE controllers having QE or CPM2, given minor tweaks. Set CONFIG_USB_GADGET to "m" to build this driver as a - dynmically linked module called "fsl_qe_udc". + dynamically linked module called "fsl_qe_udc". config USB_FSL_QE tristate diff --git a/drivers/usb/serial/ChangeLog.history b/drivers/usb/serial/ChangeLog.history index c1b279939bb..f13fd488ebe 100644 --- a/drivers/usb/serial/ChangeLog.history +++ b/drivers/usb/serial/ChangeLog.history @@ -715,7 +715,7 @@ io_edgeport.c Change Log comments: 0.2 (01/30/2000) greg kroah-hartman Milestone 1 release. - Device is found by USB subsystem, enumerated, fimware is downloaded + Device is found by USB subsystem, enumerated, firmware is downloaded and the descriptors are printed to the debug log, config is set, and green light starts to blink. Open port works, and data can be sent and received at the default settings of the UART. Loopback connector diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig index a65f9196b0a..c480ea4c19f 100644 --- a/drivers/usb/serial/Kconfig +++ b/drivers/usb/serial/Kconfig @@ -518,8 +518,8 @@ config USB_SERIAL_SIERRAWIRELESS help Say M here if you want to use Sierra Wireless devices. - Many deviecs have a feature known as TRU-Install, for those devices - to work properly the USB Storage Sierra feature must be enabled. + Many devices have a feature known as TRU-Install. For those devices + to work properly, the USB Storage Sierra feature must be enabled. To compile this driver as a module, choose M here: the module will be called sierra. diff --git a/drivers/usb/wusbcore/security.c b/drivers/usb/wusbcore/security.c index 8118db7f1d8..b2f149fedcc 100644 --- a/drivers/usb/wusbcore/security.c +++ b/drivers/usb/wusbcore/security.c @@ -562,7 +562,7 @@ void wusbhc_gtk_rekey(struct wusbhc *wusbhc) struct wusb_dev *wusb_dev; wusb_dev = wusbhc->port[p].wusb_dev; - if (!wusb_dev || !wusb_dev->usb_dev | !wusb_dev->usb_dev->authenticated) + if (!wusb_dev || !wusb_dev->usb_dev || !wusb_dev->usb_dev->authenticated) continue; usb_fill_control_urb(wusb_dev->set_gtk_urb, wusb_dev->usb_dev, diff --git a/drivers/uwb/Kconfig b/drivers/uwb/Kconfig index ca783127af3..bac8e7a6f17 100644 --- a/drivers/uwb/Kconfig +++ b/drivers/uwb/Kconfig @@ -48,10 +48,10 @@ config UWB_WHCI help This driver enables the radio controller for WHCI cards. - WHCI is an specification developed by Intel + WHCI is a specification developed by Intel (http://www.intel.com/technology/comms/wusb/whci.htm) much in the spirit of USB's EHCI, but for UWB and Wireless USB - radio/host controllers connected via memmory mapping (eg: + radio/host controllers connected via memory mapping (eg: PCI). Most of these cards come also with a Wireless USB host controller. diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index 526187c8a12..8ac9cddac57 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -37,7 +37,7 @@ config XEN_COMPAT_XENFS The old xenstore userspace tools expect to find "xenbus" under /proc/xen, but "xenbus" is now found at the root of the xenfs filesystem. Selecting this causes the kernel to create - the compatibilty mount point /proc/xen if it is running on + the compatibility mount point /proc/xen if it is running on a xen platform. If in doubt, say yes. diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c index 974f56d1ebe..5f54c01c156 100644 --- a/drivers/xen/cpu_hotplug.c +++ b/drivers/xen/cpu_hotplug.c @@ -10,7 +10,7 @@ static void enable_hotplug_cpu(int cpu) if (!cpu_present(cpu)) arch_register_cpu(cpu); - cpu_set(cpu, cpu_present_map); + set_cpu_present(cpu, true); } static void disable_hotplug_cpu(int cpu) @@ -18,7 +18,7 @@ static void disable_hotplug_cpu(int cpu) if (cpu_present(cpu)) arch_unregister_cpu(cpu); - cpu_clear(cpu, cpu_present_map); + set_cpu_present(cpu, false); } static void vcpu_hotplug(unsigned int cpu) |