diff options
Diffstat (limited to 'drivers')
209 files changed, 7841 insertions, 2041 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index c205653e9644..ab686b310100 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -31,10 +31,14 @@ menuconfig ACPI ACPI CA, see: <http://acpica.org/> - ACPI is an open industry specification co-developed by - Hewlett-Packard, Intel, Microsoft, Phoenix, and Toshiba. + ACPI is an open industry specification originally co-developed by + Hewlett-Packard, Intel, Microsoft, Phoenix, and Toshiba. Currently, + it is developed by the ACPI Specification Working Group (ASWG) under + the UEFI Forum and any UEFI member can join the ASWG and contribute + to the ACPI specification. The specification is available at: <http://www.acpi.info> + <http://www.uefi.org/acpi/specs> if ACPI diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index f0fc6260266b..d9339b442a4e 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -51,12 +51,6 @@ MODULE_PARM_DESC(immediate_undock, "1 (default) will cause the driver to " " the driver to wait for userspace to write the undock sysfs file " " before undocking"); -static const struct acpi_device_id dock_device_ids[] = { - {"LNXDOCK", 0}, - {"", 0}, -}; -MODULE_DEVICE_TABLE(acpi, dock_device_ids); - struct dock_station { acpi_handle handle; unsigned long last_dock_time; diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index f7fd72ac69cf..6776c599816f 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -1219,10 +1219,9 @@ acpi_os_create_semaphore(u32 max_units, u32 initial_units, acpi_handle * handle) { struct semaphore *sem = NULL; - sem = acpi_os_allocate(sizeof(struct semaphore)); + sem = acpi_os_allocate_zeroed(sizeof(struct semaphore)); if (!sem) return AE_NO_MEMORY; - memset(sem, 0, sizeof(struct semaphore)); sema_init(sem, initial_units); diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 964068553334..c1e31a41f949 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -344,7 +344,7 @@ static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) tz->trips.hot.flags.valid = 1; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", - tz->trips.critical.temperature)); + tz->trips.hot.temperature)); } } diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c index 0f5f78fa6545..bba526148583 100644 --- a/drivers/acpi/utils.c +++ b/drivers/acpi/utils.c @@ -164,11 +164,10 @@ acpi_extract_package(union acpi_object *package, * Validate output buffer. */ if (buffer->length == ACPI_ALLOCATE_BUFFER) { - buffer->pointer = ACPI_ALLOCATE(size_required); + buffer->pointer = ACPI_ALLOCATE_ZEROED(size_required); if (!buffer->pointer) return AE_NO_MEMORY; buffer->length = size_required; - memset(buffer->pointer, 0, size_required); } else { if (buffer->length < size_required) { buffer->length = size_required; diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 48c7e8af9c96..8b6990e417ec 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -488,6 +488,14 @@ static struct dmi_system_id video_dmi_table[] __initdata = { }, }, { + .callback = video_set_use_native_backlight, + .ident = "Thinkpad Helix", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Helix"), + }, + }, + { .callback = video_set_use_native_backlight, .ident = "Dell Inspiron 7520", .matches = { diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 6f54962aae1d..ae098a261fcd 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -705,6 +705,14 @@ static int pm_genpd_runtime_resume(struct device *dev) return 0; } +static bool pd_ignore_unused; +static int __init pd_ignore_unused_setup(char *__unused) +{ + pd_ignore_unused = true; + return 1; +} +__setup("pd_ignore_unused", pd_ignore_unused_setup); + /** * pm_genpd_poweroff_unused - Power off all PM domains with no devices in use. */ @@ -712,6 +720,11 @@ void pm_genpd_poweroff_unused(void) { struct generic_pm_domain *genpd; + if (pd_ignore_unused) { + pr_warn("genpd: Not disabling unused power domains\n"); + return; + } + mutex_lock(&gpd_list_lock); list_for_each_entry(genpd, &gpd_list, gpd_list_node) diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c index d0a072463a04..63e30ef096e2 100644 --- a/drivers/base/regmap/regmap.c +++ b/drivers/base/regmap/regmap.c @@ -761,10 +761,11 @@ skip_format_initialization: if (ret != 0) goto err_range; - if (dev) + if (dev) { ret = regmap_attach_dev(dev, map, config); if (ret != 0) goto err_regcache; + } return map; diff --git a/drivers/block/drbd/drbd_receiver.c b/drivers/block/drbd/drbd_receiver.c index 18c76e84d540..68e3992e8838 100644 --- a/drivers/block/drbd/drbd_receiver.c +++ b/drivers/block/drbd/drbd_receiver.c @@ -469,24 +469,14 @@ static void drbd_wait_ee_list_empty(struct drbd_device *device, static int drbd_recv_short(struct socket *sock, void *buf, size_t size, int flags) { - mm_segment_t oldfs; struct kvec iov = { .iov_base = buf, .iov_len = size, }; struct msghdr msg = { - .msg_iovlen = 1, - .msg_iov = (struct iovec *)&iov, .msg_flags = (flags ? flags : MSG_WAITALL | MSG_NOSIGNAL) }; - int rv; - - oldfs = get_fs(); - set_fs(KERNEL_DS); - rv = sock_recvmsg(sock, &msg, size, msg.msg_flags); - set_fs(oldfs); - - return rv; + return kernel_recvmsg(sock, &msg, &iov, 1, size, msg.msg_flags); } static int drbd_recv(struct drbd_connection *connection, void *buf, size_t size) diff --git a/drivers/block/loop.c b/drivers/block/loop.c index 66e8c3b94ef3..f70a230a2945 100644 --- a/drivers/block/loop.c +++ b/drivers/block/loop.c @@ -237,7 +237,7 @@ static int __do_lo_send_write(struct file *file, file_end_write(file); if (likely(bw == len)) return 0; - printk(KERN_ERR "loop: Write error at byte offset %llu, length %i.\n", + printk_ratelimited(KERN_ERR "loop: Write error at byte offset %llu, length %i.\n", (unsigned long long)pos, len); if (bw >= 0) bw = -EIO; @@ -277,7 +277,7 @@ static int do_lo_send_write(struct loop_device *lo, struct bio_vec *bvec, return __do_lo_send_write(lo->lo_backing_file, page_address(page), bvec->bv_len, pos); - printk(KERN_ERR "loop: Transfer error at byte offset %llu, " + printk_ratelimited(KERN_ERR "loop: Transfer error at byte offset %llu, " "length %i.\n", (unsigned long long)pos, bvec->bv_len); if (ret > 0) ret = -EIO; @@ -316,7 +316,7 @@ static int lo_send(struct loop_device *lo, struct bio *bio, loff_t pos) out: return ret; fail: - printk(KERN_ERR "loop: Failed to allocate temporary page for write.\n"); + printk_ratelimited(KERN_ERR "loop: Failed to allocate temporary page for write.\n"); ret = -ENOMEM; goto out; } @@ -345,7 +345,7 @@ lo_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, size = p->bsize; if (lo_do_transfer(lo, READ, page, buf->offset, p->page, p->offset, size, IV)) { - printk(KERN_ERR "loop: transfer error block %ld\n", + printk_ratelimited(KERN_ERR "loop: transfer error block %ld\n", page->index); size = -EINVAL; } diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index 55298db36b2d..3a70ea2f7cd6 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -630,37 +630,29 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, } case NBD_CLEAR_SOCK: { - struct file *file; - + struct socket *sock = nbd->sock; nbd->sock = NULL; - file = nbd->file; - nbd->file = NULL; nbd_clear_que(nbd); BUG_ON(!list_empty(&nbd->queue_head)); BUG_ON(!list_empty(&nbd->waiting_queue)); kill_bdev(bdev); - if (file) - fput(file); + if (sock) + sockfd_put(sock); return 0; } case NBD_SET_SOCK: { - struct file *file; - if (nbd->file) + struct socket *sock; + int err; + if (nbd->sock) return -EBUSY; - file = fget(arg); - if (file) { - struct inode *inode = file_inode(file); - if (S_ISSOCK(inode->i_mode)) { - nbd->file = file; - nbd->sock = SOCKET_I(inode); - if (max_part > 0) - bdev->bd_invalidated = 1; - nbd->disconnect = 0; /* we're connected now */ - return 0; - } else { - fput(file); - } + sock = sockfd_lookup(arg, &err); + if (sock) { + nbd->sock = sock; + if (max_part > 0) + bdev->bd_invalidated = 1; + nbd->disconnect = 0; /* we're connected now */ + return 0; } return -EINVAL; } @@ -697,12 +689,12 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, case NBD_DO_IT: { struct task_struct *thread; - struct file *file; + struct socket *sock; int error; if (nbd->pid) return -EBUSY; - if (!nbd->file) + if (!nbd->sock) return -EINVAL; mutex_unlock(&nbd->tx_lock); @@ -731,15 +723,15 @@ static int __nbd_ioctl(struct block_device *bdev, struct nbd_device *nbd, if (error) return error; sock_shutdown(nbd, 0); - file = nbd->file; - nbd->file = NULL; + sock = nbd->sock; + nbd->sock = NULL; nbd_clear_que(nbd); dev_warn(disk_to_dev(nbd->disk), "queue cleared\n"); kill_bdev(bdev); queue_flag_clear_unlocked(QUEUE_FLAG_DISCARD, nbd->disk->queue); set_device_ro(bdev, false); - if (file) - fput(file); + if (sock) + sockfd_put(sock); nbd->flags = 0; nbd->bytesize = 0; bdev->bd_inode->i_size = 0; @@ -875,9 +867,7 @@ static int __init nbd_init(void) for (i = 0; i < nbds_max; i++) { struct gendisk *disk = nbd_dev[i].disk; - nbd_dev[i].file = NULL; nbd_dev[i].magic = NBD_MAGIC; - nbd_dev[i].flags = 0; INIT_LIST_HEAD(&nbd_dev[i].waiting_queue); spin_lock_init(&nbd_dev[i].queue_lock); INIT_LIST_HEAD(&nbd_dev[i].queue_head); diff --git a/drivers/block/nvme-core.c b/drivers/block/nvme-core.c index da085ff10d25..7c64fa756cce 100644 --- a/drivers/block/nvme-core.c +++ b/drivers/block/nvme-core.c @@ -1,6 +1,6 @@ /* * NVM Express device driver - * Copyright (c) 2011, Intel Corporation. + * Copyright (c) 2011-2014, Intel Corporation. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -20,10 +20,12 @@ #include <linux/bio.h> #include <linux/bitops.h> #include <linux/blkdev.h> +#include <linux/cpu.h> #include <linux/delay.h> #include <linux/errno.h> #include <linux/fs.h> #include <linux/genhd.h> +#include <linux/hdreg.h> #include <linux/idr.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -35,6 +37,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/pci.h> +#include <linux/percpu.h> #include <linux/poison.h> #include <linux/ptrace.h> #include <linux/sched.h> @@ -47,6 +50,11 @@ #define SQ_SIZE(depth) (depth * sizeof(struct nvme_command)) #define CQ_SIZE(depth) (depth * sizeof(struct nvme_completion)) #define ADMIN_TIMEOUT (60 * HZ) +#define IOD_TIMEOUT (4 * NVME_IO_TIMEOUT) + +unsigned char io_timeout = 30; +module_param(io_timeout, byte, 0644); +MODULE_PARM_DESC(io_timeout, "timeout in seconds for I/O"); static int nvme_major; module_param(nvme_major, int, 0); @@ -58,6 +66,7 @@ static DEFINE_SPINLOCK(dev_list_lock); static LIST_HEAD(dev_list); static struct task_struct *nvme_thread; static struct workqueue_struct *nvme_workq; +static wait_queue_head_t nvme_kthread_wait; static void nvme_reset_failed_dev(struct work_struct *ws); @@ -74,6 +83,7 @@ struct async_cmd_info { * commands and one for I/O commands). */ struct nvme_queue { + struct rcu_head r_head; struct device *q_dmadev; struct nvme_dev *dev; char irqname[24]; /* nvme4294967295-65535\0 */ @@ -85,6 +95,7 @@ struct nvme_queue { wait_queue_head_t sq_full; wait_queue_t sq_cong_wait; struct bio_list sq_cong; + struct list_head iod_bio; u32 __iomem *q_db; u16 q_depth; u16 cq_vector; @@ -95,6 +106,7 @@ struct nvme_queue { u8 cq_phase; u8 cqe_seen; u8 q_suspended; + cpumask_var_t cpu_mask; struct async_cmd_info cmdinfo; unsigned long cmdid_data[]; }; @@ -118,7 +130,7 @@ static inline void _nvme_check_size(void) BUILD_BUG_ON(sizeof(struct nvme_smart_log) != 512); } -typedef void (*nvme_completion_fn)(struct nvme_dev *, void *, +typedef void (*nvme_completion_fn)(struct nvme_queue *, void *, struct nvme_completion *); struct nvme_cmd_info { @@ -190,7 +202,7 @@ static int alloc_cmdid_killable(struct nvme_queue *nvmeq, void *ctx, #define CMD_CTX_FLUSH (0x318 + CMD_CTX_BASE) #define CMD_CTX_ABORT (0x31C + CMD_CTX_BASE) -static void special_completion(struct nvme_dev *dev, void *ctx, +static void special_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { if (ctx == CMD_CTX_CANCELLED) @@ -198,26 +210,26 @@ static void special_completion(struct nvme_dev *dev, void *ctx, if (ctx == CMD_CTX_FLUSH) return; if (ctx == CMD_CTX_ABORT) { - ++dev->abort_limit; + ++nvmeq->dev->abort_limit; return; } if (ctx == CMD_CTX_COMPLETED) { - dev_warn(&dev->pci_dev->dev, + dev_warn(nvmeq->q_dmadev, "completed id %d twice on queue %d\n", cqe->command_id, le16_to_cpup(&cqe->sq_id)); return; } if (ctx == CMD_CTX_INVALID) { - dev_warn(&dev->pci_dev->dev, + dev_warn(nvmeq->q_dmadev, "invalid id %d completed on queue %d\n", cqe->command_id, le16_to_cpup(&cqe->sq_id)); return; } - dev_warn(&dev->pci_dev->dev, "Unknown special completion %p\n", ctx); + dev_warn(nvmeq->q_dmadev, "Unknown special completion %p\n", ctx); } -static void async_completion(struct nvme_dev *dev, void *ctx, +static void async_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { struct async_cmd_info *cmdinfo = ctx; @@ -262,14 +274,34 @@ static void *cancel_cmdid(struct nvme_queue *nvmeq, int cmdid, return ctx; } -struct nvme_queue *get_nvmeq(struct nvme_dev *dev) +static struct nvme_queue *raw_nvmeq(struct nvme_dev *dev, int qid) +{ + return rcu_dereference_raw(dev->queues[qid]); +} + +static struct nvme_queue *get_nvmeq(struct nvme_dev *dev) __acquires(RCU) +{ + unsigned queue_id = get_cpu_var(*dev->io_queue); + rcu_read_lock(); + return rcu_dereference(dev->queues[queue_id]); +} + +static void put_nvmeq(struct nvme_queue *nvmeq) __releases(RCU) { - return dev->queues[get_cpu() + 1]; + rcu_read_unlock(); + put_cpu_var(nvmeq->dev->io_queue); } -void put_nvmeq(struct nvme_queue *nvmeq) +static struct nvme_queue *lock_nvmeq(struct nvme_dev *dev, int q_idx) + __acquires(RCU) { - put_cpu(); + rcu_read_lock(); + return rcu_dereference(dev->queues[q_idx]); +} + +static void unlock_nvmeq(struct nvme_queue *nvmeq) __releases(RCU) +{ + rcu_read_unlock(); } /** @@ -284,6 +316,10 @@ static int nvme_submit_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd) unsigned long flags; u16 tail; spin_lock_irqsave(&nvmeq->q_lock, flags); + if (nvmeq->q_suspended) { + spin_unlock_irqrestore(&nvmeq->q_lock, flags); + return -EBUSY; + } tail = nvmeq->sq_tail; memcpy(&nvmeq->sq_cmds[tail], cmd, sizeof(*cmd)); if (++tail == nvmeq->q_depth) @@ -323,6 +359,7 @@ nvme_alloc_iod(unsigned nseg, unsigned nbytes, gfp_t gfp) iod->npages = -1; iod->length = nbytes; iod->nents = 0; + iod->first_dma = 0ULL; iod->start_time = jiffies; } @@ -371,19 +408,31 @@ static void nvme_end_io_acct(struct bio *bio, unsigned long start_time) part_stat_unlock(); } -static void bio_completion(struct nvme_dev *dev, void *ctx, +static void bio_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { struct nvme_iod *iod = ctx; struct bio *bio = iod->private; u16 status = le16_to_cpup(&cqe->status) >> 1; + if (unlikely(status)) { + if (!(status & NVME_SC_DNR || + bio->bi_rw & REQ_FAILFAST_MASK) && + (jiffies - iod->start_time) < IOD_TIMEOUT) { + if (!waitqueue_active(&nvmeq->sq_full)) + add_wait_queue(&nvmeq->sq_full, + &nvmeq->sq_cong_wait); + list_add_tail(&iod->node, &nvmeq->iod_bio); + wake_up(&nvmeq->sq_full); + return; + } + } if (iod->nents) { - dma_unmap_sg(&dev->pci_dev->dev, iod->sg, iod->nents, + dma_unmap_sg(nvmeq->q_dmadev, iod->sg, iod->nents, bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); nvme_end_io_acct(bio, iod->start_time); } - nvme_free_iod(dev, iod); + nvme_free_iod(nvmeq->dev, iod); if (status) bio_endio(bio, -EIO); else @@ -391,8 +440,8 @@ static void bio_completion(struct nvme_dev *dev, void *ctx, } /* length is in bytes. gfp flags indicates whether we may sleep. */ -int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd, - struct nvme_iod *iod, int total_len, gfp_t gfp) +int nvme_setup_prps(struct nvme_dev *dev, struct nvme_iod *iod, int total_len, + gfp_t gfp) { struct dma_pool *pool; int length = total_len; @@ -405,7 +454,6 @@ int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd, dma_addr_t prp_dma; int nprps, i; - cmd->prp1 = cpu_to_le64(dma_addr); length -= (PAGE_SIZE - offset); if (length <= 0) return total_len; @@ -420,7 +468,7 @@ int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd, } if (length <= PAGE_SIZE) { - cmd->prp2 = cpu_to_le64(dma_addr); + iod->first_dma = dma_addr; return total_len; } @@ -435,13 +483,12 @@ int nvme_setup_prps(struct nvme_dev *dev, struct nvme_common_command *cmd, prp_list = dma_pool_alloc(pool, gfp, &prp_dma); if (!prp_list) { - cmd->prp2 = cpu_to_le64(dma_addr); + iod->first_dma = dma_addr; iod->npages = -1; return (total_len - length) + PAGE_SIZE; } list[0] = prp_list; iod->first_dma = prp_dma; - cmd->prp2 = cpu_to_le64(prp_dma); i = 0; for (;;) { if (i == PAGE_SIZE / 8) { @@ -480,10 +527,11 @@ static int nvme_split_and_submit(struct bio *bio, struct nvme_queue *nvmeq, bio_chain(split, bio); - if (bio_list_empty(&nvmeq->sq_cong)) + if (!waitqueue_active(&nvmeq->sq_full)) add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); bio_list_add(&nvmeq->sq_cong, split); bio_list_add(&nvmeq->sq_cong, bio); + wake_up(&nvmeq->sq_full); return 0; } @@ -536,25 +584,13 @@ static int nvme_map_bio(struct nvme_queue *nvmeq, struct nvme_iod *iod, return length; } -/* - * We reuse the small pool to allocate the 16-byte range here as it is not - * worth having a special pool for these or additional cases to handle freeing - * the iod. - */ static int nvme_submit_discard(struct nvme_queue *nvmeq, struct nvme_ns *ns, struct bio *bio, struct nvme_iod *iod, int cmdid) { - struct nvme_dsm_range *range; + struct nvme_dsm_range *range = + (struct nvme_dsm_range *)iod_list(iod)[0]; struct nvme_command *cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail]; - range = dma_pool_alloc(nvmeq->dev->prp_small_pool, GFP_ATOMIC, - &iod->first_dma); - if (!range) - return -ENOMEM; - - iod_list(iod)[0] = (__le64 *)range; - iod->npages = 0; - range->cattr = cpu_to_le32(0); range->nlb = cpu_to_le32(bio->bi_iter.bi_size >> ns->lba_shift); range->slba = cpu_to_le64(nvme_block_nr(ns, bio->bi_iter.bi_sector)); @@ -601,44 +637,22 @@ int nvme_submit_flush_data(struct nvme_queue *nvmeq, struct nvme_ns *ns) return nvme_submit_flush(nvmeq, ns, cmdid); } -/* - * Called with local interrupts disabled and the q_lock held. May not sleep. - */ -static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, - struct bio *bio) +static int nvme_submit_iod(struct nvme_queue *nvmeq, struct nvme_iod *iod) { + struct bio *bio = iod->private; + struct nvme_ns *ns = bio->bi_bdev->bd_disk->private_data; struct nvme_command *cmnd; - struct nvme_iod *iod; - enum dma_data_direction dma_dir; - int cmdid, length, result; + int cmdid; u16 control; u32 dsmgmt; - int psegs = bio_phys_segments(ns->queue, bio); - - if ((bio->bi_rw & REQ_FLUSH) && psegs) { - result = nvme_submit_flush_data(nvmeq, ns); - if (result) - return result; - } - result = -ENOMEM; - iod = nvme_alloc_iod(psegs, bio->bi_iter.bi_size, GFP_ATOMIC); - if (!iod) - goto nomem; - iod->private = bio; - - result = -EBUSY; cmdid = alloc_cmdid(nvmeq, iod, bio_completion, NVME_IO_TIMEOUT); if (unlikely(cmdid < 0)) - goto free_iod; + return cmdid; - if (bio->bi_rw & REQ_DISCARD) { - result = nvme_submit_discard(nvmeq, ns, bio, iod, cmdid); - if (result) - goto free_cmdid; - return result; - } - if ((bio->bi_rw & REQ_FLUSH) && !psegs) + if (bio->bi_rw & REQ_DISCARD) + return nvme_submit_discard(nvmeq, ns, bio, iod, cmdid); + if ((bio->bi_rw & REQ_FLUSH) && !iod->nents) return nvme_submit_flush(nvmeq, ns, cmdid); control = 0; @@ -652,42 +666,85 @@ static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, dsmgmt |= NVME_RW_DSM_FREQ_PREFETCH; cmnd = &nvmeq->sq_cmds[nvmeq->sq_tail]; - memset(cmnd, 0, sizeof(*cmnd)); - if (bio_data_dir(bio)) { - cmnd->rw.opcode = nvme_cmd_write; - dma_dir = DMA_TO_DEVICE; - } else { - cmnd->rw.opcode = nvme_cmd_read; - dma_dir = DMA_FROM_DEVICE; - } - - result = nvme_map_bio(nvmeq, iod, bio, dma_dir, psegs); - if (result <= 0) - goto free_cmdid; - length = result; + cmnd->rw.opcode = bio_data_dir(bio) ? nvme_cmd_write : nvme_cmd_read; cmnd->rw.command_id = cmdid; cmnd->rw.nsid = cpu_to_le32(ns->ns_id); - length = nvme_setup_prps(nvmeq->dev, &cmnd->common, iod, length, - GFP_ATOMIC); + cmnd->rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg)); + cmnd->rw.prp2 = cpu_to_le64(iod->first_dma); cmnd->rw.slba = cpu_to_le64(nvme_block_nr(ns, bio->bi_iter.bi_sector)); - cmnd->rw.length = cpu_to_le16((length >> ns->lba_shift) - 1); + cmnd->rw.length = + cpu_to_le16((bio->bi_iter.bi_size >> ns->lba_shift) - 1); cmnd->rw.control = cpu_to_le16(control); cmnd->rw.dsmgmt = cpu_to_le32(dsmgmt); - nvme_start_io_acct(bio); if (++nvmeq->sq_tail == nvmeq->q_depth) nvmeq->sq_tail = 0; writel(nvmeq->sq_tail, nvmeq->q_db); return 0; +} + +/* + * Called with local interrupts disabled and the q_lock held. May not sleep. + */ +static int nvme_submit_bio_queue(struct nvme_queue *nvmeq, struct nvme_ns *ns, + struct bio *bio) +{ + struct nvme_iod *iod; + int psegs = bio_phys_segments(ns->queue, bio); + int result; + + if ((bio->bi_rw & REQ_FLUSH) && psegs) { + result = nvme_submit_flush_data(nvmeq, ns); + if (result) + return result; + } + + iod = nvme_alloc_iod(psegs, bio->bi_iter.bi_size, GFP_ATOMIC); + if (!iod) + return -ENOMEM; + + iod->private = bio; + if (bio->bi_rw & REQ_DISCARD) { + void *range; + /* + * We reuse the small pool to allocate the 16-byte range here + * as it is not worth having a special pool for these or + * additional cases to handle freeing the iod. + */ + range = dma_pool_alloc(nvmeq->dev->prp_small_pool, + GFP_ATOMIC, + &iod->first_dma); + if (!range) { + result = -ENOMEM; + goto free_iod; + } + iod_list(iod)[0] = (__le64 *)range; + iod->npages = 0; + } else if (psegs) { + result = nvme_map_bio(nvmeq, iod, bio, + bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE, + psegs); + if (result <= 0) + goto free_iod; + if (nvme_setup_prps(nvmeq->dev, iod, result, GFP_ATOMIC) != + result) { + result = -ENOMEM; + goto free_iod; + } + nvme_start_io_acct(bio); + } + if (unlikely(nvme_submit_iod(nvmeq, iod))) { + if (!waitqueue_active(&nvmeq->sq_full)) + add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); + list_add_tail(&iod->node, &nvmeq->iod_bio); + } + return 0; - free_cmdid: - free_cmdid(nvmeq, cmdid, NULL); free_iod: nvme_free_iod(nvmeq->dev, iod); - nomem: return result; } @@ -711,7 +768,7 @@ static int nvme_process_cq(struct nvme_queue *nvmeq) } ctx = free_cmdid(nvmeq, cqe.command_id, &fn); - fn(nvmeq->dev, ctx, &cqe); + fn(nvmeq, ctx, &cqe); } /* If the controller ignores the cq head doorbell and continuously @@ -747,7 +804,7 @@ static void nvme_make_request(struct request_queue *q, struct bio *bio) if (!nvmeq->q_suspended && bio_list_empty(&nvmeq->sq_cong)) result = nvme_submit_bio_queue(nvmeq, ns, bio); if (unlikely(result)) { - if (bio_list_empty(&nvmeq->sq_cong)) + if (!waitqueue_active(&nvmeq->sq_full)) add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); bio_list_add(&nvmeq->sq_cong, bio); } @@ -791,7 +848,7 @@ struct sync_cmd_info { int status; }; -static void sync_completion(struct nvme_dev *dev, void *ctx, +static void sync_completion(struct nvme_queue *nvmeq, void *ctx, struct nvme_completion *cqe) { struct sync_cmd_info *cmdinfo = ctx; @@ -804,27 +861,46 @@ static void sync_completion(struct nvme_dev *dev, void *ctx, * Returns 0 on success. If the result is negative, it's a Linux error code; * if the result is positive, it's an NVM Express status code */ -int nvme_submit_sync_cmd(struct nvme_queue *nvmeq, struct nvme_command *cmd, +static int nvme_submit_sync_cmd(struct nvme_dev *dev, int q_idx, + struct nvme_command *cmd, u32 *result, unsigned timeout) { - int cmdid; + int cmdid, ret; struct sync_cmd_info cmdinfo; + struct nvme_queue *nvmeq; + + nvmeq = lock_nvmeq(dev, q_idx); + if (!nvmeq) { + unlock_nvmeq(nvmeq); + return -ENODEV; + } cmdinfo.task = current; cmdinfo.status = -EINTR; - cmdid = alloc_cmdid_killable(nvmeq, &cmdinfo, sync_completion, - timeout); - if (cmdid < 0) + cmdid = alloc_cmdid(nvmeq, &cmdinfo, sync_completion, timeout); + if (cmdid < 0) { + unlock_nvmeq(nvmeq); return cmdid; + } cmd->common.command_id = cmdid; set_current_state(TASK_KILLABLE); - nvme_submit_cmd(nvmeq, cmd); + ret = nvme_submit_cmd(nvmeq, cmd); + if (ret) { + free_cmdid(nvmeq, cmdid, NULL); + unlock_nvmeq(nvmeq); + set_current_state(TASK_RUNNING); + return ret; + } + unlock_nvmeq(nvmeq); schedule_timeout(timeout); if (cmdinfo.status == -EINTR) { - nvme_abort_command(nvmeq, cmdid); + nvmeq = lock_nvmeq(dev, q_idx); + if (nvmeq) + nvme_abort_command(nvmeq, cmdid); + unlock_nvmeq(nvmeq); return -EINTR; } @@ -845,20 +921,26 @@ static int nvme_submit_async_cmd(struct nvme_queue *nvmeq, return cmdid; cmdinfo->status = -EINTR; cmd->common.command_id = cmdid; - nvme_submit_cmd(nvmeq, cmd); - return 0; + return nvme_submit_cmd(nvmeq, cmd); } int nvme_submit_admin_cmd(struct nvme_dev *dev, struct nvme_command *cmd, u32 *result) { - return nvme_submit_sync_cmd(dev->queues[0], cmd, result, ADMIN_TIMEOUT); + return nvme_submit_sync_cmd(dev, 0, cmd, result, ADMIN_TIMEOUT); +} + +int nvme_submit_io_cmd(struct nvme_dev *dev, struct nvme_command *cmd, + u32 *result) +{ + return nvme_submit_sync_cmd(dev, smp_processor_id() + 1, cmd, result, + NVME_IO_TIMEOUT); } static int nvme_submit_admin_cmd_async(struct nvme_dev *dev, struct nvme_command *cmd, struct async_cmd_info *cmdinfo) { - return nvme_submit_async_cmd(dev->queues[0], cmd, cmdinfo, + return nvme_submit_async_cmd(raw_nvmeq(dev, 0), cmd, cmdinfo, ADMIN_TIMEOUT); } @@ -985,6 +1067,7 @@ static void nvme_abort_cmd(int cmdid, struct nvme_queue *nvmeq) struct nvme_command cmd; struct nvme_dev *dev = nvmeq->dev; struct nvme_cmd_info *info = nvme_cmd_info(nvmeq); + struct nvme_queue *adminq; if (!nvmeq->qid || info[cmdid].aborted) { if (work_busy(&dev->reset_work)) @@ -1001,7 +1084,8 @@ static void nvme_abort_cmd(int cmdid, struct nvme_queue *nvmeq) if (!dev->abort_limit) return; - a_cmdid = alloc_cmdid(dev->queues[0], CMD_CTX_ABORT, special_completion, + adminq = rcu_dereference(dev->queues[0]); + a_cmdid = alloc_cmdid(adminq, CMD_CTX_ABORT, special_completion, ADMIN_TIMEOUT); if (a_cmdid < 0) return; @@ -1018,7 +1102,7 @@ static void nvme_abort_cmd(int cmdid, struct nvme_queue *nvmeq) dev_warn(nvmeq->q_dmadev, "Aborting I/O %d QID %d\n", cmdid, nvmeq->qid); - nvme_submit_cmd(dev->queues[0], &cmd); + nvme_submit_cmd(adminq, &cmd); } /** @@ -1051,23 +1135,38 @@ static void nvme_cancel_ios(struct nvme_queue *nvmeq, bool timeout) dev_warn(nvmeq->q_dmadev, "Cancelling I/O %d QID %d\n", cmdid, nvmeq->qid); ctx = cancel_cmdid(nvmeq, cmdid, &fn); - fn(nvmeq->dev, ctx, &cqe); + fn(nvmeq, ctx, &cqe); } } -static void nvme_free_queue(struct nvme_queue *nvmeq) +static void nvme_free_queue(struct rcu_head *r) { + struct nvme_queue *nvmeq = container_of(r, struct nvme_queue, r_head); + spin_lock_irq(&nvmeq->q_lock); while (bio_list_peek(&nvmeq->sq_cong)) { struct bio *bio = bio_list_pop(&nvmeq->sq_cong); bio_endio(bio, -EIO); } + while (!list_empty(&nvmeq->iod_bio)) { + static struct nvme_completion cqe = { + .status = cpu_to_le16( + (NVME_SC_ABORT_REQ | NVME_SC_DNR) << 1), + }; + struct nvme_iod *iod = list_first_entry(&nvmeq->iod_bio, + struct nvme_iod, + node); + list_del(&iod->node); + bio_completion(nvmeq, iod, &cqe); + } spin_unlock_irq(&nvmeq->q_lock); dma_free_coherent(nvmeq->q_dmadev, CQ_SIZE(nvmeq->q_depth), (void *)nvmeq->cqes, nvmeq->cq_dma_addr); dma_free_coherent(nvmeq->q_dmadev, SQ_SIZE(nvmeq->q_depth), nvmeq->sq_cmds, nvmeq->sq_dma_addr); + if (nvmeq->qid) + free_cpumask_var(nvmeq->cpu_mask); kfree(nvmeq); } @@ -1076,9 +1175,10 @@ static void nvme_free_queues(struct nvme_dev *dev, int lowest) int i; for (i = dev->queue_count - 1; i >= lowest; i--) { - nvme_free_queue(dev->queues[i]); + struct nvme_queue *nvmeq = raw_nvmeq(dev, i); + rcu_assign_pointer(dev->queues[i], NULL); + call_rcu(&nvmeq->r_head, nvme_free_queue); dev->queue_count--; - dev->queues[i] = NULL; } } @@ -1098,6 +1198,7 @@ static int nvme_suspend_queue(struct nvme_queue *nvmeq) return 1; } nvmeq->q_suspended = 1; + nvmeq->dev->online_queues--; spin_unlock_irq(&nvmeq->q_lock); irq_set_affinity_hint(vector, NULL); @@ -1116,7 +1217,7 @@ static void nvme_clear_queue(struct nvme_queue *nvmeq) static void nvme_disable_queue(struct nvme_dev *dev, int qid) { - struct nvme_queue *nvmeq = dev->queues[qid]; + struct nvme_queue *nvmeq = raw_nvmeq(dev, qid); if (!nvmeq) return; @@ -1152,6 +1253,9 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, if (!nvmeq->sq_cmds) goto free_cqdma; + if (qid && !zalloc_cpumask_var(&nvmeq->cpu_mask, GFP_KERNEL)) + goto free_sqdma; + nvmeq->q_dmadev = dmadev; nvmeq->dev = dev; snprintf(nvmeq->irqname, sizeof(nvmeq->irqname), "nvme%dq%d", @@ -1162,15 +1266,20 @@ static struct nvme_queue *nvme_alloc_queue(struct nvme_dev *dev, int qid, init_waitqueue_head(&nvmeq->sq_full); init_waitqueue_entry(&nvmeq->sq_cong_wait, nvme_thread); bio_list_init(&nvmeq->sq_cong); + INIT_LIST_HEAD(&nvmeq->iod_bio); nvmeq->q_db = &dev->dbs[qid * 2 * dev->db_stride]; nvmeq->q_depth = depth; nvmeq->cq_vector = vector; nvmeq->qid = qid; nvmeq->q_suspended = 1; dev->queue_count++; + rcu_assign_pointer(dev->queues[qid], nvmeq); return nvmeq; + free_sqdma: + dma_free_coherent(dmadev, SQ_SIZE(depth), (void *)nvmeq->sq_cmds, + nvmeq->sq_dma_addr); free_cqdma: dma_free_coherent(dmadev, CQ_SIZE(depth), (void *)nvmeq->cqes, nvmeq->cq_dma_addr); @@ -1203,6 +1312,7 @@ static void nvme_init_queue(struct nvme_queue *nvmeq, u16 qid) memset((void *)nvmeq->cqes, 0, CQ_SIZE(nvmeq->q_depth)); nvme_cancel_ios(nvmeq, false); nvmeq->q_suspended = 0; + dev->online_queues++; } static int nvme_create_queue(struct nvme_queue *nvmeq, int qid) @@ -1311,12 +1421,11 @@ static int nvme_configure_admin_queue(struct nvme_dev *dev) if (result < 0) return result; - nvmeq = dev->queues[0]; + nvmeq = raw_nvmeq(dev, 0); if (!nvmeq) { nvmeq = nvme_alloc_queue(dev, 0, 64, 0); if (!nvmeq) return -ENOMEM; - dev->queues[0] = nvmeq; } aqa = nvmeq->q_depth - 1; @@ -1418,7 +1527,6 @@ void nvme_unmap_user_pages(struct nvme_dev *dev, int write, static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) { struct nvme_dev *dev = ns->dev; - struct nvme_queue *nvmeq; struct nvme_user_io io; struct nvme_command c; unsigned length, meta_len; @@ -1492,22 +1600,14 @@ static int nvme_submit_io(struct nvme_ns *ns, struct nvme_user_io __user *uio) c.rw.metadata = cpu_to_le64(meta_dma_addr); } - length = nvme_setup_prps(dev, &c.common, iod, length, GFP_KERNEL); + length = nvme_setup_prps(dev, iod, length, GFP_KERNEL); + c.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg)); + c.rw.prp2 = cpu_to_le64(iod->first_dma); - nvmeq = get_nvmeq(dev); - /* - * Since nvme_submit_sync_cmd sleeps, we can't keep preemption - * disabled. We may be preempted at any point, and be rescheduled - * to a different CPU. That will cause cacheline bouncing, but no - * additional races since q_lock already protects against other CPUs. - */ - put_nvmeq(nvmeq); if (length != (io.nblocks + 1) << ns->lba_shift) status = -ENOMEM; - else if (!nvmeq || nvmeq->q_suspended) - status = -EBUSY; else - status = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT); + status = nvme_submit_io_cmd(dev, &c, NULL); if (meta_len) { if (status == NVME_SC_SUCCESS && !(io.opcode & 1)) { @@ -1572,8 +1672,9 @@ static int nvme_user_admin_cmd(struct nvme_dev *dev, length); if (IS_ERR(iod)) return PTR_ERR(iod); - length = nvme_setup_prps(dev, &c.common, iod, length, - GFP_KERNEL); + length = nvme_setup_prps(dev, iod, length, GFP_KERNEL); + c.common.prp1 = cpu_to_le64(sg_dma_address(iod->sg)); + c.common.prp2 = cpu_to_le64(iod->first_dma); } timeout = cmd.timeout_ms ? msecs_to_jiffies(cmd.timeout_ms) : @@ -1581,8 +1682,7 @@ static int nvme_user_admin_cmd(struct nvme_dev *dev, if (length != cmd.data_len) status = -ENOMEM; else - status = nvme_submit_sync_cmd(dev->queues[0], &c, &cmd.result, - timeout); + status = nvme_submit_sync_cmd(dev, 0, &c, &cmd.result, timeout); if (cmd.data_len) { nvme_unmap_user_pages(dev, cmd.opcode & 1, iod); @@ -1653,25 +1753,51 @@ static void nvme_release(struct gendisk *disk, fmode_t mode) kref_put(&dev->kref, nvme_free_dev); } +static int nvme_getgeo(struct block_device *bd, struct hd_geometry *geo) +{ + /* some standard values */ + geo->heads = 1 << 6; + geo->sectors = 1 << 5; + geo->cylinders = get_capacity(bd->bd_disk) >> 11; + return 0; +} + static const struct block_device_operations nvme_fops = { .owner = THIS_MODULE, .ioctl = nvme_ioctl, .compat_ioctl = nvme_compat_ioctl, .open = nvme_open, .release = nvme_release, + .getgeo = nvme_getgeo, }; +static void nvme_resubmit_iods(struct nvme_queue *nvmeq) +{ + struct nvme_iod *iod, *next; + + list_for_each_entry_safe(iod, next, &nvmeq->iod_bio, node) { + if (unlikely(nvme_submit_iod(nvmeq, iod))) + break; + list_del(&iod->node); + if (bio_list_empty(&nvmeq->sq_cong) && + list_empty(&nvmeq->iod_bio)) + remove_wait_queue(&nvmeq->sq_full, + &nvmeq->sq_cong_wait); + } +} + static void nvme_resubmit_bios(struct nvme_queue *nvmeq) { while (bio_list_peek(&nvmeq->sq_cong)) { struct bio *bio = bio_list_pop(&nvmeq->sq_cong); struct nvme_ns *ns = bio->bi_bdev->bd_disk->private_data; - if (bio_list_empty(&nvmeq->sq_cong)) + if (bio_list_empty(&nvmeq->sq_cong) && + list_empty(&nvmeq->iod_bio)) remove_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); if (nvme_submit_bio_queue(nvmeq, ns, bio)) { - if (bio_list_empty(&nvmeq->sq_cong)) + if (!waitqueue_active(&nvmeq->sq_full)) add_wait_queue(&nvmeq->sq_full, &nvmeq->sq_cong_wait); bio_list_add_head(&nvmeq->sq_cong, bio); @@ -1700,8 +1826,10 @@ static int nvme_kthread(void *data) queue_work(nvme_workq, &dev->reset_work); continue; } + rcu_read_lock(); for (i = 0; i < dev->queue_count; i++) { - struct nvme_queue *nvmeq = dev->queues[i]; + struct nvme_queue *nvmeq = + rcu_dereference(dev->queues[i]); if (!nvmeq) continue; spin_lock_irq(&nvmeq->q_lock); @@ -1710,9 +1838,11 @@ static int nvme_kthread(void *data) nvme_process_cq(nvmeq); nvme_cancel_ios(nvmeq, true); nvme_resubmit_bios(nvmeq); + nvme_resubmit_iods(nvmeq); unlock: spin_unlock_irq(&nvmeq->q_lock); } + rcu_read_unlock(); } spin_unlock(&dev_list_lock); schedule_timeout(round_jiffies_relative(HZ)); @@ -1787,6 +1917,143 @@ static struct nvme_ns *nvme_alloc_ns(struct nvme_dev *dev, unsigned nsid, return NULL; } +static int nvme_find_closest_node(int node) +{ + int n, val, min_val = INT_MAX, best_node = node; + + for_each_online_node(n) { + if (n == node) + continue; + val = node_distance(node, n); + if (val < min_val) { + min_val = val; + best_node = n; + } + } + return best_node; +} + +static void nvme_set_queue_cpus(cpumask_t *qmask, struct nvme_queue *nvmeq, + int count) +{ + int cpu; + for_each_cpu(cpu, qmask) { + if (cpumask_weight(nvmeq->cpu_mask) >= count) + break; + if (!cpumask_test_and_set_cpu(cpu, nvmeq->cpu_mask)) + *per_cpu_ptr(nvmeq->dev->io_queue, cpu) = nvmeq->qid; + } +} + +static void nvme_add_cpus(cpumask_t *mask, const cpumask_t *unassigned_cpus, + const cpumask_t *new_mask, struct nvme_queue *nvmeq, int cpus_per_queue) +{ + int next_cpu; + for_each_cpu(next_cpu, new_mask) { + cpumask_or(mask, mask, get_cpu_mask(next_cpu)); + cpumask_or(mask, mask, topology_thread_cpumask(next_cpu)); + cpumask_and(mask, mask, unassigned_cpus); + nvme_set_queue_cpus(mask, nvmeq, cpus_per_queue); + } +} + +static void nvme_create_io_queues(struct nvme_dev *dev) +{ + unsigned i, max; + + max = min(dev->max_qid, num_online_cpus()); + for (i = dev->queue_count; i <= max; i++) + if (!nvme_alloc_queue(dev, i, dev->q_depth, i - 1)) + break; + + max = min(dev->queue_count - 1, num_online_cpus()); + for (i = dev->online_queues; i <= max; i++) + if (nvme_create_queue(raw_nvmeq(dev, i), i)) + break; +} + +/* + * If there are fewer queues than online cpus, this will try to optimally + * assign a queue to multiple cpus by grouping cpus that are "close" together: + * thread siblings, core, socket, closest node, then whatever else is + * available. + */ +static void nvme_assign_io_queues(struct nvme_dev *dev) +{ + unsigned cpu, cpus_per_queue, queues, remainder, i; + cpumask_var_t unassigned_cpus; + + nvme_create_io_queues(dev); + + queues = min(dev->online_queues - 1, num_online_cpus()); + if (!queues) + return; + + cpus_per_queue = num_online_cpus() / queues; + remainder = queues - (num_online_cpus() - queues * cpus_per_queue); + + if (!alloc_cpumask_var(&unassigned_cpus, GFP_KERNEL)) + return; + + cpumask_copy(unassigned_cpus, cpu_online_mask); + cpu = cpumask_first(unassigned_cpus); + for (i = 1; i <= queues; i++) { + struct nvme_queue *nvmeq = lock_nvmeq(dev, i); + cpumask_t mask; + + cpumask_clear(nvmeq->cpu_mask); + if (!cpumask_weight(unassigned_cpus)) { + unlock_nvmeq(nvmeq); + break; + } + + mask = *get_cpu_mask(cpu); + nvme_set_queue_cpus(&mask, nvmeq, cpus_per_queue); + if (cpus_weight(mask) < cpus_per_queue) + nvme_add_cpus(&mask, unassigned_cpus, + topology_thread_cpumask(cpu), + nvmeq, cpus_per_queue); + if (cpus_weight(mask) < cpus_per_queue) + nvme_add_cpus(&mask, unassigned_cpus, + topology_core_cpumask(cpu), + nvmeq, cpus_per_queue); + if (cpus_weight(mask) < cpus_per_queue) + nvme_add_cpus(&mask, unassigned_cpus, + cpumask_of_node(cpu_to_node(cpu)), + nvmeq, cpus_per_queue); + if (cpus_weight(mask) < cpus_per_queue) + nvme_add_cpus(&mask, unassigned_cpus, + cpumask_of_node( + nvme_find_closest_node( + cpu_to_node(cpu))), + nvmeq, cpus_per_queue); + if (cpus_weight(mask) < cpus_per_queue) + nvme_add_cpus(&mask, unassigned_cpus, + unassigned_cpus, + nvmeq, cpus_per_queue); + + WARN(cpumask_weight(nvmeq->cpu_mask) != cpus_per_queue, + "nvme%d qid:%d mis-matched queue-to-cpu assignment\n", + dev->instance, i); + + irq_set_affinity_hint(dev->entry[nvmeq->cq_vector].vector, + nvmeq->cpu_mask); + cpumask_andnot(unassigned_cpus, unassigned_cpus, + nvmeq->cpu_mask); + cpu = cpumask_next(cpu, unassigned_cpus); + if (remainder && !--remainder) + cpus_per_queue++; + unlock_nvmeq(nvmeq); + } + WARN(cpumask_weight(unassigned_cpus), "nvme%d unassigned online cpus\n", + dev->instance); + i = 0; + cpumask_andnot(unassigned_cpus, cpu_possible_mask, cpu_online_mask); + for_each_cpu(cpu, unassigned_cpus) + *per_cpu_ptr(dev->io_queue, cpu) = (i++ % queues) + 1; + free_cpumask_var(unassigned_cpus); +} + static int set_queue_count(struct nvme_dev *dev, int count) { int status; @@ -1805,13 +2072,26 @@ static size_t db_bar_size(struct nvme_dev *dev, unsigned nr_io_queues) return 4096 + ((nr_io_queues + 1) * 8 * dev->db_stride); } +static int nvme_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + struct nvme_dev *dev = container_of(self, struct nvme_dev, nb); + switch (action) { + case CPU_ONLINE: + case CPU_DEAD: + nvme_assign_io_queues(dev); + break; + } + return NOTIFY_OK; +} + static int nvme_setup_io_queues(struct nvme_dev *dev) { - struct nvme_queue *adminq = dev->queues[0]; + struct nvme_queue *adminq = raw_nvmeq(dev, 0); struct pci_dev *pdev = dev->pci_dev; - int result, cpu, i, vecs, nr_io_queues, size, q_depth; + int result, i, vecs, nr_io_queues, size; - nr_io_queues = num_online_cpus(); + nr_io_queues = num_possible_cpus(); result = set_queue_count(dev, nr_io_queues); if (result < 0) return result; @@ -1830,7 +2110,7 @@ static int nvme_setup_io_queues(struct nvme_dev *dev) size = db_bar_size(dev, nr_io_queues); } while (1); dev->dbs = ((void __iomem *)dev->bar) + 4096; - dev->queues[0]->q_db = dev->dbs; + adminq->q_db = dev->dbs; } /* Deregister the admin queue's interrupt */ @@ -1856,6 +2136,7 @@ static int nvme_setup_io_queues(struct nvme_dev *dev) * number of interrupts. */ nr_io_queues = vecs; + dev->max_qid = nr_io_queues; result = queue_request_irq(dev, adminq, adminq->irqname); if (result) { @@ -1864,49 +2145,13 @@ static int nvme_setup_io_queues(struct nvme_dev *dev) } /* Free previously allocated queues that are no longer usable */ - spin_lock(&dev_list_lock); - for (i = dev->queue_count - 1; i > nr_io_queues; i--) { - struct nvme_queue *nvmeq = dev->queues[i]; - - spin_lock_irq(&nvmeq->q_lock); - nvme_cancel_ios(nvmeq, false); - spin_unlock_irq(&nvmeq->q_lock); - - nvme_free_queue(nvmeq); - dev->queue_count--; - dev->queues[i] = NULL; - } - spin_unlock(&dev_list_lock); - - cpu = cpumask_first(cpu_online_mask); - for (i = 0; i < nr_io_queues; i++) { - irq_set_affinity_hint(dev->entry[i].vector, get_cpu_mask(cpu)); - cpu = cpumask_next(cpu, cpu_online_mask); - } - - q_depth = min_t(int, NVME_CAP_MQES(readq(&dev->bar->cap)) + 1, - NVME_Q_DEPTH); - for (i = dev->queue_count - 1; i < nr_io_queues; i++) { - dev->queues[i + 1] = nvme_alloc_queue(dev, i + 1, q_depth, i); - if (!dev->queues[i + 1]) { - result = -ENOMEM; - goto free_queues; - } - } - - for (; i < num_possible_cpus(); i++) { - int target = i % rounddown_pow_of_two(dev->queue_count - 1); - dev->queues[i + 1] = dev->queues[target + 1]; - } + nvme_free_queues(dev, nr_io_queues + 1); + nvme_assign_io_queues(dev); - for (i = 1; i < dev->queue_count; i++) { - result = nvme_create_queue(dev->queues[i], i); - if (result) { - for (--i; i > 0; i--) - nvme_disable_queue(dev, i); - goto free_queues; - } - } + dev->nb.notifier_call = &nvme_cpu_notify; + result = register_hotcpu_notifier(&dev->nb); + if (result) + goto free_queues; return 0; @@ -1985,6 +2230,7 @@ static int nvme_dev_add(struct nvme_dev *dev) static int nvme_dev_map(struct nvme_dev *dev) { + u64 cap; int bars, result = -ENOMEM; struct pci_dev *pdev = dev->pci_dev; @@ -2008,7 +2254,9 @@ static int nvme_dev_map(struct nvme_dev *dev) result = -ENODEV; goto unmap; } - dev->db_stride = 1 << NVME_CAP_STRIDE(readq(&dev->bar->cap)); + cap = readq(&dev->bar->cap); + dev->q_depth = min_t(int, NVME_CAP_MQES(cap) + 1, NVME_Q_DEPTH); + dev->db_stride = 1 << NVME_CAP_STRIDE(cap); dev->dbs = ((void __iomem *)dev->bar) + 4096; return 0; @@ -2164,7 +2412,7 @@ static void nvme_disable_io_queues(struct nvme_dev *dev) atomic_set(&dq.refcount, 0); dq.worker = &worker; for (i = dev->queue_count - 1; i > 0; i--) { - struct nvme_queue *nvmeq = dev->queues[i]; + struct nvme_queue *nvmeq = raw_nvmeq(dev, i); if (nvme_suspend_queue(nvmeq)) continue; @@ -2177,19 +2425,38 @@ static void nvme_disable_io_queues(struct nvme_dev *dev) kthread_stop(kworker_task); } +/* +* Remove the node from the device list and check +* for whether or not we need to stop the nvme_thread. +*/ +static void nvme_dev_list_remove(struct nvme_dev *dev) +{ + struct task_struct *tmp = NULL; + + spin_lock(&dev_list_lock); + list_del_init(&dev->node); + if (list_empty(&dev_list) && !IS_ERR_OR_NULL(nvme_thread)) { + tmp = nvme_thread; + nvme_thread = NULL; + } + spin_unlock(&dev_list_lock); + + if (tmp) + kthread_stop(tmp); +} + static void nvme_dev_shutdown(struct nvme_dev *dev) { int i; dev->initialized = 0; + unregister_hotcpu_notifier(&dev->nb); - spin_lock(&dev_list_lock); - list_del_init(&dev->node); - spin_unlock(&dev_list_lock); + nvme_dev_list_remove(dev); if (!dev->bar || (dev->bar && readl(&dev->bar->csts) == -1)) { for (i = dev->queue_count - 1; i >= 0; i--) { - struct nvme_queue *nvmeq = dev->queues[i]; + struct nvme_queue *nvmeq = raw_nvmeq(dev, i); nvme_suspend_queue(nvmeq); nvme_clear_queue(nvmeq); } @@ -2282,6 +2549,7 @@ static void nvme_free_dev(struct kref *kref) struct nvme_dev *dev = container_of(kref, struct nvme_dev, kref); nvme_free_namespaces(dev); + free_percpu(dev->io_queue); kfree(dev->queues); kfree(dev->entry); kfree(dev); @@ -2325,6 +2593,7 @@ static const struct file_operations nvme_dev_fops = { static int nvme_dev_start(struct nvme_dev *dev) { int result; + bool start_thread = false; result = nvme_dev_map(dev); if (result) @@ -2335,9 +2604,24 @@ static int nvme_dev_start(struct nvme_dev *dev) goto unmap; spin_lock(&dev_list_lock); + if (list_empty(&dev_list) && IS_ERR_OR_NULL(nvme_thread)) { + start_thread = true; + nvme_thread = NULL; + } list_add(&dev->node, &dev_list); spin_unlock(&dev_list_lock); + if (start_thread) { + nvme_thread = kthread_run(nvme_kthread, NULL, "nvme"); + wake_up(&nvme_kthread_wait); + } else + wait_event_killable(nvme_kthread_wait, nvme_thread); + + if (IS_ERR_OR_NULL(nvme_thread)) { + result = nvme_thread ? PTR_ERR(nvme_thread) : -EINTR; + goto disable; + } + result = nvme_setup_io_queues(dev); if (result && result != -EBUSY) goto disable; @@ -2346,9 +2630,7 @@ static int nvme_dev_start(struct nvme_dev *dev) disable: nvme_disable_queue(dev, 0); - spin_lock(&dev_list_lock); - list_del_init(&dev->node); - spin_unlock(&dev_list_lock); + nvme_dev_list_remove(dev); unmap: nvme_dev_unmap(dev); return result; @@ -2367,18 +2649,10 @@ static int nvme_remove_dead_ctrl(void *arg) static void nvme_remove_disks(struct work_struct *ws) { - int i; struct nvme_dev *dev = container_of(ws, struct nvme_dev, reset_work); nvme_dev_remove(dev); - spin_lock(&dev_list_lock); - for (i = dev->queue_count - 1; i > 0; i--) { - BUG_ON(!dev->queues[i] || !dev->queues[i]->q_suspended); - nvme_free_queue(dev->queues[i]); - dev->queue_count--; - dev->queues[i] = NULL; - } - spin_unlock(&dev_list_lock); + nvme_free_queues(dev, 1); } static int nvme_dev_resume(struct nvme_dev *dev) @@ -2441,6 +2715,9 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) GFP_KERNEL); if (!dev->queues) goto free; + dev->io_queue = alloc_percpu(unsigned short); + if (!dev->io_queue) + goto free; INIT_LIST_HEAD(&dev->namespaces); dev->reset_workfn = nvme_reset_failed_dev; @@ -2455,6 +2732,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) if (result) goto release; + kref_init(&dev->kref); result = nvme_dev_start(dev); if (result) { if (result == -EBUSY) @@ -2462,7 +2740,6 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto release_pools; } - kref_init(&dev->kref); result = nvme_dev_add(dev); if (result) goto shutdown; @@ -2491,6 +2768,7 @@ static int nvme_probe(struct pci_dev *pdev, const struct pci_device_id *id) release: nvme_release_instance(dev); free: + free_percpu(dev->io_queue); kfree(dev->queues); kfree(dev->entry); kfree(dev); @@ -2517,6 +2795,7 @@ static void nvme_remove(struct pci_dev *pdev) nvme_dev_remove(dev); nvme_dev_shutdown(dev); nvme_free_queues(dev, 0); + rcu_barrier(); nvme_release_instance(dev); nvme_release_prp_pools(dev); kref_put(&dev->kref, nvme_free_dev); @@ -2529,6 +2808,7 @@ static void nvme_remove(struct pci_dev *pdev) #define nvme_slot_reset NULL #define nvme_error_resume NULL +#ifdef CONFIG_PM_SLEEP static int nvme_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); @@ -2549,6 +2829,7 @@ static int nvme_resume(struct device *dev) } return 0; } +#endif static SIMPLE_DEV_PM_OPS(nvme_dev_pm_ops, nvme_suspend, nvme_resume); @@ -2563,7 +2844,7 @@ static const struct pci_error_handlers nvme_err_handler = { /* Move to pci_ids.h later */ #define PCI_CLASS_STORAGE_EXPRESS 0x010802 -static DEFINE_PCI_DEVICE_TABLE(nvme_id_table) = { +static const struct pci_device_id nvme_id_table[] = { { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) }, { 0, } }; @@ -2585,14 +2866,11 @@ static int __init nvme_init(void) { int result; - nvme_thread = kthread_run(nvme_kthread, NULL, "nvme"); - if (IS_ERR(nvme_thread)) - return PTR_ERR(nvme_thread); + init_waitqueue_head(&nvme_kthread_wait); - result = -ENOMEM; nvme_workq = create_singlethread_workqueue("nvme"); if (!nvme_workq) - goto kill_kthread; + return -ENOMEM; result = register_blkdev(nvme_major, "nvme"); if (result < 0) @@ -2609,8 +2887,6 @@ static int __init nvme_init(void) unregister_blkdev(nvme_major, "nvme"); kill_workq: destroy_workqueue(nvme_workq); - kill_kthread: - kthread_stop(nvme_thread); return result; } @@ -2619,11 +2895,11 @@ static void __exit nvme_exit(void) pci_unregister_driver(&nvme_driver); unregister_blkdev(nvme_major, "nvme"); destroy_workqueue(nvme_workq); - kthread_stop(nvme_thread); + BUG_ON(nvme_thread && !IS_ERR(nvme_thread)); } MODULE_AUTHOR("Matthew Wilcox <willy@linux.intel.com>"); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.8"); +MODULE_VERSION("0.9"); module_init(nvme_init); module_exit(nvme_exit); diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c index 4a0ceb64e269..2c3f5be06da1 100644 --- a/drivers/block/nvme-scsi.c +++ b/drivers/block/nvme-scsi.c @@ -1562,13 +1562,14 @@ static int nvme_trans_send_fw_cmd(struct nvme_ns *ns, struct sg_io_hdr *hdr, res = PTR_ERR(iod); goto out; } - length = nvme_setup_prps(dev, &c.common, iod, tot_len, - GFP_KERNEL); + length = nvme_setup_prps(dev, iod, tot_len, GFP_KERNEL); if (length != tot_len) { res = -ENOMEM; goto out_unmap; } + c.dlfw.prp1 = cpu_to_le64(sg_dma_address(iod->sg)); + c.dlfw.prp2 = cpu_to_le64(iod->first_dma); c.dlfw.numd = cpu_to_le32((tot_len/BYTES_TO_DWORDS) - 1); c.dlfw.offset = cpu_to_le32(offset/BYTES_TO_DWORDS); } else if (opcode == nvme_admin_activate_fw) { @@ -2033,7 +2034,6 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, int res = SNTI_TRANSLATION_SUCCESS; int nvme_sc; struct nvme_dev *dev = ns->dev; - struct nvme_queue *nvmeq; u32 num_cmds; struct nvme_iod *iod; u64 unit_len; @@ -2045,7 +2045,7 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, struct nvme_command c; u8 opcode = (is_write ? nvme_cmd_write : nvme_cmd_read); u16 control; - u32 max_blocks = nvme_block_nr(ns, dev->max_hw_sectors); + u32 max_blocks = queue_max_hw_sectors(ns->queue); num_cmds = nvme_trans_io_get_num_cmds(hdr, cdb_info, max_blocks); @@ -2093,8 +2093,7 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, res = PTR_ERR(iod); goto out; } - retcode = nvme_setup_prps(dev, &c.common, iod, unit_len, - GFP_KERNEL); + retcode = nvme_setup_prps(dev, iod, unit_len, GFP_KERNEL); if (retcode != unit_len) { nvme_unmap_user_pages(dev, (is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE, @@ -2103,21 +2102,12 @@ static int nvme_trans_do_nvme_io(struct nvme_ns *ns, struct sg_io_hdr *hdr, res = -ENOMEM; goto out; } + c.rw.prp1 = cpu_to_le64(sg_dma_address(iod->sg)); + c.rw.prp2 = cpu_to_le64(iod->first_dma); nvme_offset += unit_num_blocks; - nvmeq = get_nvmeq(dev); - /* - * Since nvme_submit_sync_cmd sleeps, we can't keep - * preemption disabled. We may be preempted at any - * point, and be rescheduled to a different CPU. That - * will cause cacheline bouncing, but no additional - * races since q_lock already protects against other - * CPUs. - */ - put_nvmeq(nvmeq); - nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL, - NVME_IO_TIMEOUT); + nvme_sc = nvme_submit_io_cmd(dev, &c, NULL); if (nvme_sc != NVME_SC_SUCCESS) { nvme_unmap_user_pages(dev, (is_write) ? DMA_TO_DEVICE : DMA_FROM_DEVICE, @@ -2644,7 +2634,6 @@ static int nvme_trans_start_stop(struct nvme_ns *ns, struct sg_io_hdr *hdr, { int res = SNTI_TRANSLATION_SUCCESS; int nvme_sc; - struct nvme_queue *nvmeq; struct nvme_command c; u8 immed, pcmod, pc, no_flush, start; @@ -2671,10 +2660,7 @@ static int nvme_trans_start_stop(struct nvme_ns *ns, struct sg_io_hdr *hdr, c.common.opcode = nvme_cmd_flush; c.common.nsid = cpu_to_le32(ns->ns_id); - nvmeq = get_nvmeq(ns->dev); - put_nvmeq(nvmeq); - nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT); - + nvme_sc = nvme_submit_io_cmd(ns->dev, &c, NULL); res = nvme_trans_status_code(hdr, nvme_sc); if (res) goto out; @@ -2697,15 +2683,12 @@ static int nvme_trans_synchronize_cache(struct nvme_ns *ns, int res = SNTI_TRANSLATION_SUCCESS; int nvme_sc; struct nvme_command c; - struct nvme_queue *nvmeq; memset(&c, 0, sizeof(c)); c.common.opcode = nvme_cmd_flush; c.common.nsid = cpu_to_le32(ns->ns_id); - nvmeq = get_nvmeq(ns->dev); - put_nvmeq(nvmeq); - nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT); + nvme_sc = nvme_submit_io_cmd(ns->dev, &c, NULL); res = nvme_trans_status_code(hdr, nvme_sc); if (res) @@ -2872,7 +2855,6 @@ static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr, struct nvme_dev *dev = ns->dev; struct scsi_unmap_parm_list *plist; struct nvme_dsm_range *range; - struct nvme_queue *nvmeq; struct nvme_command c; int i, nvme_sc, res = -ENOMEM; u16 ndesc, list_len; @@ -2914,10 +2896,7 @@ static int nvme_trans_unmap(struct nvme_ns *ns, struct sg_io_hdr *hdr, c.dsm.nr = cpu_to_le32(ndesc - 1); c.dsm.attributes = cpu_to_le32(NVME_DSMGMT_AD); - nvmeq = get_nvmeq(dev); - put_nvmeq(nvmeq); - - nvme_sc = nvme_submit_sync_cmd(nvmeq, &c, NULL, NVME_IO_TIMEOUT); + nvme_sc = nvme_submit_io_cmd(dev, &c, NULL); res = nvme_trans_status_code(hdr, nvme_sc); dma_free_coherent(&dev->pci_dev->dev, ndesc * sizeof(*range), diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 1386749b48ff..fbae63e3d304 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -408,7 +408,7 @@ config APPLICOM config SONYPI tristate "Sony Vaio Programmable I/O Control Device support" - depends on X86 && PCI && INPUT && !64BIT + depends on X86_32 && PCI && INPUT ---help--- This driver enables access to the Sony Programmable I/O Control Device which can be found in many (all ?) Sony Vaio laptops. diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c index 6928d094451d..60aafb8a1f2e 100644 --- a/drivers/char/virtio_console.c +++ b/drivers/char/virtio_console.c @@ -901,9 +901,9 @@ static int pipe_to_sg(struct pipe_inode_info *pipe, struct pipe_buffer *buf, if (len + offset > PAGE_SIZE) len = PAGE_SIZE - offset; - src = buf->ops->map(pipe, buf, 1); + src = kmap_atomic(buf->page); memcpy(page_address(page) + offset, src + buf->offset, len); - buf->ops->unmap(pipe, buf, src); + kunmap_atomic(src); sg_set_page(&(sgl->sg[sgl->n]), page, len, offset); } diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 1e2b9db563ec..0e9cce82844b 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -30,7 +30,7 @@ config ARM_EXYNOS_CPUFREQ config ARM_EXYNOS4210_CPUFREQ bool "SAMSUNG EXYNOS4210" - depends on CPU_EXYNOS4210 + depends on CPU_EXYNOS4210 && !ARCH_MULTIPLATFORM default y select ARM_EXYNOS_CPUFREQ help @@ -41,7 +41,7 @@ config ARM_EXYNOS4210_CPUFREQ config ARM_EXYNOS4X12_CPUFREQ bool "SAMSUNG EXYNOS4x12" - depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) + depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412) && !ARCH_MULTIPLATFORM default y select ARM_EXYNOS_CPUFREQ help @@ -52,7 +52,7 @@ config ARM_EXYNOS4X12_CPUFREQ config ARM_EXYNOS5250_CPUFREQ bool "SAMSUNG EXYNOS5250" - depends on SOC_EXYNOS5250 + depends on SOC_EXYNOS5250 && !ARCH_MULTIPLATFORM default y select ARM_EXYNOS_CPUFREQ help diff --git a/drivers/cpufreq/Kconfig.powerpc b/drivers/cpufreq/Kconfig.powerpc index ca0021a96e19..72564b701b4a 100644 --- a/drivers/cpufreq/Kconfig.powerpc +++ b/drivers/cpufreq/Kconfig.powerpc @@ -54,3 +54,11 @@ config PPC_PASEMI_CPUFREQ help This adds the support for frequency switching on PA Semi PWRficient processors. + +config POWERNV_CPUFREQ + tristate "CPU frequency scaling for IBM POWERNV platform" + depends on PPC_POWERNV + default y + help + This adds support for CPU frequency switching on IBM POWERNV + platform diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 74945652dd7a..0dbb963c1aef 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -86,6 +86,7 @@ obj-$(CONFIG_PPC_CORENET_CPUFREQ) += ppc-corenet-cpufreq.o obj-$(CONFIG_CPU_FREQ_PMAC) += pmac32-cpufreq.o obj-$(CONFIG_CPU_FREQ_PMAC64) += pmac64-cpufreq.o obj-$(CONFIG_PPC_PASEMI_CPUFREQ) += pasemi-cpufreq.o +obj-$(CONFIG_POWERNV_CPUFREQ) += powernv-cpufreq.o ################################################################################## # Other platform drivers diff --git a/drivers/cpufreq/acpi-cpufreq.c b/drivers/cpufreq/acpi-cpufreq.c index d5eaedbe464f..000e4e0afd7e 100644 --- a/drivers/cpufreq/acpi-cpufreq.c +++ b/drivers/cpufreq/acpi-cpufreq.c @@ -754,7 +754,7 @@ static int acpi_cpufreq_cpu_init(struct cpufreq_policy *policy) goto err_unreg; } - data->freq_table = kmalloc(sizeof(*data->freq_table) * + data->freq_table = kzalloc(sizeof(*data->freq_table) * (perf->state_count+1), GFP_KERNEL); if (!data->freq_table) { result = -ENOMEM; diff --git a/drivers/cpufreq/at32ap-cpufreq.c b/drivers/cpufreq/at32ap-cpufreq.c index a1c79f549edb..7b612c8bb09e 100644 --- a/drivers/cpufreq/at32ap-cpufreq.c +++ b/drivers/cpufreq/at32ap-cpufreq.c @@ -52,7 +52,7 @@ static int at32_set_target(struct cpufreq_policy *policy, unsigned int index) static int at32_cpufreq_driver_init(struct cpufreq_policy *policy) { unsigned int frequency, rate, min_freq; - static struct clk *cpuclk; + struct clk *cpuclk; int retval, steps, i; if (policy->cpu != 0) diff --git a/drivers/cpufreq/cris-artpec3-cpufreq.c b/drivers/cpufreq/cris-artpec3-cpufreq.c index d4573032cbbc..601b88c490cf 100644 --- a/drivers/cpufreq/cris-artpec3-cpufreq.c +++ b/drivers/cpufreq/cris-artpec3-cpufreq.c @@ -15,9 +15,9 @@ static struct notifier_block cris_sdram_freq_notifier_block = { }; static struct cpufreq_frequency_table cris_freq_table[] = { - {0x01, 6000}, - {0x02, 200000}, - {0, CPUFREQ_TABLE_END}, + {0, 0x01, 6000}, + {0, 0x02, 200000}, + {0, 0, CPUFREQ_TABLE_END}, }; static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) diff --git a/drivers/cpufreq/cris-etraxfs-cpufreq.c b/drivers/cpufreq/cris-etraxfs-cpufreq.c index 13c3361437f7..22b2cdde74d9 100644 --- a/drivers/cpufreq/cris-etraxfs-cpufreq.c +++ b/drivers/cpufreq/cris-etraxfs-cpufreq.c @@ -15,9 +15,9 @@ static struct notifier_block cris_sdram_freq_notifier_block = { }; static struct cpufreq_frequency_table cris_freq_table[] = { - {0x01, 6000}, - {0x02, 200000}, - {0, CPUFREQ_TABLE_END}, + {0, 0x01, 6000}, + {0, 0x02, 200000}, + {0, 0, CPUFREQ_TABLE_END}, }; static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu) diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c index c987e94708f5..7f5d2a68c353 100644 --- a/drivers/cpufreq/elanfreq.c +++ b/drivers/cpufreq/elanfreq.c @@ -56,15 +56,15 @@ static struct s_elan_multiplier elan_multiplier[] = { }; static struct cpufreq_frequency_table elanfreq_table[] = { - {0, 1000}, - {1, 2000}, - {2, 4000}, - {3, 8000}, - {4, 16000}, - {5, 33000}, - {6, 66000}, - {7, 99000}, - {0, CPUFREQ_TABLE_END}, + {0, 0, 1000}, + {0, 1, 2000}, + {0, 2, 4000}, + {0, 3, 8000}, + {0, 4, 16000}, + {0, 5, 33000}, + {0, 6, 66000}, + {0, 7, 99000}, + {0, 0, CPUFREQ_TABLE_END}, }; diff --git a/drivers/cpufreq/exynos4210-cpufreq.c b/drivers/cpufreq/exynos4210-cpufreq.c index 40d84c43d8f4..6384e5b9a347 100644 --- a/drivers/cpufreq/exynos4210-cpufreq.c +++ b/drivers/cpufreq/exynos4210-cpufreq.c @@ -29,12 +29,12 @@ static unsigned int exynos4210_volt_table[] = { }; static struct cpufreq_frequency_table exynos4210_freq_table[] = { - {L0, 1200 * 1000}, - {L1, 1000 * 1000}, - {L2, 800 * 1000}, - {L3, 500 * 1000}, - {L4, 200 * 1000}, - {0, CPUFREQ_TABLE_END}, + {0, L0, 1200 * 1000}, + {0, L1, 1000 * 1000}, + {0, L2, 800 * 1000}, + {0, L3, 500 * 1000}, + {0, L4, 200 * 1000}, + {0, 0, CPUFREQ_TABLE_END}, }; static struct apll_freq apll_freq_4210[] = { diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c index 7c11ace3b3fc..466c76ad335b 100644 --- a/drivers/cpufreq/exynos4x12-cpufreq.c +++ b/drivers/cpufreq/exynos4x12-cpufreq.c @@ -30,21 +30,21 @@ static unsigned int exynos4x12_volt_table[] = { }; static struct cpufreq_frequency_table exynos4x12_freq_table[] = { - {CPUFREQ_BOOST_FREQ, 1500 * 1000}, - {L1, 1400 * 1000}, - {L2, 1300 * 1000}, - {L3, 1200 * 1000}, - {L4, 1100 * 1000}, - {L5, 1000 * 1000}, - {L6, 900 * 1000}, - {L7, 800 * 1000}, - {L8, 700 * 1000}, - {L9, 600 * 1000}, - {L10, 500 * 1000}, - {L11, 400 * 1000}, - {L12, 300 * 1000}, - {L13, 200 * 1000}, - {0, CPUFREQ_TABLE_END}, + {CPUFREQ_BOOST_FREQ, L0, 1500 * 1000}, + {0, L1, 1400 * 1000}, + {0, L2, 1300 * 1000}, + {0, L3, 1200 * 1000}, + {0, L4, 1100 * 1000}, + {0, L5, 1000 * 1000}, + {0, L6, 900 * 1000}, + {0, L7, 800 * 1000}, + {0, L8, 700 * 1000}, + {0, L9, 600 * 1000}, + {0, L10, 500 * 1000}, + {0, L11, 400 * 1000}, + {0, L12, 300 * 1000}, + {0, L13, 200 * 1000}, + {0, 0, CPUFREQ_TABLE_END}, }; static struct apll_freq *apll_freq_4x12; diff --git a/drivers/cpufreq/exynos5250-cpufreq.c b/drivers/cpufreq/exynos5250-cpufreq.c index 5f90b82a4082..363a0b3fe1b1 100644 --- a/drivers/cpufreq/exynos5250-cpufreq.c +++ b/drivers/cpufreq/exynos5250-cpufreq.c @@ -34,23 +34,23 @@ static unsigned int exynos5250_volt_table[] = { }; static struct cpufreq_frequency_table exynos5250_freq_table[] = { - {L0, 1700 * 1000}, - {L1, 1600 * 1000}, - {L2, 1500 * 1000}, - {L3, 1400 * 1000}, - {L4, 1300 * 1000}, - {L5, 1200 * 1000}, - {L6, 1100 * 1000}, - {L7, 1000 * 1000}, - {L8, 900 * 1000}, - {L9, 800 * 1000}, - {L10, 700 * 1000}, - {L11, 600 * 1000}, - {L12, 500 * 1000}, - {L13, 400 * 1000}, - {L14, 300 * 1000}, - {L15, 200 * 1000}, - {0, CPUFREQ_TABLE_END}, + {0, L0, 1700 * 1000}, + {0, L1, 1600 * 1000}, + {0, L2, 1500 * 1000}, + {0, L3, 1400 * 1000}, + {0, L4, 1300 * 1000}, + {0, L5, 1200 * 1000}, + {0, L6, 1100 * 1000}, + {0, L7, 1000 * 1000}, + {0, L8, 900 * 1000}, + {0, L9, 800 * 1000}, + {0, L10, 700 * 1000}, + {0, L11, 600 * 1000}, + {0, L12, 500 * 1000}, + {0, L13, 400 * 1000}, + {0, L14, 300 * 1000}, + {0, L15, 200 * 1000}, + {0, 0, CPUFREQ_TABLE_END}, }; static struct apll_freq apll_freq_5250[] = { diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 65a477075b3f..08e7bbcf6d73 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -33,11 +33,10 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy, continue; } if (!cpufreq_boost_enabled() - && table[i].driver_data == CPUFREQ_BOOST_FREQ) + && (table[i].flags & CPUFREQ_BOOST_FREQ)) continue; - pr_debug("table entry %u: %u kHz, %u driver_data\n", - i, freq, table[i].driver_data); + pr_debug("table entry %u: %u kHz\n", i, freq); if (freq < min_freq) min_freq = freq; if (freq > max_freq) @@ -175,8 +174,8 @@ int cpufreq_frequency_table_target(struct cpufreq_policy *policy, } else *index = optimal.driver_data; - pr_debug("target is %u (%u kHz, %u)\n", *index, table[*index].frequency, - table[*index].driver_data); + pr_debug("target index is %u, freq is:%u kHz\n", *index, + table[*index].frequency); return 0; } @@ -230,7 +229,7 @@ static ssize_t show_available_freqs(struct cpufreq_policy *policy, char *buf, * show_boost = false and driver_data != BOOST freq * display NON BOOST freqs */ - if (show_boost ^ (table[i].driver_data == CPUFREQ_BOOST_FREQ)) + if (show_boost ^ (table[i].flags & CPUFREQ_BOOST_FREQ)) continue; count += sprintf(&buf[count], "%d ", table[i].frequency); diff --git a/drivers/cpufreq/ia64-acpi-cpufreq.c b/drivers/cpufreq/ia64-acpi-cpufreq.c index a22b5d182e0e..c30aaa6a54e8 100644 --- a/drivers/cpufreq/ia64-acpi-cpufreq.c +++ b/drivers/cpufreq/ia64-acpi-cpufreq.c @@ -254,7 +254,7 @@ acpi_cpufreq_cpu_init ( } /* alloc freq_table */ - data->freq_table = kmalloc(sizeof(*data->freq_table) * + data->freq_table = kzalloc(sizeof(*data->freq_table) * (data->acpi_data.state_count + 1), GFP_KERNEL); if (!data->freq_table) { @@ -275,7 +275,6 @@ acpi_cpufreq_cpu_init ( /* table init */ for (i = 0; i <= data->acpi_data.state_count; i++) { - data->freq_table[i].driver_data = i; if (i < data->acpi_data.state_count) { data->freq_table[i].frequency = data->acpi_data.states[i].core_frequency * 1000; diff --git a/drivers/cpufreq/kirkwood-cpufreq.c b/drivers/cpufreq/kirkwood-cpufreq.c index 3d114bc5a97a..37a480680cd0 100644 --- a/drivers/cpufreq/kirkwood-cpufreq.c +++ b/drivers/cpufreq/kirkwood-cpufreq.c @@ -43,9 +43,9 @@ static struct priv * table. */ static struct cpufreq_frequency_table kirkwood_freq_table[] = { - {STATE_CPU_FREQ, 0}, /* CPU uses cpuclk */ - {STATE_DDR_FREQ, 0}, /* CPU uses ddrclk */ - {0, CPUFREQ_TABLE_END}, + {0, STATE_CPU_FREQ, 0}, /* CPU uses cpuclk */ + {0, STATE_DDR_FREQ, 0}, /* CPU uses ddrclk */ + {0, 0, CPUFREQ_TABLE_END}, }; static unsigned int kirkwood_cpufreq_get_cpu_frequency(unsigned int cpu) diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index 5c440f87ba8a..d00e5d1abd25 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -475,7 +475,7 @@ static int longhaul_get_ranges(void) return -EINVAL; } - longhaul_table = kmalloc((numscales + 1) * sizeof(*longhaul_table), + longhaul_table = kzalloc((numscales + 1) * sizeof(*longhaul_table), GFP_KERNEL); if (!longhaul_table) return -ENOMEM; diff --git a/drivers/cpufreq/loongson2_cpufreq.c b/drivers/cpufreq/loongson2_cpufreq.c index a3588d61d933..f0bc31f5db27 100644 --- a/drivers/cpufreq/loongson2_cpufreq.c +++ b/drivers/cpufreq/loongson2_cpufreq.c @@ -69,7 +69,7 @@ static int loongson2_cpufreq_target(struct cpufreq_policy *policy, static int loongson2_cpufreq_cpu_init(struct cpufreq_policy *policy) { - static struct clk *cpuclk; + struct clk *cpuclk; int i; unsigned long rate; int ret; diff --git a/drivers/cpufreq/maple-cpufreq.c b/drivers/cpufreq/maple-cpufreq.c index c4dfa42a75ac..cc3408fc073f 100644 --- a/drivers/cpufreq/maple-cpufreq.c +++ b/drivers/cpufreq/maple-cpufreq.c @@ -59,9 +59,9 @@ #define CPUFREQ_LOW 1 static struct cpufreq_frequency_table maple_cpu_freqs[] = { - {CPUFREQ_HIGH, 0}, - {CPUFREQ_LOW, 0}, - {0, CPUFREQ_TABLE_END}, + {0, CPUFREQ_HIGH, 0}, + {0, CPUFREQ_LOW, 0}, + {0, 0, CPUFREQ_TABLE_END}, }; /* Power mode data is an array of the 32 bits PCR values to use for diff --git a/drivers/cpufreq/p4-clockmod.c b/drivers/cpufreq/p4-clockmod.c index 74f593e70e19..529cfd92158f 100644 --- a/drivers/cpufreq/p4-clockmod.c +++ b/drivers/cpufreq/p4-clockmod.c @@ -92,16 +92,16 @@ static int cpufreq_p4_setdc(unsigned int cpu, unsigned int newstate) static struct cpufreq_frequency_table p4clockmod_table[] = { - {DC_RESV, CPUFREQ_ENTRY_INVALID}, - {DC_DFLT, 0}, - {DC_25PT, 0}, - {DC_38PT, 0}, - {DC_50PT, 0}, - {DC_64PT, 0}, - {DC_75PT, 0}, - {DC_88PT, 0}, - {DC_DISABLE, 0}, - {DC_RESV, CPUFREQ_TABLE_END}, + {0, DC_RESV, CPUFREQ_ENTRY_INVALID}, + {0, DC_DFLT, 0}, + {0, DC_25PT, 0}, + {0, DC_38PT, 0}, + {0, DC_50PT, 0}, + {0, DC_64PT, 0}, + {0, DC_75PT, 0}, + {0, DC_88PT, 0}, + {0, DC_DISABLE, 0}, + {0, DC_RESV, CPUFREQ_TABLE_END}, }; diff --git a/drivers/cpufreq/pasemi-cpufreq.c b/drivers/cpufreq/pasemi-cpufreq.c index 6a2b7d3e85a7..84c84b5f0f3a 100644 --- a/drivers/cpufreq/pasemi-cpufreq.c +++ b/drivers/cpufreq/pasemi-cpufreq.c @@ -60,12 +60,12 @@ static int current_astate; /* We support 5(A0-A4) power states excluding turbo(A5-A6) modes */ static struct cpufreq_frequency_table pas_freqs[] = { - {0, 0}, - {1, 0}, - {2, 0}, - {3, 0}, - {4, 0}, - {0, CPUFREQ_TABLE_END}, + {0, 0, 0}, + {0, 1, 0}, + {0, 2, 0}, + {0, 3, 0}, + {0, 4, 0}, + {0, 0, CPUFREQ_TABLE_END}, }; /* diff --git a/drivers/cpufreq/pmac32-cpufreq.c b/drivers/cpufreq/pmac32-cpufreq.c index cf55d202f332..7615180d7ee3 100644 --- a/drivers/cpufreq/pmac32-cpufreq.c +++ b/drivers/cpufreq/pmac32-cpufreq.c @@ -81,9 +81,9 @@ static int is_pmu_based; #define CPUFREQ_LOW 1 static struct cpufreq_frequency_table pmac_cpu_freqs[] = { - {CPUFREQ_HIGH, 0}, - {CPUFREQ_LOW, 0}, - {0, CPUFREQ_TABLE_END}, + {0, CPUFREQ_HIGH, 0}, + {0, CPUFREQ_LOW, 0}, + {0, 0, CPUFREQ_TABLE_END}, }; static inline void local_delay(unsigned long ms) diff --git a/drivers/cpufreq/pmac64-cpufreq.c b/drivers/cpufreq/pmac64-cpufreq.c index 6a338f8c3860..8bc422977b5b 100644 --- a/drivers/cpufreq/pmac64-cpufreq.c +++ b/drivers/cpufreq/pmac64-cpufreq.c @@ -65,9 +65,9 @@ #define CPUFREQ_LOW 1 static struct cpufreq_frequency_table g5_cpu_freqs[] = { - {CPUFREQ_HIGH, 0}, - {CPUFREQ_LOW, 0}, - {0, CPUFREQ_TABLE_END}, + {0, CPUFREQ_HIGH, 0}, + {0, CPUFREQ_LOW, 0}, + {0, 0, CPUFREQ_TABLE_END}, }; /* Power mode data is an array of the 32 bits PCR values to use for diff --git a/drivers/cpufreq/powernow-k6.c b/drivers/cpufreq/powernow-k6.c index 62c6f2e5afce..49f120e1bc7b 100644 --- a/drivers/cpufreq/powernow-k6.c +++ b/drivers/cpufreq/powernow-k6.c @@ -37,15 +37,15 @@ MODULE_PARM_DESC(bus_frequency, "Bus frequency in kHz"); /* Clock ratio multiplied by 10 - see table 27 in AMD#23446 */ static struct cpufreq_frequency_table clock_ratio[] = { - {60, /* 110 -> 6.0x */ 0}, - {55, /* 011 -> 5.5x */ 0}, - {50, /* 001 -> 5.0x */ 0}, - {45, /* 000 -> 4.5x */ 0}, - {40, /* 010 -> 4.0x */ 0}, - {35, /* 111 -> 3.5x */ 0}, - {30, /* 101 -> 3.0x */ 0}, - {20, /* 100 -> 2.0x */ 0}, - {0, CPUFREQ_TABLE_END} + {0, 60, /* 110 -> 6.0x */ 0}, + {0, 55, /* 011 -> 5.5x */ 0}, + {0, 50, /* 001 -> 5.0x */ 0}, + {0, 45, /* 000 -> 4.5x */ 0}, + {0, 40, /* 010 -> 4.0x */ 0}, + {0, 35, /* 111 -> 3.5x */ 0}, + {0, 30, /* 101 -> 3.0x */ 0}, + {0, 20, /* 100 -> 2.0x */ 0}, + {0, 0, CPUFREQ_TABLE_END} }; static const u8 index_to_register[8] = { 6, 3, 1, 0, 2, 7, 5, 4 }; diff --git a/drivers/cpufreq/powernow-k8.c b/drivers/cpufreq/powernow-k8.c index 770a9e1b3468..1b6ae6b57c11 100644 --- a/drivers/cpufreq/powernow-k8.c +++ b/drivers/cpufreq/powernow-k8.c @@ -623,7 +623,7 @@ static int fill_powernow_table(struct powernow_k8_data *data, if (check_pst_table(data, pst, maxvid)) return -EINVAL; - powernow_table = kmalloc((sizeof(*powernow_table) + powernow_table = kzalloc((sizeof(*powernow_table) * (data->numps + 1)), GFP_KERNEL); if (!powernow_table) { printk(KERN_ERR PFX "powernow_table memory alloc failure\n"); @@ -793,7 +793,7 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) } /* fill in data->powernow_table */ - powernow_table = kmalloc((sizeof(*powernow_table) + powernow_table = kzalloc((sizeof(*powernow_table) * (data->acpi_data.state_count + 1)), GFP_KERNEL); if (!powernow_table) { pr_debug("powernow_table memory alloc failure\n"); @@ -810,7 +810,6 @@ static int powernow_k8_cpu_init_acpi(struct powernow_k8_data *data) powernow_table[data->acpi_data.state_count].frequency = CPUFREQ_TABLE_END; - powernow_table[data->acpi_data.state_count].driver_data = 0; data->powernow_table = powernow_table; if (cpumask_first(cpu_core_mask(data->cpu)) == data->cpu) diff --git a/drivers/cpufreq/powernv-cpufreq.c b/drivers/cpufreq/powernv-cpufreq.c new file mode 100644 index 000000000000..9edccc63245d --- /dev/null +++ b/drivers/cpufreq/powernv-cpufreq.c @@ -0,0 +1,341 @@ +/* + * POWERNV cpufreq driver for the IBM POWER processors + * + * (C) Copyright IBM 2014 + * + * Author: Vaidyanathan Srinivasan <svaidy at linux.vnet.ibm.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, 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. + * + */ + +#define pr_fmt(fmt) "powernv-cpufreq: " fmt + +#include <linux/kernel.h> +#include <linux/sysfs.h> +#include <linux/cpumask.h> +#include <linux/module.h> +#include <linux/cpufreq.h> +#include <linux/smp.h> +#include <linux/of.h> + +#include <asm/cputhreads.h> +#include <asm/reg.h> + +#define POWERNV_MAX_PSTATES 256 + +static struct cpufreq_frequency_table powernv_freqs[POWERNV_MAX_PSTATES+1]; + +/* + * Note: The set of pstates consists of contiguous integers, the + * smallest of which is indicated by powernv_pstate_info.min, the + * largest of which is indicated by powernv_pstate_info.max. + * + * The nominal pstate is the highest non-turbo pstate in this + * platform. This is indicated by powernv_pstate_info.nominal. + */ +static struct powernv_pstate_info { + int min; + int max; + int nominal; + int nr_pstates; +} powernv_pstate_info; + +/* + * Initialize the freq table based on data obtained + * from the firmware passed via device-tree + */ +static int init_powernv_pstates(void) +{ + struct device_node *power_mgt; + int i, pstate_min, pstate_max, pstate_nominal, nr_pstates = 0; + const __be32 *pstate_ids, *pstate_freqs; + u32 len_ids, len_freqs; + + power_mgt = of_find_node_by_path("/ibm,opal/power-mgt"); + if (!power_mgt) { + pr_warn("power-mgt node not found\n"); + return -ENODEV; + } + + if (of_property_read_u32(power_mgt, "ibm,pstate-min", &pstate_min)) { + pr_warn("ibm,pstate-min node not found\n"); + return -ENODEV; + } + + if (of_property_read_u32(power_mgt, "ibm,pstate-max", &pstate_max)) { + pr_warn("ibm,pstate-max node not found\n"); + return -ENODEV; + } + + if (of_property_read_u32(power_mgt, "ibm,pstate-nominal", + &pstate_nominal)) { + pr_warn("ibm,pstate-nominal not found\n"); + return -ENODEV; + } + pr_info("cpufreq pstate min %d nominal %d max %d\n", pstate_min, + pstate_nominal, pstate_max); + + pstate_ids = of_get_property(power_mgt, "ibm,pstate-ids", &len_ids); + if (!pstate_ids) { + pr_warn("ibm,pstate-ids not found\n"); + return -ENODEV; + } + + pstate_freqs = of_get_property(power_mgt, "ibm,pstate-frequencies-mhz", + &len_freqs); + if (!pstate_freqs) { + pr_warn("ibm,pstate-frequencies-mhz not found\n"); + return -ENODEV; + } + + WARN_ON(len_ids != len_freqs); + nr_pstates = min(len_ids, len_freqs) / sizeof(u32); + if (!nr_pstates) { + pr_warn("No PStates found\n"); + return -ENODEV; + } + + pr_debug("NR PStates %d\n", nr_pstates); + for (i = 0; i < nr_pstates; i++) { + u32 id = be32_to_cpu(pstate_ids[i]); + u32 freq = be32_to_cpu(pstate_freqs[i]); + + pr_debug("PState id %d freq %d MHz\n", id, freq); + powernv_freqs[i].frequency = freq * 1000; /* kHz */ + powernv_freqs[i].driver_data = id; + } + /* End of list marker entry */ + powernv_freqs[i].frequency = CPUFREQ_TABLE_END; + + powernv_pstate_info.min = pstate_min; + powernv_pstate_info.max = pstate_max; + powernv_pstate_info.nominal = pstate_nominal; + powernv_pstate_info.nr_pstates = nr_pstates; + + return 0; +} + +/* Returns the CPU frequency corresponding to the pstate_id. */ +static unsigned int pstate_id_to_freq(int pstate_id) +{ + int i; + + i = powernv_pstate_info.max - pstate_id; + BUG_ON(i >= powernv_pstate_info.nr_pstates || i < 0); + + return powernv_freqs[i].frequency; +} + +/* + * cpuinfo_nominal_freq_show - Show the nominal CPU frequency as indicated by + * the firmware + */ +static ssize_t cpuinfo_nominal_freq_show(struct cpufreq_policy *policy, + char *buf) +{ + return sprintf(buf, "%u\n", + pstate_id_to_freq(powernv_pstate_info.nominal)); +} + +struct freq_attr cpufreq_freq_attr_cpuinfo_nominal_freq = + __ATTR_RO(cpuinfo_nominal_freq); + +static struct freq_attr *powernv_cpu_freq_attr[] = { + &cpufreq_freq_attr_scaling_available_freqs, + &cpufreq_freq_attr_cpuinfo_nominal_freq, + NULL, +}; + +/* Helper routines */ + +/* Access helpers to power mgt SPR */ + +static inline unsigned long get_pmspr(unsigned long sprn) +{ + switch (sprn) { + case SPRN_PMCR: + return mfspr(SPRN_PMCR); + + case SPRN_PMICR: + return mfspr(SPRN_PMICR); + + case SPRN_PMSR: + return mfspr(SPRN_PMSR); + } + BUG(); +} + +static inline void set_pmspr(unsigned long sprn, unsigned long val) +{ + switch (sprn) { + case SPRN_PMCR: + mtspr(SPRN_PMCR, val); + return; + + case SPRN_PMICR: + mtspr(SPRN_PMICR, val); + return; + } + BUG(); +} + +/* + * Use objects of this type to query/update + * pstates on a remote CPU via smp_call_function. + */ +struct powernv_smp_call_data { + unsigned int freq; + int pstate_id; +}; + +/* + * powernv_read_cpu_freq: Reads the current frequency on this CPU. + * + * Called via smp_call_function. + * + * Note: The caller of the smp_call_function should pass an argument of + * the type 'struct powernv_smp_call_data *' along with this function. + * + * The current frequency on this CPU will be returned via + * ((struct powernv_smp_call_data *)arg)->freq; + */ +static void powernv_read_cpu_freq(void *arg) +{ + unsigned long pmspr_val; + s8 local_pstate_id; + struct powernv_smp_call_data *freq_data = arg; + + pmspr_val = get_pmspr(SPRN_PMSR); + + /* + * The local pstate id corresponds bits 48..55 in the PMSR. + * Note: Watch out for the sign! + */ + local_pstate_id = (pmspr_val >> 48) & 0xFF; + freq_data->pstate_id = local_pstate_id; + freq_data->freq = pstate_id_to_freq(freq_data->pstate_id); + + pr_debug("cpu %d pmsr %016lX pstate_id %d frequency %d kHz\n", + raw_smp_processor_id(), pmspr_val, freq_data->pstate_id, + freq_data->freq); +} + +/* + * powernv_cpufreq_get: Returns the CPU frequency as reported by the + * firmware for CPU 'cpu'. This value is reported through the sysfs + * file cpuinfo_cur_freq. + */ +unsigned int powernv_cpufreq_get(unsigned int cpu) +{ + struct powernv_smp_call_data freq_data; + + smp_call_function_any(cpu_sibling_mask(cpu), powernv_read_cpu_freq, + &freq_data, 1); + + return freq_data.freq; +} + +/* + * set_pstate: Sets the pstate on this CPU. + * + * This is called via an smp_call_function. + * + * The caller must ensure that freq_data is of the type + * (struct powernv_smp_call_data *) and the pstate_id which needs to be set + * on this CPU should be present in freq_data->pstate_id. + */ +static void set_pstate(void *freq_data) +{ + unsigned long val; + unsigned long pstate_ul = + ((struct powernv_smp_call_data *) freq_data)->pstate_id; + + val = get_pmspr(SPRN_PMCR); + val = val & 0x0000FFFFFFFFFFFFULL; + + pstate_ul = pstate_ul & 0xFF; + + /* Set both global(bits 56..63) and local(bits 48..55) PStates */ + val = val | (pstate_ul << 56) | (pstate_ul << 48); + + pr_debug("Setting cpu %d pmcr to %016lX\n", + raw_smp_processor_id(), val); + set_pmspr(SPRN_PMCR, val); +} + +/* + * powernv_cpufreq_target_index: Sets the frequency corresponding to + * the cpufreq table entry indexed by new_index on the cpus in the + * mask policy->cpus + */ +static int powernv_cpufreq_target_index(struct cpufreq_policy *policy, + unsigned int new_index) +{ + struct powernv_smp_call_data freq_data; + + freq_data.pstate_id = powernv_freqs[new_index].driver_data; + + /* + * Use smp_call_function to send IPI and execute the + * mtspr on target CPU. We could do that without IPI + * if current CPU is within policy->cpus (core) + */ + smp_call_function_any(policy->cpus, set_pstate, &freq_data, 1); + + return 0; +} + +static int powernv_cpufreq_cpu_init(struct cpufreq_policy *policy) +{ + int base, i; + + base = cpu_first_thread_sibling(policy->cpu); + + for (i = 0; i < threads_per_core; i++) + cpumask_set_cpu(base + i, policy->cpus); + + return cpufreq_table_validate_and_show(policy, powernv_freqs); +} + +static struct cpufreq_driver powernv_cpufreq_driver = { + .name = "powernv-cpufreq", + .flags = CPUFREQ_CONST_LOOPS, + .init = powernv_cpufreq_cpu_init, + .verify = cpufreq_generic_frequency_table_verify, + .target_index = powernv_cpufreq_target_index, + .get = powernv_cpufreq_get, + .attr = powernv_cpu_freq_attr, +}; + +static int __init powernv_cpufreq_init(void) +{ + int rc = 0; + + /* Discover pstates from device tree and init */ + rc = init_powernv_pstates(); + if (rc) { + pr_info("powernv-cpufreq disabled. System does not support PState control\n"); + return rc; + } + + return cpufreq_register_driver(&powernv_cpufreq_driver); +} +module_init(powernv_cpufreq_init); + +static void __exit powernv_cpufreq_exit(void) +{ + cpufreq_unregister_driver(&powernv_cpufreq_driver); +} +module_exit(powernv_cpufreq_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Vaidyanathan Srinivasan <svaidy at linux.vnet.ibm.com>"); diff --git a/drivers/cpufreq/ppc-corenet-cpufreq.c b/drivers/cpufreq/ppc-corenet-cpufreq.c index 3bd9123e7026..b7e677be1df0 100644 --- a/drivers/cpufreq/ppc-corenet-cpufreq.c +++ b/drivers/cpufreq/ppc-corenet-cpufreq.c @@ -13,7 +13,6 @@ #include <linux/clk.h> #include <linux/cpufreq.h> #include <linux/errno.h> -#include <sysdev/fsl_soc.h> #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> diff --git a/drivers/cpufreq/ppc_cbe_cpufreq.c b/drivers/cpufreq/ppc_cbe_cpufreq.c index af7b1cabd1e7..5be8a48dba74 100644 --- a/drivers/cpufreq/ppc_cbe_cpufreq.c +++ b/drivers/cpufreq/ppc_cbe_cpufreq.c @@ -32,15 +32,15 @@ /* the CBE supports an 8 step frequency scaling */ static struct cpufreq_frequency_table cbe_freqs[] = { - {1, 0}, - {2, 0}, - {3, 0}, - {4, 0}, - {5, 0}, - {6, 0}, - {8, 0}, - {10, 0}, - {0, CPUFREQ_TABLE_END}, + {0, 1, 0}, + {0, 2, 0}, + {0, 3, 0}, + {0, 4, 0}, + {0, 5, 0}, + {0, 6, 0}, + {0, 8, 0}, + {0, 10, 0}, + {0, 0, CPUFREQ_TABLE_END}, }; /* diff --git a/drivers/cpufreq/s3c2416-cpufreq.c b/drivers/cpufreq/s3c2416-cpufreq.c index 826b8be23099..4626f90559b5 100644 --- a/drivers/cpufreq/s3c2416-cpufreq.c +++ b/drivers/cpufreq/s3c2416-cpufreq.c @@ -72,19 +72,19 @@ static struct s3c2416_dvfs s3c2416_dvfs_table[] = { #endif static struct cpufreq_frequency_table s3c2416_freq_table[] = { - { SOURCE_HCLK, FREQ_DVS }, - { SOURCE_ARMDIV, 133333 }, - { SOURCE_ARMDIV, 266666 }, - { SOURCE_ARMDIV, 400000 }, - { 0, CPUFREQ_TABLE_END }, + { 0, SOURCE_HCLK, FREQ_DVS }, + { 0, SOURCE_ARMDIV, 133333 }, + { 0, SOURCE_ARMDIV, 266666 }, + { 0, SOURCE_ARMDIV, 400000 }, + { 0, 0, CPUFREQ_TABLE_END }, }; static struct cpufreq_frequency_table s3c2450_freq_table[] = { - { SOURCE_HCLK, FREQ_DVS }, - { SOURCE_ARMDIV, 133500 }, - { SOURCE_ARMDIV, 267000 }, - { SOURCE_ARMDIV, 534000 }, - { 0, CPUFREQ_TABLE_END }, + { 0, SOURCE_HCLK, FREQ_DVS }, + { 0, SOURCE_ARMDIV, 133500 }, + { 0, SOURCE_ARMDIV, 267000 }, + { 0, SOURCE_ARMDIV, 534000 }, + { 0, 0, CPUFREQ_TABLE_END }, }; static unsigned int s3c2416_cpufreq_get_speed(unsigned int cpu) diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c index a3dc192d21f9..be1b2b5c9753 100644 --- a/drivers/cpufreq/s3c24xx-cpufreq.c +++ b/drivers/cpufreq/s3c24xx-cpufreq.c @@ -586,7 +586,7 @@ static int s3c_cpufreq_build_freq(void) size = cpu_cur.info->calc_freqtable(&cpu_cur, NULL, 0); size++; - ftab = kmalloc(sizeof(*ftab) * size, GFP_KERNEL); + ftab = kzalloc(sizeof(*ftab) * size, GFP_KERNEL); if (!ftab) { printk(KERN_ERR "%s: no memory for tables\n", __func__); return -ENOMEM; @@ -664,7 +664,7 @@ int __init s3c_plltab_register(struct cpufreq_frequency_table *plls, size = sizeof(*vals) * (plls_no + 1); - vals = kmalloc(size, GFP_KERNEL); + vals = kzalloc(size, GFP_KERNEL); if (vals) { memcpy(vals, plls, size); pll_reg = vals; diff --git a/drivers/cpufreq/s3c64xx-cpufreq.c b/drivers/cpufreq/s3c64xx-cpufreq.c index c4226de079ab..ff7d3ecb85f0 100644 --- a/drivers/cpufreq/s3c64xx-cpufreq.c +++ b/drivers/cpufreq/s3c64xx-cpufreq.c @@ -37,19 +37,19 @@ static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = { }; static struct cpufreq_frequency_table s3c64xx_freq_table[] = { - { 0, 66000 }, - { 0, 100000 }, - { 0, 133000 }, - { 1, 200000 }, - { 1, 222000 }, - { 1, 266000 }, - { 2, 333000 }, - { 2, 400000 }, - { 2, 532000 }, - { 2, 533000 }, - { 3, 667000 }, - { 4, 800000 }, - { 0, CPUFREQ_TABLE_END }, + { 0, 0, 66000 }, + { 0, 0, 100000 }, + { 0, 0, 133000 }, + { 0, 1, 200000 }, + { 0, 1, 222000 }, + { 0, 1, 266000 }, + { 0, 2, 333000 }, + { 0, 2, 400000 }, + { 0, 2, 532000 }, + { 0, 2, 533000 }, + { 0, 3, 667000 }, + { 0, 4, 800000 }, + { 0, 0, CPUFREQ_TABLE_END }, }; #endif diff --git a/drivers/cpufreq/s5pv210-cpufreq.c b/drivers/cpufreq/s5pv210-cpufreq.c index 72421534fff5..ab2c1a40d437 100644 --- a/drivers/cpufreq/s5pv210-cpufreq.c +++ b/drivers/cpufreq/s5pv210-cpufreq.c @@ -64,12 +64,12 @@ enum s5pv210_dmc_port { }; static struct cpufreq_frequency_table s5pv210_freq_table[] = { - {L0, 1000*1000}, - {L1, 800*1000}, - {L2, 400*1000}, - {L3, 200*1000}, - {L4, 100*1000}, - {0, CPUFREQ_TABLE_END}, + {0, L0, 1000*1000}, + {0, L1, 800*1000}, + {0, L2, 400*1000}, + {0, L3, 200*1000}, + {0, L4, 100*1000}, + {0, 0, CPUFREQ_TABLE_END}, }; static struct regulator *arm_regulator; diff --git a/drivers/cpufreq/sc520_freq.c b/drivers/cpufreq/sc520_freq.c index 69371bf0886d..ac84e4818014 100644 --- a/drivers/cpufreq/sc520_freq.c +++ b/drivers/cpufreq/sc520_freq.c @@ -33,9 +33,9 @@ static __u8 __iomem *cpuctl; #define PFX "sc520_freq: " static struct cpufreq_frequency_table sc520_freq_table[] = { - {0x01, 100000}, - {0x02, 133000}, - {0, CPUFREQ_TABLE_END}, + {0, 0x01, 100000}, + {0, 0x02, 133000}, + {0, 0, CPUFREQ_TABLE_END}, }; static unsigned int sc520_freq_get_cpu_frequency(unsigned int cpu) diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 4cfdcff8a310..38678396636d 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -195,18 +195,15 @@ static int spear_cpufreq_probe(struct platform_device *pdev) cnt = prop->length / sizeof(u32); val = prop->value; - freq_tbl = kmalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL); + freq_tbl = kzalloc(sizeof(*freq_tbl) * (cnt + 1), GFP_KERNEL); if (!freq_tbl) { ret = -ENOMEM; goto out_put_node; } - for (i = 0; i < cnt; i++) { - freq_tbl[i].driver_data = i; + for (i = 0; i < cnt; i++) freq_tbl[i].frequency = be32_to_cpup(val++); - } - freq_tbl[i].driver_data = i; freq_tbl[i].frequency = CPUFREQ_TABLE_END; spear_cpufreq.freq_tbl = freq_tbl; diff --git a/drivers/cpufreq/speedstep-ich.c b/drivers/cpufreq/speedstep-ich.c index 394ac159312a..1a07b5904ed5 100644 --- a/drivers/cpufreq/speedstep-ich.c +++ b/drivers/cpufreq/speedstep-ich.c @@ -49,9 +49,9 @@ static u32 pmbase; * are in kHz for the time being. */ static struct cpufreq_frequency_table speedstep_freqs[] = { - {SPEEDSTEP_HIGH, 0}, - {SPEEDSTEP_LOW, 0}, - {0, CPUFREQ_TABLE_END}, + {0, SPEEDSTEP_HIGH, 0}, + {0, SPEEDSTEP_LOW, 0}, + {0, 0, CPUFREQ_TABLE_END}, }; diff --git a/drivers/cpufreq/speedstep-smi.c b/drivers/cpufreq/speedstep-smi.c index db5d274dc13a..8635eec96da5 100644 --- a/drivers/cpufreq/speedstep-smi.c +++ b/drivers/cpufreq/speedstep-smi.c @@ -42,9 +42,9 @@ static enum speedstep_processor speedstep_processor; * are in kHz for the time being. */ static struct cpufreq_frequency_table speedstep_freqs[] = { - {SPEEDSTEP_HIGH, 0}, - {SPEEDSTEP_LOW, 0}, - {0, CPUFREQ_TABLE_END}, + {0, SPEEDSTEP_HIGH, 0}, + {0, SPEEDSTEP_LOW, 0}, + {0, 0, CPUFREQ_TABLE_END}, }; #define GET_SPEEDSTEP_OWNER 0 diff --git a/drivers/cpufreq/unicore2-cpufreq.c b/drivers/cpufreq/unicore2-cpufreq.c index 13be802b6170..8d045afa7fb4 100644 --- a/drivers/cpufreq/unicore2-cpufreq.c +++ b/drivers/cpufreq/unicore2-cpufreq.c @@ -45,7 +45,7 @@ static int ucv2_target(struct cpufreq_policy *policy, freqs.new = target_freq; cpufreq_freq_transition_begin(policy, &freqs); - ret = clk_set_rate(policy->mclk, target_freq * 1000); + ret = clk_set_rate(policy->clk, target_freq * 1000); cpufreq_freq_transition_end(policy, &freqs, ret); return ret; diff --git a/drivers/cpuidle/sysfs.c b/drivers/cpuidle/sysfs.c index e918b6d0caf7..efe2f175168f 100644 --- a/drivers/cpuidle/sysfs.c +++ b/drivers/cpuidle/sysfs.c @@ -293,6 +293,7 @@ static ssize_t show_state_##_name(struct cpuidle_state *state, \ } define_show_state_function(exit_latency) +define_show_state_function(target_residency) define_show_state_function(power_usage) define_show_state_ull_function(usage) define_show_state_ull_function(time) @@ -304,6 +305,7 @@ define_store_state_ull_function(disable) define_one_state_ro(name, show_state_name); define_one_state_ro(desc, show_state_desc); define_one_state_ro(latency, show_state_exit_latency); +define_one_state_ro(residency, show_state_target_residency); define_one_state_ro(power, show_state_power_usage); define_one_state_ro(usage, show_state_usage); define_one_state_ro(time, show_state_time); @@ -313,6 +315,7 @@ static struct attribute *cpuidle_state_default_attrs[] = { &attr_name.attr, &attr_desc.attr, &attr_latency.attr, + &attr_residency.attr, &attr_power.attr, &attr_usage.attr, &attr_time.attr, diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 605b016bcea4..ba06d1d2f99e 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -308,7 +308,7 @@ config DMA_OMAP config DMA_BCM2835 tristate "BCM2835 DMA engine support" - depends on (ARCH_BCM2835 || MACH_BCM2708) + depends on ARCH_BCM2835 select DMA_ENGINE select DMA_VIRTUAL_CHANNELS @@ -350,6 +350,16 @@ config MOXART_DMA select DMA_VIRTUAL_CHANNELS help Enable support for the MOXA ART SoC DMA controller. + +config FSL_EDMA + tristate "Freescale eDMA engine support" + depends on OF + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + help + Support the Freescale eDMA engine with programmable channel + multiplexing capability for DMA request sources(slot). + This module can be found on Freescale Vybrid and LS-1 SoCs. config DMA_ENGINE bool @@ -401,4 +411,13 @@ config DMATEST config DMA_ENGINE_RAID bool +config QCOM_BAM_DMA + tristate "QCOM BAM DMA support" + depends on ARCH_QCOM || (COMPILE_TEST && OF && ARM) + select DMA_ENGINE + select DMA_VIRTUAL_CHANNELS + ---help--- + Enable support for the QCOM BAM DMA controller. This controller + provides DMA capabilities for a variety of on-chip devices. + endif diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index a029d0f4a1be..5150c82c9caf 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -44,3 +44,5 @@ obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o obj-$(CONFIG_TI_CPPI41) += cppi41.o obj-$(CONFIG_K3_DMA) += k3dma.o obj-$(CONFIG_MOXART_DMA) += moxart-dma.o +obj-$(CONFIG_FSL_EDMA) += fsl-edma.o +obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o diff --git a/drivers/dma/acpi-dma.c b/drivers/dma/acpi-dma.c index 1e506afa33f5..de361a156b34 100644 --- a/drivers/dma/acpi-dma.c +++ b/drivers/dma/acpi-dma.c @@ -13,6 +13,7 @@ */ #include <linux/device.h> +#include <linux/err.h> #include <linux/module.h> #include <linux/list.h> #include <linux/mutex.h> @@ -265,7 +266,7 @@ EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_register); */ void devm_acpi_dma_controller_free(struct device *dev) { - WARN_ON(devres_destroy(dev, devm_acpi_dma_release, NULL, NULL)); + WARN_ON(devres_release(dev, devm_acpi_dma_release, NULL, NULL)); } EXPORT_SYMBOL_GPL(devm_acpi_dma_controller_free); @@ -343,7 +344,7 @@ static int acpi_dma_parse_fixed_dma(struct acpi_resource *res, void *data) * @index: index of FixedDMA descriptor for @dev * * Return: - * Pointer to appropriate dma channel on success or NULL on error. + * Pointer to appropriate dma channel on success or an error pointer. */ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev, size_t index) @@ -358,10 +359,10 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev, /* Check if the device was enumerated by ACPI */ if (!dev || !ACPI_HANDLE(dev)) - return NULL; + return ERR_PTR(-ENODEV); if (acpi_bus_get_device(ACPI_HANDLE(dev), &adev)) - return NULL; + return ERR_PTR(-ENODEV); memset(&pdata, 0, sizeof(pdata)); pdata.index = index; @@ -376,7 +377,7 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev, acpi_dev_free_resource_list(&resource_list); if (dma_spec->slave_id < 0 || dma_spec->chan_id < 0) - return NULL; + return ERR_PTR(-ENODEV); mutex_lock(&acpi_dma_lock); @@ -399,7 +400,7 @@ struct dma_chan *acpi_dma_request_slave_chan_by_index(struct device *dev, } mutex_unlock(&acpi_dma_lock); - return chan; + return chan ? chan : ERR_PTR(-EPROBE_DEFER); } EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index); @@ -413,7 +414,7 @@ EXPORT_SYMBOL_GPL(acpi_dma_request_slave_chan_by_index); * the first FixedDMA descriptor is TX and second is RX. * * Return: - * Pointer to appropriate dma channel on success or NULL on error. + * Pointer to appropriate dma channel on success or an error pointer. */ struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev, const char *name) @@ -425,7 +426,7 @@ struct dma_chan *acpi_dma_request_slave_chan_by_name(struct device *dev, else if (!strcmp(name, "rx")) index = 1; else - return NULL; + return ERR_PTR(-ENODEV); return acpi_dma_request_slave_chan_by_index(dev, index); } diff --git a/drivers/dma/at_hdmac.c b/drivers/dma/at_hdmac.c index e2c04dc81e2a..c13a3bb0f594 100644 --- a/drivers/dma/at_hdmac.c +++ b/drivers/dma/at_hdmac.c @@ -1569,7 +1569,6 @@ static int at_dma_remove(struct platform_device *pdev) /* Disable interrupts */ atc_disable_chan_irq(atdma, chan->chan_id); - tasklet_disable(&atchan->tasklet); tasklet_kill(&atchan->tasklet); list_del(&chan->device_node); diff --git a/drivers/dma/cppi41.c b/drivers/dma/cppi41.c index c18aebf7d5aa..d028f36ae655 100644 --- a/drivers/dma/cppi41.c +++ b/drivers/dma/cppi41.c @@ -620,12 +620,15 @@ static int cppi41_stop_chan(struct dma_chan *chan) u32 desc_phys; int ret; + desc_phys = lower_32_bits(c->desc_phys); + desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); + if (!cdd->chan_busy[desc_num]) + return 0; + ret = cppi41_tear_down_chan(c); if (ret) return ret; - desc_phys = lower_32_bits(c->desc_phys); - desc_num = (desc_phys - cdd->descs_phys) / sizeof(struct cppi41_desc); WARN_ON(!cdd->chan_busy[desc_num]); cdd->chan_busy[desc_num] = NULL; diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index ed610b497518..a886713937fd 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -627,18 +627,13 @@ EXPORT_SYMBOL_GPL(__dma_request_channel); struct dma_chan *dma_request_slave_channel_reason(struct device *dev, const char *name) { - struct dma_chan *chan; - /* If device-tree is present get slave info from here */ if (dev->of_node) return of_dma_request_slave_channel(dev->of_node, name); /* If device was enumerated by ACPI get slave info from here */ - if (ACPI_HANDLE(dev)) { - chan = acpi_dma_request_slave_chan_by_name(dev, name); - if (chan) - return chan; - } + if (ACPI_HANDLE(dev)) + return acpi_dma_request_slave_chan_by_name(dev, name); return ERR_PTR(-ENODEV); } diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index 05b6dea770a4..e27cec25c59e 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -340,7 +340,7 @@ static unsigned int min_odd(unsigned int x, unsigned int y) static void result(const char *err, unsigned int n, unsigned int src_off, unsigned int dst_off, unsigned int len, unsigned long data) { - pr_info("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)", + pr_info("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)\n", current->comm, n, err, src_off, dst_off, len, data); } @@ -348,7 +348,7 @@ static void dbg_result(const char *err, unsigned int n, unsigned int src_off, unsigned int dst_off, unsigned int len, unsigned long data) { - pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)", + pr_debug("%s: result #%u: '%s' with src_off=0x%x dst_off=0x%x len=0x%x (%lu)\n", current->comm, n, err, src_off, dst_off, len, data); } diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 13ac3f240e79..cfdbb92aae1d 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -33,8 +33,8 @@ * of which use ARM any more). See the "Databook" from Synopsys for * information beyond what licensees probably provide. * - * The driver has currently been tested only with the Atmel AT32AP7000, - * which does not support descriptor writeback. + * The driver has been tested with the Atmel AT32AP7000, which does not + * support descriptor writeback. */ static inline bool is_request_line_unset(struct dw_dma_chan *dwc) @@ -1479,7 +1479,6 @@ static void dw_dma_off(struct dw_dma *dw) int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) { struct dw_dma *dw; - size_t size; bool autocfg; unsigned int dw_params; unsigned int nr_channels; @@ -1487,6 +1486,13 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) int err; int i; + dw = devm_kzalloc(chip->dev, sizeof(*dw), GFP_KERNEL); + if (!dw) + return -ENOMEM; + + dw->regs = chip->regs; + chip->dw = dw; + dw_params = dma_read_byaddr(chip->regs, DW_PARAMS); autocfg = dw_params >> DW_PARAMS_EN & 0x1; @@ -1509,9 +1515,9 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) else nr_channels = pdata->nr_channels; - size = sizeof(struct dw_dma) + nr_channels * sizeof(struct dw_dma_chan); - dw = devm_kzalloc(chip->dev, size, GFP_KERNEL); - if (!dw) + dw->chan = devm_kcalloc(chip->dev, nr_channels, sizeof(*dw->chan), + GFP_KERNEL); + if (!dw->chan) return -ENOMEM; dw->clk = devm_clk_get(chip->dev, "hclk"); @@ -1519,9 +1525,6 @@ int dw_dma_probe(struct dw_dma_chip *chip, struct dw_dma_platform_data *pdata) return PTR_ERR(dw->clk); clk_prepare_enable(dw->clk); - dw->regs = chip->regs; - chip->dw = dw; - /* Get hardware configuration parameters */ if (autocfg) { max_blk_size = dma_readl(dw, MAX_BLK_SIZE); diff --git a/drivers/dma/dw/pci.c b/drivers/dma/dw/pci.c index e89fc24b8293..fec59f1a77bb 100644 --- a/drivers/dma/dw/pci.c +++ b/drivers/dma/dw/pci.c @@ -75,6 +75,36 @@ static void dw_pci_remove(struct pci_dev *pdev) dev_warn(&pdev->dev, "can't remove device properly: %d\n", ret); } +#ifdef CONFIG_PM_SLEEP + +static int dw_pci_suspend_late(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct dw_dma_chip *chip = pci_get_drvdata(pci); + + return dw_dma_suspend(chip); +}; + +static int dw_pci_resume_early(struct device *dev) +{ + struct pci_dev *pci = to_pci_dev(dev); + struct dw_dma_chip *chip = pci_get_drvdata(pci); + + return dw_dma_resume(chip); +}; + +#else /* !CONFIG_PM_SLEEP */ + +#define dw_pci_suspend_late NULL +#define dw_pci_resume_early NULL + +#endif /* !CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops dw_pci_dev_pm_ops = { + .suspend_late = dw_pci_suspend_late, + .resume_early = dw_pci_resume_early, +}; + static DEFINE_PCI_DEVICE_TABLE(dw_pci_id_table) = { /* Medfield */ { PCI_VDEVICE(INTEL, 0x0827), (kernel_ulong_t)&dw_pci_pdata }, @@ -83,6 +113,9 @@ static DEFINE_PCI_DEVICE_TABLE(dw_pci_id_table) = { /* BayTrail */ { PCI_VDEVICE(INTEL, 0x0f06), (kernel_ulong_t)&dw_pci_pdata }, { PCI_VDEVICE(INTEL, 0x0f40), (kernel_ulong_t)&dw_pci_pdata }, + + /* Haswell */ + { PCI_VDEVICE(INTEL, 0x9c60), (kernel_ulong_t)&dw_pci_pdata }, { } }; MODULE_DEVICE_TABLE(pci, dw_pci_id_table); @@ -92,6 +125,9 @@ static struct pci_driver dw_pci_driver = { .id_table = dw_pci_id_table, .probe = dw_pci_probe, .remove = dw_pci_remove, + .driver = { + .pm = &dw_pci_dev_pm_ops, + }, }; module_pci_driver(dw_pci_driver); diff --git a/drivers/dma/dw/regs.h b/drivers/dma/dw/regs.h index deb4274f80f4..bb98d3e91e8b 100644 --- a/drivers/dma/dw/regs.h +++ b/drivers/dma/dw/regs.h @@ -252,13 +252,13 @@ struct dw_dma { struct tasklet_struct tasklet; struct clk *clk; + /* channels */ + struct dw_dma_chan *chan; u8 all_chan_mask; /* hardware configuration */ unsigned char nr_masters; unsigned char data_width[4]; - - struct dw_dma_chan chan[0]; }; static inline struct dw_dma_regs __iomem *__dw_regs(struct dw_dma *dw) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index cd8da451d199..cd04eb7b182e 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -539,6 +539,7 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( edma_alloc_slot(EDMA_CTLR(echan->ch_num), EDMA_SLOT_ANY); if (echan->slot[i] < 0) { + kfree(edesc); dev_err(dev, "Failed to allocate slot\n"); return NULL; } @@ -553,8 +554,10 @@ static struct dma_async_tx_descriptor *edma_prep_dma_cyclic( ret = edma_config_pset(chan, &edesc->pset[i], src_addr, dst_addr, burst, dev_width, period_len, direction); - if (ret < 0) + if (ret < 0) { + kfree(edesc); return NULL; + } if (direction == DMA_DEV_TO_MEM) dst_addr += period_len; diff --git a/drivers/dma/fsl-edma.c b/drivers/dma/fsl-edma.c new file mode 100644 index 000000000000..381e793184ba --- /dev/null +++ b/drivers/dma/fsl-edma.c @@ -0,0 +1,975 @@ +/* + * drivers/dma/fsl-edma.c + * + * Copyright 2013-2014 Freescale Semiconductor, Inc. + * + * Driver for the Freescale eDMA engine with flexible channel multiplexing + * capability for DMA request sources. The eDMA block can be found on some + * Vybrid and Layerscape SoCs. + * + * 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/init.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_dma.h> + +#include "virt-dma.h" + +#define EDMA_CR 0x00 +#define EDMA_ES 0x04 +#define EDMA_ERQ 0x0C +#define EDMA_EEI 0x14 +#define EDMA_SERQ 0x1B +#define EDMA_CERQ 0x1A +#define EDMA_SEEI 0x19 +#define EDMA_CEEI 0x18 +#define EDMA_CINT 0x1F +#define EDMA_CERR 0x1E +#define EDMA_SSRT 0x1D +#define EDMA_CDNE 0x1C +#define EDMA_INTR 0x24 +#define EDMA_ERR 0x2C + +#define EDMA_TCD_SADDR(x) (0x1000 + 32 * (x)) +#define EDMA_TCD_SOFF(x) (0x1004 + 32 * (x)) +#define EDMA_TCD_ATTR(x) (0x1006 + 32 * (x)) +#define EDMA_TCD_NBYTES(x) (0x1008 + 32 * (x)) +#define EDMA_TCD_SLAST(x) (0x100C + 32 * (x)) +#define EDMA_TCD_DADDR(x) (0x1010 + 32 * (x)) +#define EDMA_TCD_DOFF(x) (0x1014 + 32 * (x)) +#define EDMA_TCD_CITER_ELINK(x) (0x1016 + 32 * (x)) +#define EDMA_TCD_CITER(x) (0x1016 + 32 * (x)) +#define EDMA_TCD_DLAST_SGA(x) (0x1018 + 32 * (x)) +#define EDMA_TCD_CSR(x) (0x101C + 32 * (x)) +#define EDMA_TCD_BITER_ELINK(x) (0x101E + 32 * (x)) +#define EDMA_TCD_BITER(x) (0x101E + 32 * (x)) + +#define EDMA_CR_EDBG BIT(1) +#define EDMA_CR_ERCA BIT(2) +#define EDMA_CR_ERGA BIT(3) +#define EDMA_CR_HOE BIT(4) +#define EDMA_CR_HALT BIT(5) +#define EDMA_CR_CLM BIT(6) +#define EDMA_CR_EMLM BIT(7) +#define EDMA_CR_ECX BIT(16) +#define EDMA_CR_CX BIT(17) + +#define EDMA_SEEI_SEEI(x) ((x) & 0x1F) +#define EDMA_CEEI_CEEI(x) ((x) & 0x1F) +#define EDMA_CINT_CINT(x) ((x) & 0x1F) +#define EDMA_CERR_CERR(x) ((x) & 0x1F) + +#define EDMA_TCD_ATTR_DSIZE(x) (((x) & 0x0007)) +#define EDMA_TCD_ATTR_DMOD(x) (((x) & 0x001F) << 3) +#define EDMA_TCD_ATTR_SSIZE(x) (((x) & 0x0007) << 8) +#define EDMA_TCD_ATTR_SMOD(x) (((x) & 0x001F) << 11) +#define EDMA_TCD_ATTR_SSIZE_8BIT (0x0000) +#define EDMA_TCD_ATTR_SSIZE_16BIT (0x0100) +#define EDMA_TCD_ATTR_SSIZE_32BIT (0x0200) +#define EDMA_TCD_ATTR_SSIZE_64BIT (0x0300) +#define EDMA_TCD_ATTR_SSIZE_32BYTE (0x0500) +#define EDMA_TCD_ATTR_DSIZE_8BIT (0x0000) +#define EDMA_TCD_ATTR_DSIZE_16BIT (0x0001) +#define EDMA_TCD_ATTR_DSIZE_32BIT (0x0002) +#define EDMA_TCD_ATTR_DSIZE_64BIT (0x0003) +#define EDMA_TCD_ATTR_DSIZE_32BYTE (0x0005) + +#define EDMA_TCD_SOFF_SOFF(x) (x) +#define EDMA_TCD_NBYTES_NBYTES(x) (x) +#define EDMA_TCD_SLAST_SLAST(x) (x) +#define EDMA_TCD_DADDR_DADDR(x) (x) +#define EDMA_TCD_CITER_CITER(x) ((x) & 0x7FFF) +#define EDMA_TCD_DOFF_DOFF(x) (x) +#define EDMA_TCD_DLAST_SGA_DLAST_SGA(x) (x) +#define EDMA_TCD_BITER_BITER(x) ((x) & 0x7FFF) + +#define EDMA_TCD_CSR_START BIT(0) +#define EDMA_TCD_CSR_INT_MAJOR BIT(1) +#define EDMA_TCD_CSR_INT_HALF BIT(2) +#define EDMA_TCD_CSR_D_REQ BIT(3) +#define EDMA_TCD_CSR_E_SG BIT(4) +#define EDMA_TCD_CSR_E_LINK BIT(5) +#define EDMA_TCD_CSR_ACTIVE BIT(6) +#define EDMA_TCD_CSR_DONE BIT(7) + +#define EDMAMUX_CHCFG_DIS 0x0 +#define EDMAMUX_CHCFG_ENBL 0x80 +#define EDMAMUX_CHCFG_SOURCE(n) ((n) & 0x3F) + +#define DMAMUX_NR 2 + +#define FSL_EDMA_BUSWIDTHS BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) + +struct fsl_edma_hw_tcd { + u32 saddr; + u16 soff; + u16 attr; + u32 nbytes; + u32 slast; + u32 daddr; + u16 doff; + u16 citer; + u32 dlast_sga; + u16 csr; + u16 biter; +}; + +struct fsl_edma_sw_tcd { + dma_addr_t ptcd; + struct fsl_edma_hw_tcd *vtcd; +}; + +struct fsl_edma_slave_config { + enum dma_transfer_direction dir; + enum dma_slave_buswidth addr_width; + u32 dev_addr; + u32 burst; + u32 attr; +}; + +struct fsl_edma_chan { + struct virt_dma_chan vchan; + enum dma_status status; + struct fsl_edma_engine *edma; + struct fsl_edma_desc *edesc; + struct fsl_edma_slave_config fsc; + struct dma_pool *tcd_pool; +}; + +struct fsl_edma_desc { + struct virt_dma_desc vdesc; + struct fsl_edma_chan *echan; + bool iscyclic; + unsigned int n_tcds; + struct fsl_edma_sw_tcd tcd[]; +}; + +struct fsl_edma_engine { + struct dma_device dma_dev; + void __iomem *membase; + void __iomem *muxbase[DMAMUX_NR]; + struct clk *muxclk[DMAMUX_NR]; + struct mutex fsl_edma_mutex; + u32 n_chans; + int txirq; + int errirq; + bool big_endian; + struct fsl_edma_chan chans[]; +}; + +/* + * R/W functions for big- or little-endian registers + * the eDMA controller's endian is independent of the CPU core's endian. + */ + +static u16 edma_readw(struct fsl_edma_engine *edma, void __iomem *addr) +{ + if (edma->big_endian) + return ioread16be(addr); + else + return ioread16(addr); +} + +static u32 edma_readl(struct fsl_edma_engine *edma, void __iomem *addr) +{ + if (edma->big_endian) + return ioread32be(addr); + else + return ioread32(addr); +} + +static void edma_writeb(struct fsl_edma_engine *edma, u8 val, void __iomem *addr) +{ + iowrite8(val, addr); +} + +static void edma_writew(struct fsl_edma_engine *edma, u16 val, void __iomem *addr) +{ + if (edma->big_endian) + iowrite16be(val, addr); + else + iowrite16(val, addr); +} + +static void edma_writel(struct fsl_edma_engine *edma, u32 val, void __iomem *addr) +{ + if (edma->big_endian) + iowrite32be(val, addr); + else + iowrite32(val, addr); +} + +static struct fsl_edma_chan *to_fsl_edma_chan(struct dma_chan *chan) +{ + return container_of(chan, struct fsl_edma_chan, vchan.chan); +} + +static struct fsl_edma_desc *to_fsl_edma_desc(struct virt_dma_desc *vd) +{ + return container_of(vd, struct fsl_edma_desc, vdesc); +} + +static void fsl_edma_enable_request(struct fsl_edma_chan *fsl_chan) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + edma_writeb(fsl_chan->edma, EDMA_SEEI_SEEI(ch), addr + EDMA_SEEI); + edma_writeb(fsl_chan->edma, ch, addr + EDMA_SERQ); +} + +static void fsl_edma_disable_request(struct fsl_edma_chan *fsl_chan) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + edma_writeb(fsl_chan->edma, ch, addr + EDMA_CERQ); + edma_writeb(fsl_chan->edma, EDMA_CEEI_CEEI(ch), addr + EDMA_CEEI); +} + +static void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan, + unsigned int slot, bool enable) +{ + u32 ch = fsl_chan->vchan.chan.chan_id; + void __iomem *muxaddr = fsl_chan->edma->muxbase[ch / DMAMUX_NR]; + unsigned chans_per_mux, ch_off; + + chans_per_mux = fsl_chan->edma->n_chans / DMAMUX_NR; + ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux; + + if (enable) + edma_writeb(fsl_chan->edma, + EDMAMUX_CHCFG_ENBL | EDMAMUX_CHCFG_SOURCE(slot), + muxaddr + ch_off); + else + edma_writeb(fsl_chan->edma, EDMAMUX_CHCFG_DIS, muxaddr + ch_off); +} + +static unsigned int fsl_edma_get_tcd_attr(enum dma_slave_buswidth addr_width) +{ + switch (addr_width) { + case 1: + return EDMA_TCD_ATTR_SSIZE_8BIT | EDMA_TCD_ATTR_DSIZE_8BIT; + case 2: + return EDMA_TCD_ATTR_SSIZE_16BIT | EDMA_TCD_ATTR_DSIZE_16BIT; + case 4: + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; + case 8: + return EDMA_TCD_ATTR_SSIZE_64BIT | EDMA_TCD_ATTR_DSIZE_64BIT; + default: + return EDMA_TCD_ATTR_SSIZE_32BIT | EDMA_TCD_ATTR_DSIZE_32BIT; + } +} + +static void fsl_edma_free_desc(struct virt_dma_desc *vdesc) +{ + struct fsl_edma_desc *fsl_desc; + int i; + + fsl_desc = to_fsl_edma_desc(vdesc); + for (i = 0; i < fsl_desc->n_tcds; i++) + dma_pool_free(fsl_desc->echan->tcd_pool, + fsl_desc->tcd[i].vtcd, + fsl_desc->tcd[i].ptcd); + kfree(fsl_desc); +} + +static int fsl_edma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct dma_slave_config *cfg = (void *)arg; + unsigned long flags; + LIST_HEAD(head); + + switch (cmd) { + case DMA_TERMINATE_ALL: + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + fsl_edma_disable_request(fsl_chan); + fsl_chan->edesc = NULL; + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + return 0; + + case DMA_SLAVE_CONFIG: + fsl_chan->fsc.dir = cfg->direction; + if (cfg->direction == DMA_DEV_TO_MEM) { + fsl_chan->fsc.dev_addr = cfg->src_addr; + fsl_chan->fsc.addr_width = cfg->src_addr_width; + fsl_chan->fsc.burst = cfg->src_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->src_addr_width); + } else if (cfg->direction == DMA_MEM_TO_DEV) { + fsl_chan->fsc.dev_addr = cfg->dst_addr; + fsl_chan->fsc.addr_width = cfg->dst_addr_width; + fsl_chan->fsc.burst = cfg->dst_maxburst; + fsl_chan->fsc.attr = fsl_edma_get_tcd_attr(cfg->dst_addr_width); + } else { + return -EINVAL; + } + return 0; + + case DMA_PAUSE: + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { + fsl_edma_disable_request(fsl_chan); + fsl_chan->status = DMA_PAUSED; + } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; + + case DMA_RESUME: + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + if (fsl_chan->edesc) { + fsl_edma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; + } + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + return 0; + + default: + return -ENXIO; + } +} + +static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan, + struct virt_dma_desc *vdesc, bool in_progress) +{ + struct fsl_edma_desc *edesc = fsl_chan->edesc; + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + enum dma_transfer_direction dir = fsl_chan->fsc.dir; + dma_addr_t cur_addr, dma_addr; + size_t len, size; + int i; + + /* calculate the total size in this desc */ + for (len = i = 0; i < fsl_chan->edesc->n_tcds; i++) + len += edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes)) + * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter)); + + if (!in_progress) + return len; + + if (dir == DMA_MEM_TO_DEV) + cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_SADDR(ch)); + else + cur_addr = edma_readl(fsl_chan->edma, addr + EDMA_TCD_DADDR(ch)); + + /* figure out the finished and calculate the residue */ + for (i = 0; i < fsl_chan->edesc->n_tcds; i++) { + size = edma_readl(fsl_chan->edma, &(edesc->tcd[i].vtcd->nbytes)) + * edma_readw(fsl_chan->edma, &(edesc->tcd[i].vtcd->biter)); + if (dir == DMA_MEM_TO_DEV) + dma_addr = edma_readl(fsl_chan->edma, + &(edesc->tcd[i].vtcd->saddr)); + else + dma_addr = edma_readl(fsl_chan->edma, + &(edesc->tcd[i].vtcd->daddr)); + + len -= size; + if (cur_addr > dma_addr && cur_addr < dma_addr + size) { + len += dma_addr + size - cur_addr; + break; + } + } + + return len; +} + +static enum dma_status fsl_edma_tx_status(struct dma_chan *chan, + dma_cookie_t cookie, struct dma_tx_state *txstate) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct virt_dma_desc *vdesc; + enum dma_status status; + unsigned long flags; + + status = dma_cookie_status(chan, cookie, txstate); + if (status == DMA_COMPLETE) + return status; + + if (!txstate) + return fsl_chan->status; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + vdesc = vchan_find_desc(&fsl_chan->vchan, cookie); + if (fsl_chan->edesc && cookie == fsl_chan->edesc->vdesc.tx.cookie) + txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, true); + else if (vdesc) + txstate->residue = fsl_edma_desc_residue(fsl_chan, vdesc, false); + else + txstate->residue = 0; + + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + return fsl_chan->status; +} + +static void fsl_edma_set_tcd_params(struct fsl_edma_chan *fsl_chan, + u32 src, u32 dst, u16 attr, u16 soff, u32 nbytes, + u32 slast, u16 citer, u16 biter, u32 doff, u32 dlast_sga, + u16 csr) +{ + void __iomem *addr = fsl_chan->edma->membase; + u32 ch = fsl_chan->vchan.chan.chan_id; + + /* + * TCD parameters have been swapped in fill_tcd_params(), + * so just write them to registers in the cpu endian here + */ + writew(0, addr + EDMA_TCD_CSR(ch)); + writel(src, addr + EDMA_TCD_SADDR(ch)); + writel(dst, addr + EDMA_TCD_DADDR(ch)); + writew(attr, addr + EDMA_TCD_ATTR(ch)); + writew(soff, addr + EDMA_TCD_SOFF(ch)); + writel(nbytes, addr + EDMA_TCD_NBYTES(ch)); + writel(slast, addr + EDMA_TCD_SLAST(ch)); + writew(citer, addr + EDMA_TCD_CITER(ch)); + writew(biter, addr + EDMA_TCD_BITER(ch)); + writew(doff, addr + EDMA_TCD_DOFF(ch)); + writel(dlast_sga, addr + EDMA_TCD_DLAST_SGA(ch)); + writew(csr, addr + EDMA_TCD_CSR(ch)); +} + +static void fill_tcd_params(struct fsl_edma_engine *edma, + struct fsl_edma_hw_tcd *tcd, u32 src, u32 dst, + u16 attr, u16 soff, u32 nbytes, u32 slast, u16 citer, + u16 biter, u16 doff, u32 dlast_sga, bool major_int, + bool disable_req, bool enable_sg) +{ + u16 csr = 0; + + /* + * eDMA hardware SGs require the TCD parameters stored in memory + * the same endian as the eDMA module so that they can be loaded + * automatically by the engine + */ + edma_writel(edma, src, &(tcd->saddr)); + edma_writel(edma, dst, &(tcd->daddr)); + edma_writew(edma, attr, &(tcd->attr)); + edma_writew(edma, EDMA_TCD_SOFF_SOFF(soff), &(tcd->soff)); + edma_writel(edma, EDMA_TCD_NBYTES_NBYTES(nbytes), &(tcd->nbytes)); + edma_writel(edma, EDMA_TCD_SLAST_SLAST(slast), &(tcd->slast)); + edma_writew(edma, EDMA_TCD_CITER_CITER(citer), &(tcd->citer)); + edma_writew(edma, EDMA_TCD_DOFF_DOFF(doff), &(tcd->doff)); + edma_writel(edma, EDMA_TCD_DLAST_SGA_DLAST_SGA(dlast_sga), &(tcd->dlast_sga)); + edma_writew(edma, EDMA_TCD_BITER_BITER(biter), &(tcd->biter)); + if (major_int) + csr |= EDMA_TCD_CSR_INT_MAJOR; + + if (disable_req) + csr |= EDMA_TCD_CSR_D_REQ; + + if (enable_sg) + csr |= EDMA_TCD_CSR_E_SG; + + edma_writew(edma, csr, &(tcd->csr)); +} + +static struct fsl_edma_desc *fsl_edma_alloc_desc(struct fsl_edma_chan *fsl_chan, + int sg_len) +{ + struct fsl_edma_desc *fsl_desc; + int i; + + fsl_desc = kzalloc(sizeof(*fsl_desc) + sizeof(struct fsl_edma_sw_tcd) * sg_len, + GFP_NOWAIT); + if (!fsl_desc) + return NULL; + + fsl_desc->echan = fsl_chan; + fsl_desc->n_tcds = sg_len; + for (i = 0; i < sg_len; i++) { + fsl_desc->tcd[i].vtcd = dma_pool_alloc(fsl_chan->tcd_pool, + GFP_NOWAIT, &fsl_desc->tcd[i].ptcd); + if (!fsl_desc->tcd[i].vtcd) + goto err; + } + return fsl_desc; + +err: + while (--i >= 0) + dma_pool_free(fsl_chan->tcd_pool, fsl_desc->tcd[i].vtcd, + fsl_desc->tcd[i].ptcd); + kfree(fsl_desc); + return NULL; +} + +static struct dma_async_tx_descriptor *fsl_edma_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t dma_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + dma_addr_t dma_buf_next; + int sg_len, i; + u32 src_addr, dst_addr, last_sg, nbytes; + u16 soff, doff, iter; + + if (!is_slave_direction(fsl_chan->fsc.dir)) + return NULL; + + sg_len = buf_len / period_len; + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = true; + + dma_buf_next = dma_addr; + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; + iter = period_len / nbytes; + + for (i = 0; i < sg_len; i++) { + if (dma_buf_next >= dma_addr + buf_len) + dma_buf_next = dma_addr; + + /* get next sg's physical address */ + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; + + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + src_addr = dma_buf_next; + dst_addr = fsl_chan->fsc.dev_addr; + soff = fsl_chan->fsc.addr_width; + doff = 0; + } else { + src_addr = fsl_chan->fsc.dev_addr; + dst_addr = dma_buf_next; + soff = 0; + doff = fsl_chan->fsc.addr_width; + } + + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, src_addr, + dst_addr, fsl_chan->fsc.attr, soff, nbytes, 0, + iter, iter, doff, last_sg, true, false, true); + dma_buf_next += period_len; + } + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); +} + +static struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( + struct dma_chan *chan, struct scatterlist *sgl, + unsigned int sg_len, enum dma_transfer_direction direction, + unsigned long flags, void *context) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + struct fsl_edma_desc *fsl_desc; + struct scatterlist *sg; + u32 src_addr, dst_addr, last_sg, nbytes; + u16 soff, doff, iter; + int i; + + if (!is_slave_direction(fsl_chan->fsc.dir)) + return NULL; + + fsl_desc = fsl_edma_alloc_desc(fsl_chan, sg_len); + if (!fsl_desc) + return NULL; + fsl_desc->iscyclic = false; + + nbytes = fsl_chan->fsc.addr_width * fsl_chan->fsc.burst; + for_each_sg(sgl, sg, sg_len, i) { + /* get next sg's physical address */ + last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; + + if (fsl_chan->fsc.dir == DMA_MEM_TO_DEV) { + src_addr = sg_dma_address(sg); + dst_addr = fsl_chan->fsc.dev_addr; + soff = fsl_chan->fsc.addr_width; + doff = 0; + } else { + src_addr = fsl_chan->fsc.dev_addr; + dst_addr = sg_dma_address(sg); + soff = 0; + doff = fsl_chan->fsc.addr_width; + } + + iter = sg_dma_len(sg) / nbytes; + if (i < sg_len - 1) { + last_sg = fsl_desc->tcd[(i + 1)].ptcd; + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, + src_addr, dst_addr, fsl_chan->fsc.attr, + soff, nbytes, 0, iter, iter, doff, last_sg, + false, false, true); + } else { + last_sg = 0; + fill_tcd_params(fsl_chan->edma, fsl_desc->tcd[i].vtcd, + src_addr, dst_addr, fsl_chan->fsc.attr, + soff, nbytes, 0, iter, iter, doff, last_sg, + true, true, false); + } + } + + return vchan_tx_prep(&fsl_chan->vchan, &fsl_desc->vdesc, flags); +} + +static void fsl_edma_xfer_desc(struct fsl_edma_chan *fsl_chan) +{ + struct fsl_edma_hw_tcd *tcd; + struct virt_dma_desc *vdesc; + + vdesc = vchan_next_desc(&fsl_chan->vchan); + if (!vdesc) + return; + fsl_chan->edesc = to_fsl_edma_desc(vdesc); + tcd = fsl_chan->edesc->tcd[0].vtcd; + fsl_edma_set_tcd_params(fsl_chan, tcd->saddr, tcd->daddr, tcd->attr, + tcd->soff, tcd->nbytes, tcd->slast, tcd->citer, + tcd->biter, tcd->doff, tcd->dlast_sga, tcd->csr); + fsl_edma_enable_request(fsl_chan); + fsl_chan->status = DMA_IN_PROGRESS; +} + +static irqreturn_t fsl_edma_tx_handler(int irq, void *dev_id) +{ + struct fsl_edma_engine *fsl_edma = dev_id; + unsigned int intr, ch; + void __iomem *base_addr; + struct fsl_edma_chan *fsl_chan; + + base_addr = fsl_edma->membase; + + intr = edma_readl(fsl_edma, base_addr + EDMA_INTR); + if (!intr) + return IRQ_NONE; + + for (ch = 0; ch < fsl_edma->n_chans; ch++) { + if (intr & (0x1 << ch)) { + edma_writeb(fsl_edma, EDMA_CINT_CINT(ch), + base_addr + EDMA_CINT); + + fsl_chan = &fsl_edma->chans[ch]; + + spin_lock(&fsl_chan->vchan.lock); + if (!fsl_chan->edesc->iscyclic) { + list_del(&fsl_chan->edesc->vdesc.node); + vchan_cookie_complete(&fsl_chan->edesc->vdesc); + fsl_chan->edesc = NULL; + fsl_chan->status = DMA_COMPLETE; + } else { + vchan_cyclic_callback(&fsl_chan->edesc->vdesc); + } + + if (!fsl_chan->edesc) + fsl_edma_xfer_desc(fsl_chan); + + spin_unlock(&fsl_chan->vchan.lock); + } + } + return IRQ_HANDLED; +} + +static irqreturn_t fsl_edma_err_handler(int irq, void *dev_id) +{ + struct fsl_edma_engine *fsl_edma = dev_id; + unsigned int err, ch; + + err = edma_readl(fsl_edma, fsl_edma->membase + EDMA_ERR); + if (!err) + return IRQ_NONE; + + for (ch = 0; ch < fsl_edma->n_chans; ch++) { + if (err & (0x1 << ch)) { + fsl_edma_disable_request(&fsl_edma->chans[ch]); + edma_writeb(fsl_edma, EDMA_CERR_CERR(ch), + fsl_edma->membase + EDMA_CERR); + fsl_edma->chans[ch].status = DMA_ERROR; + } + } + return IRQ_HANDLED; +} + +static irqreturn_t fsl_edma_irq_handler(int irq, void *dev_id) +{ + if (fsl_edma_tx_handler(irq, dev_id) == IRQ_HANDLED) + return IRQ_HANDLED; + + return fsl_edma_err_handler(irq, dev_id); +} + +static void fsl_edma_issue_pending(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + + if (vchan_issue_pending(&fsl_chan->vchan) && !fsl_chan->edesc) + fsl_edma_xfer_desc(fsl_chan); + + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); +} + +static struct dma_chan *fsl_edma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct fsl_edma_engine *fsl_edma = ofdma->of_dma_data; + struct dma_chan *chan, *_chan; + + if (dma_spec->args_count != 2) + return NULL; + + mutex_lock(&fsl_edma->fsl_edma_mutex); + list_for_each_entry_safe(chan, _chan, &fsl_edma->dma_dev.channels, device_node) { + if (chan->client_count) + continue; + if ((chan->chan_id / DMAMUX_NR) == dma_spec->args[0]) { + chan = dma_get_slave_channel(chan); + if (chan) { + chan->device->privatecnt++; + fsl_edma_chan_mux(to_fsl_edma_chan(chan), + dma_spec->args[1], true); + mutex_unlock(&fsl_edma->fsl_edma_mutex); + return chan; + } + } + } + mutex_unlock(&fsl_edma->fsl_edma_mutex); + return NULL; +} + +static int fsl_edma_alloc_chan_resources(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + + fsl_chan->tcd_pool = dma_pool_create("tcd_pool", chan->device->dev, + sizeof(struct fsl_edma_hw_tcd), + 32, 0); + return 0; +} + +static void fsl_edma_free_chan_resources(struct dma_chan *chan) +{ + struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&fsl_chan->vchan.lock, flags); + fsl_edma_disable_request(fsl_chan); + fsl_edma_chan_mux(fsl_chan, 0, false); + fsl_chan->edesc = NULL; + vchan_get_all_descriptors(&fsl_chan->vchan, &head); + spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags); + + vchan_dma_desc_free_list(&fsl_chan->vchan, &head); + dma_pool_destroy(fsl_chan->tcd_pool); + fsl_chan->tcd_pool = NULL; +} + +static int fsl_dma_device_slave_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + caps->src_addr_widths = FSL_EDMA_BUSWIDTHS; + caps->dstn_addr_widths = FSL_EDMA_BUSWIDTHS; + caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + caps->cmd_pause = true; + caps->cmd_terminate = true; + + return 0; +} + +static int +fsl_edma_irq_init(struct platform_device *pdev, struct fsl_edma_engine *fsl_edma) +{ + int ret; + + fsl_edma->txirq = platform_get_irq_byname(pdev, "edma-tx"); + if (fsl_edma->txirq < 0) { + dev_err(&pdev->dev, "Can't get edma-tx irq.\n"); + return fsl_edma->txirq; + } + + fsl_edma->errirq = platform_get_irq_byname(pdev, "edma-err"); + if (fsl_edma->errirq < 0) { + dev_err(&pdev->dev, "Can't get edma-err irq.\n"); + return fsl_edma->errirq; + } + + if (fsl_edma->txirq == fsl_edma->errirq) { + ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, + fsl_edma_irq_handler, 0, "eDMA", fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register eDMA IRQ.\n"); + return ret; + } + } else { + ret = devm_request_irq(&pdev->dev, fsl_edma->txirq, + fsl_edma_tx_handler, 0, "eDMA tx", fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register eDMA tx IRQ.\n"); + return ret; + } + + ret = devm_request_irq(&pdev->dev, fsl_edma->errirq, + fsl_edma_err_handler, 0, "eDMA err", fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register eDMA err IRQ.\n"); + return ret; + } + } + + return 0; +} + +static int fsl_edma_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_edma_engine *fsl_edma; + struct fsl_edma_chan *fsl_chan; + struct resource *res; + int len, chans; + int ret, i; + + ret = of_property_read_u32(np, "dma-channels", &chans); + if (ret) { + dev_err(&pdev->dev, "Can't get dma-channels.\n"); + return ret; + } + + len = sizeof(*fsl_edma) + sizeof(*fsl_chan) * chans; + fsl_edma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL); + if (!fsl_edma) + return -ENOMEM; + + fsl_edma->n_chans = chans; + mutex_init(&fsl_edma->fsl_edma_mutex); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fsl_edma->membase = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_edma->membase)) + return PTR_ERR(fsl_edma->membase); + + for (i = 0; i < DMAMUX_NR; i++) { + char clkname[32]; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1 + i); + fsl_edma->muxbase[i] = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(fsl_edma->muxbase[i])) + return PTR_ERR(fsl_edma->muxbase[i]); + + sprintf(clkname, "dmamux%d", i); + fsl_edma->muxclk[i] = devm_clk_get(&pdev->dev, clkname); + if (IS_ERR(fsl_edma->muxclk[i])) { + dev_err(&pdev->dev, "Missing DMAMUX block clock.\n"); + return PTR_ERR(fsl_edma->muxclk[i]); + } + + ret = clk_prepare_enable(fsl_edma->muxclk[i]); + if (ret) { + dev_err(&pdev->dev, "DMAMUX clk block failed.\n"); + return ret; + } + + } + + ret = fsl_edma_irq_init(pdev, fsl_edma); + if (ret) + return ret; + + fsl_edma->big_endian = of_property_read_bool(np, "big-endian"); + + INIT_LIST_HEAD(&fsl_edma->dma_dev.channels); + for (i = 0; i < fsl_edma->n_chans; i++) { + struct fsl_edma_chan *fsl_chan = &fsl_edma->chans[i]; + + fsl_chan->edma = fsl_edma; + + fsl_chan->vchan.desc_free = fsl_edma_free_desc; + vchan_init(&fsl_chan->vchan, &fsl_edma->dma_dev); + + edma_writew(fsl_edma, 0x0, fsl_edma->membase + EDMA_TCD_CSR(i)); + fsl_edma_chan_mux(fsl_chan, 0, false); + } + + dma_cap_set(DMA_PRIVATE, fsl_edma->dma_dev.cap_mask); + dma_cap_set(DMA_SLAVE, fsl_edma->dma_dev.cap_mask); + dma_cap_set(DMA_CYCLIC, fsl_edma->dma_dev.cap_mask); + + fsl_edma->dma_dev.dev = &pdev->dev; + fsl_edma->dma_dev.device_alloc_chan_resources + = fsl_edma_alloc_chan_resources; + fsl_edma->dma_dev.device_free_chan_resources + = fsl_edma_free_chan_resources; + fsl_edma->dma_dev.device_tx_status = fsl_edma_tx_status; + fsl_edma->dma_dev.device_prep_slave_sg = fsl_edma_prep_slave_sg; + fsl_edma->dma_dev.device_prep_dma_cyclic = fsl_edma_prep_dma_cyclic; + fsl_edma->dma_dev.device_control = fsl_edma_control; + fsl_edma->dma_dev.device_issue_pending = fsl_edma_issue_pending; + fsl_edma->dma_dev.device_slave_caps = fsl_dma_device_slave_caps; + + platform_set_drvdata(pdev, fsl_edma); + + ret = dma_async_device_register(&fsl_edma->dma_dev); + if (ret) { + dev_err(&pdev->dev, "Can't register Freescale eDMA engine.\n"); + return ret; + } + + ret = of_dma_controller_register(np, fsl_edma_xlate, fsl_edma); + if (ret) { + dev_err(&pdev->dev, "Can't register Freescale eDMA of_dma.\n"); + dma_async_device_unregister(&fsl_edma->dma_dev); + return ret; + } + + /* enable round robin arbitration */ + edma_writel(fsl_edma, EDMA_CR_ERGA | EDMA_CR_ERCA, fsl_edma->membase + EDMA_CR); + + return 0; +} + +static int fsl_edma_remove(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct fsl_edma_engine *fsl_edma = platform_get_drvdata(pdev); + int i; + + of_dma_controller_free(np); + dma_async_device_unregister(&fsl_edma->dma_dev); + + for (i = 0; i < DMAMUX_NR; i++) + clk_disable_unprepare(fsl_edma->muxclk[i]); + + return 0; +} + +static const struct of_device_id fsl_edma_dt_ids[] = { + { .compatible = "fsl,vf610-edma", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fsl_edma_dt_ids); + +static struct platform_driver fsl_edma_driver = { + .driver = { + .name = "fsl-edma", + .owner = THIS_MODULE, + .of_match_table = fsl_edma_dt_ids, + }, + .probe = fsl_edma_probe, + .remove = fsl_edma_remove, +}; + +module_platform_driver(fsl_edma_driver); + +MODULE_ALIAS("platform:fsl-edma"); +MODULE_DESCRIPTION("Freescale eDMA engine driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 6f9ac2022abd..286660a12cc6 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -422,12 +422,12 @@ static irqreturn_t imxdma_err_handler(int irq, void *dev_id) /* Tasklet error handler */ tasklet_schedule(&imxdma->channel[i].dma_tasklet); - printk(KERN_WARNING - "DMA timeout on channel %d -%s%s%s%s\n", i, - errcode & IMX_DMA_ERR_BURST ? " burst" : "", - errcode & IMX_DMA_ERR_REQUEST ? " request" : "", - errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "", - errcode & IMX_DMA_ERR_BUFFER ? " buffer" : ""); + dev_warn(imxdma->dev, + "DMA timeout on channel %d -%s%s%s%s\n", i, + errcode & IMX_DMA_ERR_BURST ? " burst" : "", + errcode & IMX_DMA_ERR_REQUEST ? " request" : "", + errcode & IMX_DMA_ERR_TRANSFER ? " transfer" : "", + errcode & IMX_DMA_ERR_BUFFER ? " buffer" : ""); } return IRQ_HANDLED; } @@ -1236,6 +1236,7 @@ static int imxdma_remove(struct platform_device *pdev) static struct platform_driver imxdma_driver = { .driver = { .name = "imx-dma", + .owner = THIS_MODULE, .of_match_table = imx_dma_of_dev_id, }, .id_table = imx_dma_devtype, diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index b439679f4126..bf02e7beb51a 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -867,8 +867,8 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq) phy->base = pdev->base; if (irq) { - ret = devm_request_irq(pdev->dev, irq, mmp_pdma_chan_handler, 0, - "pdma", phy); + ret = devm_request_irq(pdev->dev, irq, mmp_pdma_chan_handler, + IRQF_SHARED, "pdma", phy); if (ret) { dev_err(pdev->dev, "channel request irq fail!\n"); return ret; @@ -957,8 +957,8 @@ static int mmp_pdma_probe(struct platform_device *op) if (irq_num != dma_channels) { /* all chan share one irq, demux inside */ irq = platform_get_irq(op, 0); - ret = devm_request_irq(pdev->dev, irq, mmp_pdma_int_handler, 0, - "pdma", pdev); + ret = devm_request_irq(pdev->dev, irq, mmp_pdma_int_handler, + IRQF_SHARED, "pdma", pdev); if (ret) return ret; } diff --git a/drivers/dma/mmp_tdma.c b/drivers/dma/mmp_tdma.c index 33f96aaa80c7..724f7f4c9720 100644 --- a/drivers/dma/mmp_tdma.c +++ b/drivers/dma/mmp_tdma.c @@ -22,6 +22,7 @@ #include <mach/regs-icu.h> #include <linux/platform_data/dma-mmp_tdma.h> #include <linux/of_device.h> +#include <linux/of_dma.h> #include "dmaengine.h" @@ -541,6 +542,45 @@ static int mmp_tdma_chan_init(struct mmp_tdma_device *tdev, return 0; } +struct mmp_tdma_filter_param { + struct device_node *of_node; + unsigned int chan_id; +}; + +static bool mmp_tdma_filter_fn(struct dma_chan *chan, void *fn_param) +{ + struct mmp_tdma_filter_param *param = fn_param; + struct mmp_tdma_chan *tdmac = to_mmp_tdma_chan(chan); + struct dma_device *pdma_device = tdmac->chan.device; + + if (pdma_device->dev->of_node != param->of_node) + return false; + + if (chan->chan_id != param->chan_id) + return false; + + return true; +} + +struct dma_chan *mmp_tdma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct mmp_tdma_device *tdev = ofdma->of_dma_data; + dma_cap_mask_t mask = tdev->device.cap_mask; + struct mmp_tdma_filter_param param; + + if (dma_spec->args_count != 1) + return NULL; + + param.of_node = ofdma->of_node; + param.chan_id = dma_spec->args[0]; + + if (param.chan_id >= TDMA_CHANNEL_NUM) + return NULL; + + return dma_request_channel(mask, mmp_tdma_filter_fn, ¶m); +} + static struct of_device_id mmp_tdma_dt_ids[] = { { .compatible = "marvell,adma-1.0", .data = (void *)MMP_AUD_TDMA}, { .compatible = "marvell,pxa910-squ", .data = (void *)PXA910_SQU}, @@ -631,6 +671,16 @@ static int mmp_tdma_probe(struct platform_device *pdev) return ret; } + if (pdev->dev.of_node) { + ret = of_dma_controller_register(pdev->dev.of_node, + mmp_tdma_xlate, tdev); + if (ret) { + dev_err(tdev->device.dev, + "failed to register controller\n"); + dma_async_device_unregister(&tdev->device); + } + } + dev_info(tdev->device.dev, "initialized\n"); return 0; } diff --git a/drivers/dma/omap-dma.c b/drivers/dma/omap-dma.c index 64ceca2920b8..b19f04f4390b 100644 --- a/drivers/dma/omap-dma.c +++ b/drivers/dma/omap-dma.c @@ -1088,6 +1088,23 @@ static void omap_dma_free(struct omap_dmadev *od) } } +#define OMAP_DMA_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \ + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \ + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES)) + +static int omap_dma_device_slave_caps(struct dma_chan *dchan, + struct dma_slave_caps *caps) +{ + caps->src_addr_widths = OMAP_DMA_BUSWIDTHS; + caps->dstn_addr_widths = OMAP_DMA_BUSWIDTHS; + caps->directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV); + caps->cmd_pause = true; + caps->cmd_terminate = true; + caps->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + + return 0; +} + static int omap_dma_probe(struct platform_device *pdev) { struct omap_dmadev *od; @@ -1118,6 +1135,7 @@ static int omap_dma_probe(struct platform_device *pdev) od->ddev.device_prep_slave_sg = omap_dma_prep_slave_sg; od->ddev.device_prep_dma_cyclic = omap_dma_prep_dma_cyclic; od->ddev.device_control = omap_dma_control; + od->ddev.device_slave_caps = omap_dma_device_slave_caps; od->ddev.dev = &pdev->dev; INIT_LIST_HEAD(&od->ddev.channels); INIT_LIST_HEAD(&od->pending); diff --git a/drivers/dma/pch_dma.c b/drivers/dma/pch_dma.c index 61fdc54a3c88..05fa548bd659 100644 --- a/drivers/dma/pch_dma.c +++ b/drivers/dma/pch_dma.c @@ -964,16 +964,16 @@ static void pch_dma_remove(struct pci_dev *pdev) if (pd) { dma_async_device_unregister(&pd->dma); + free_irq(pdev->irq, pd); + list_for_each_entry_safe(chan, _c, &pd->dma.channels, device_node) { pd_chan = to_pd_chan(chan); - tasklet_disable(&pd_chan->tasklet); tasklet_kill(&pd_chan->tasklet); } pci_pool_destroy(pd->pool); - free_irq(pdev->irq, pd); pci_iounmap(pdev, pd->membase); pci_release_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/dma/qcom_bam_dma.c b/drivers/dma/qcom_bam_dma.c new file mode 100644 index 000000000000..82c923146e49 --- /dev/null +++ b/drivers/dma/qcom_bam_dma.c @@ -0,0 +1,1111 @@ +/* + * Copyright (c) 2013-2014, The Linux Foundation. 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +/* + * QCOM BAM DMA engine driver + * + * QCOM BAM DMA blocks are distributed amongst a number of the on-chip + * peripherals on the MSM 8x74. The configuration of the channels are dependent + * on the way they are hard wired to that specific peripheral. The peripheral + * device tree entries specify the configuration of each channel. + * + * The DMA controller requires the use of external memory for storage of the + * hardware descriptors for each channel. The descriptor FIFO is accessed as a + * circular buffer and operations are managed according to the offset within the + * FIFO. After pipe/channel reset, all of the pipe registers and internal state + * are back to defaults. + * + * During DMA operations, we write descriptors to the FIFO, being careful to + * handle wrapping and then write the last FIFO offset to that channel's + * P_EVNT_REG register to kick off the transaction. The P_SW_OFSTS register + * indicates the current FIFO offset that is being processed, so there is some + * indication of where the hardware is currently working. + */ + +#include <linux/kernel.h> +#include <linux/io.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/scatterlist.h> +#include <linux/device.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/of_dma.h> +#include <linux/clk.h> +#include <linux/dmaengine.h> + +#include "dmaengine.h" +#include "virt-dma.h" + +struct bam_desc_hw { + u32 addr; /* Buffer physical address */ + u16 size; /* Buffer size in bytes */ + u16 flags; +}; + +#define DESC_FLAG_INT BIT(15) +#define DESC_FLAG_EOT BIT(14) +#define DESC_FLAG_EOB BIT(13) + +struct bam_async_desc { + struct virt_dma_desc vd; + + u32 num_desc; + u32 xfer_len; + struct bam_desc_hw *curr_desc; + + enum dma_transfer_direction dir; + size_t length; + struct bam_desc_hw desc[0]; +}; + +#define BAM_CTRL 0x0000 +#define BAM_REVISION 0x0004 +#define BAM_SW_REVISION 0x0080 +#define BAM_NUM_PIPES 0x003C +#define BAM_TIMER 0x0040 +#define BAM_TIMER_CTRL 0x0044 +#define BAM_DESC_CNT_TRSHLD 0x0008 +#define BAM_IRQ_SRCS 0x000C +#define BAM_IRQ_SRCS_MSK 0x0010 +#define BAM_IRQ_SRCS_UNMASKED 0x0030 +#define BAM_IRQ_STTS 0x0014 +#define BAM_IRQ_CLR 0x0018 +#define BAM_IRQ_EN 0x001C +#define BAM_CNFG_BITS 0x007C +#define BAM_IRQ_SRCS_EE(ee) (0x0800 + ((ee) * 0x80)) +#define BAM_IRQ_SRCS_MSK_EE(ee) (0x0804 + ((ee) * 0x80)) +#define BAM_P_CTRL(pipe) (0x1000 + ((pipe) * 0x1000)) +#define BAM_P_RST(pipe) (0x1004 + ((pipe) * 0x1000)) +#define BAM_P_HALT(pipe) (0x1008 + ((pipe) * 0x1000)) +#define BAM_P_IRQ_STTS(pipe) (0x1010 + ((pipe) * 0x1000)) +#define BAM_P_IRQ_CLR(pipe) (0x1014 + ((pipe) * 0x1000)) +#define BAM_P_IRQ_EN(pipe) (0x1018 + ((pipe) * 0x1000)) +#define BAM_P_EVNT_DEST_ADDR(pipe) (0x182C + ((pipe) * 0x1000)) +#define BAM_P_EVNT_REG(pipe) (0x1818 + ((pipe) * 0x1000)) +#define BAM_P_SW_OFSTS(pipe) (0x1800 + ((pipe) * 0x1000)) +#define BAM_P_DATA_FIFO_ADDR(pipe) (0x1824 + ((pipe) * 0x1000)) +#define BAM_P_DESC_FIFO_ADDR(pipe) (0x181C + ((pipe) * 0x1000)) +#define BAM_P_EVNT_TRSHLD(pipe) (0x1828 + ((pipe) * 0x1000)) +#define BAM_P_FIFO_SIZES(pipe) (0x1820 + ((pipe) * 0x1000)) + +/* BAM CTRL */ +#define BAM_SW_RST BIT(0) +#define BAM_EN BIT(1) +#define BAM_EN_ACCUM BIT(4) +#define BAM_TESTBUS_SEL_SHIFT 5 +#define BAM_TESTBUS_SEL_MASK 0x3F +#define BAM_DESC_CACHE_SEL_SHIFT 13 +#define BAM_DESC_CACHE_SEL_MASK 0x3 +#define BAM_CACHED_DESC_STORE BIT(15) +#define IBC_DISABLE BIT(16) + +/* BAM REVISION */ +#define REVISION_SHIFT 0 +#define REVISION_MASK 0xFF +#define NUM_EES_SHIFT 8 +#define NUM_EES_MASK 0xF +#define CE_BUFFER_SIZE BIT(13) +#define AXI_ACTIVE BIT(14) +#define USE_VMIDMT BIT(15) +#define SECURED BIT(16) +#define BAM_HAS_NO_BYPASS BIT(17) +#define HIGH_FREQUENCY_BAM BIT(18) +#define INACTIV_TMRS_EXST BIT(19) +#define NUM_INACTIV_TMRS BIT(20) +#define DESC_CACHE_DEPTH_SHIFT 21 +#define DESC_CACHE_DEPTH_1 (0 << DESC_CACHE_DEPTH_SHIFT) +#define DESC_CACHE_DEPTH_2 (1 << DESC_CACHE_DEPTH_SHIFT) +#define DESC_CACHE_DEPTH_3 (2 << DESC_CACHE_DEPTH_SHIFT) +#define DESC_CACHE_DEPTH_4 (3 << DESC_CACHE_DEPTH_SHIFT) +#define CMD_DESC_EN BIT(23) +#define INACTIV_TMR_BASE_SHIFT 24 +#define INACTIV_TMR_BASE_MASK 0xFF + +/* BAM NUM PIPES */ +#define BAM_NUM_PIPES_SHIFT 0 +#define BAM_NUM_PIPES_MASK 0xFF +#define PERIPH_NON_PIPE_GRP_SHIFT 16 +#define PERIPH_NON_PIP_GRP_MASK 0xFF +#define BAM_NON_PIPE_GRP_SHIFT 24 +#define BAM_NON_PIPE_GRP_MASK 0xFF + +/* BAM CNFG BITS */ +#define BAM_PIPE_CNFG BIT(2) +#define BAM_FULL_PIPE BIT(11) +#define BAM_NO_EXT_P_RST BIT(12) +#define BAM_IBC_DISABLE BIT(13) +#define BAM_SB_CLK_REQ BIT(14) +#define BAM_PSM_CSW_REQ BIT(15) +#define BAM_PSM_P_RES BIT(16) +#define BAM_AU_P_RES BIT(17) +#define BAM_SI_P_RES BIT(18) +#define BAM_WB_P_RES BIT(19) +#define BAM_WB_BLK_CSW BIT(20) +#define BAM_WB_CSW_ACK_IDL BIT(21) +#define BAM_WB_RETR_SVPNT BIT(22) +#define BAM_WB_DSC_AVL_P_RST BIT(23) +#define BAM_REG_P_EN BIT(24) +#define BAM_PSM_P_HD_DATA BIT(25) +#define BAM_AU_ACCUMED BIT(26) +#define BAM_CMD_ENABLE BIT(27) + +#define BAM_CNFG_BITS_DEFAULT (BAM_PIPE_CNFG | \ + BAM_NO_EXT_P_RST | \ + BAM_IBC_DISABLE | \ + BAM_SB_CLK_REQ | \ + BAM_PSM_CSW_REQ | \ + BAM_PSM_P_RES | \ + BAM_AU_P_RES | \ + BAM_SI_P_RES | \ + BAM_WB_P_RES | \ + BAM_WB_BLK_CSW | \ + BAM_WB_CSW_ACK_IDL | \ + BAM_WB_RETR_SVPNT | \ + BAM_WB_DSC_AVL_P_RST | \ + BAM_REG_P_EN | \ + BAM_PSM_P_HD_DATA | \ + BAM_AU_ACCUMED | \ + BAM_CMD_ENABLE) + +/* PIPE CTRL */ +#define P_EN BIT(1) +#define P_DIRECTION BIT(3) +#define P_SYS_STRM BIT(4) +#define P_SYS_MODE BIT(5) +#define P_AUTO_EOB BIT(6) +#define P_AUTO_EOB_SEL_SHIFT 7 +#define P_AUTO_EOB_SEL_512 (0 << P_AUTO_EOB_SEL_SHIFT) +#define P_AUTO_EOB_SEL_256 (1 << P_AUTO_EOB_SEL_SHIFT) +#define P_AUTO_EOB_SEL_128 (2 << P_AUTO_EOB_SEL_SHIFT) +#define P_AUTO_EOB_SEL_64 (3 << P_AUTO_EOB_SEL_SHIFT) +#define P_PREFETCH_LIMIT_SHIFT 9 +#define P_PREFETCH_LIMIT_32 (0 << P_PREFETCH_LIMIT_SHIFT) +#define P_PREFETCH_LIMIT_16 (1 << P_PREFETCH_LIMIT_SHIFT) +#define P_PREFETCH_LIMIT_4 (2 << P_PREFETCH_LIMIT_SHIFT) +#define P_WRITE_NWD BIT(11) +#define P_LOCK_GROUP_SHIFT 16 +#define P_LOCK_GROUP_MASK 0x1F + +/* BAM_DESC_CNT_TRSHLD */ +#define CNT_TRSHLD 0xffff +#define DEFAULT_CNT_THRSHLD 0x4 + +/* BAM_IRQ_SRCS */ +#define BAM_IRQ BIT(31) +#define P_IRQ 0x7fffffff + +/* BAM_IRQ_SRCS_MSK */ +#define BAM_IRQ_MSK BAM_IRQ +#define P_IRQ_MSK P_IRQ + +/* BAM_IRQ_STTS */ +#define BAM_TIMER_IRQ BIT(4) +#define BAM_EMPTY_IRQ BIT(3) +#define BAM_ERROR_IRQ BIT(2) +#define BAM_HRESP_ERR_IRQ BIT(1) + +/* BAM_IRQ_CLR */ +#define BAM_TIMER_CLR BIT(4) +#define BAM_EMPTY_CLR BIT(3) +#define BAM_ERROR_CLR BIT(2) +#define BAM_HRESP_ERR_CLR BIT(1) + +/* BAM_IRQ_EN */ +#define BAM_TIMER_EN BIT(4) +#define BAM_EMPTY_EN BIT(3) +#define BAM_ERROR_EN BIT(2) +#define BAM_HRESP_ERR_EN BIT(1) + +/* BAM_P_IRQ_EN */ +#define P_PRCSD_DESC_EN BIT(0) +#define P_TIMER_EN BIT(1) +#define P_WAKE_EN BIT(2) +#define P_OUT_OF_DESC_EN BIT(3) +#define P_ERR_EN BIT(4) +#define P_TRNSFR_END_EN BIT(5) +#define P_DEFAULT_IRQS_EN (P_PRCSD_DESC_EN | P_ERR_EN | P_TRNSFR_END_EN) + +/* BAM_P_SW_OFSTS */ +#define P_SW_OFSTS_MASK 0xffff + +#define BAM_DESC_FIFO_SIZE SZ_32K +#define MAX_DESCRIPTORS (BAM_DESC_FIFO_SIZE / sizeof(struct bam_desc_hw) - 1) +#define BAM_MAX_DATA_SIZE (SZ_32K - 8) + +struct bam_chan { + struct virt_dma_chan vc; + + struct bam_device *bdev; + + /* configuration from device tree */ + u32 id; + + struct bam_async_desc *curr_txd; /* current running dma */ + + /* runtime configuration */ + struct dma_slave_config slave; + + /* fifo storage */ + struct bam_desc_hw *fifo_virt; + dma_addr_t fifo_phys; + + /* fifo markers */ + unsigned short head; /* start of active descriptor entries */ + unsigned short tail; /* end of active descriptor entries */ + + unsigned int initialized; /* is the channel hw initialized? */ + unsigned int paused; /* is the channel paused? */ + unsigned int reconfigure; /* new slave config? */ + + struct list_head node; +}; + +static inline struct bam_chan *to_bam_chan(struct dma_chan *common) +{ + return container_of(common, struct bam_chan, vc.chan); +} + +struct bam_device { + void __iomem *regs; + struct device *dev; + struct dma_device common; + struct device_dma_parameters dma_parms; + struct bam_chan *channels; + u32 num_channels; + + /* execution environment ID, from DT */ + u32 ee; + + struct clk *bamclk; + int irq; + + /* dma start transaction tasklet */ + struct tasklet_struct task; +}; + +/** + * bam_reset_channel - Reset individual BAM DMA channel + * @bchan: bam channel + * + * This function resets a specific BAM channel + */ +static void bam_reset_channel(struct bam_chan *bchan) +{ + struct bam_device *bdev = bchan->bdev; + + lockdep_assert_held(&bchan->vc.lock); + + /* reset channel */ + writel_relaxed(1, bdev->regs + BAM_P_RST(bchan->id)); + writel_relaxed(0, bdev->regs + BAM_P_RST(bchan->id)); + + /* don't allow cpu to reorder BAM register accesses done after this */ + wmb(); + + /* make sure hw is initialized when channel is used the first time */ + bchan->initialized = 0; +} + +/** + * bam_chan_init_hw - Initialize channel hardware + * @bchan: bam channel + * + * This function resets and initializes the BAM channel + */ +static void bam_chan_init_hw(struct bam_chan *bchan, + enum dma_transfer_direction dir) +{ + struct bam_device *bdev = bchan->bdev; + u32 val; + + /* Reset the channel to clear internal state of the FIFO */ + bam_reset_channel(bchan); + + /* + * write out 8 byte aligned address. We have enough space for this + * because we allocated 1 more descriptor (8 bytes) than we can use + */ + writel_relaxed(ALIGN(bchan->fifo_phys, sizeof(struct bam_desc_hw)), + bdev->regs + BAM_P_DESC_FIFO_ADDR(bchan->id)); + writel_relaxed(BAM_DESC_FIFO_SIZE, bdev->regs + + BAM_P_FIFO_SIZES(bchan->id)); + + /* enable the per pipe interrupts, enable EOT, ERR, and INT irqs */ + writel_relaxed(P_DEFAULT_IRQS_EN, bdev->regs + BAM_P_IRQ_EN(bchan->id)); + + /* unmask the specific pipe and EE combo */ + val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); + val |= BIT(bchan->id); + writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); + + /* don't allow cpu to reorder the channel enable done below */ + wmb(); + + /* set fixed direction and mode, then enable channel */ + val = P_EN | P_SYS_MODE; + if (dir == DMA_DEV_TO_MEM) + val |= P_DIRECTION; + + writel_relaxed(val, bdev->regs + BAM_P_CTRL(bchan->id)); + + bchan->initialized = 1; + + /* init FIFO pointers */ + bchan->head = 0; + bchan->tail = 0; +} + +/** + * bam_alloc_chan - Allocate channel resources for DMA channel. + * @chan: specified channel + * + * This function allocates the FIFO descriptor memory + */ +static int bam_alloc_chan(struct dma_chan *chan) +{ + struct bam_chan *bchan = to_bam_chan(chan); + struct bam_device *bdev = bchan->bdev; + + if (bchan->fifo_virt) + return 0; + + /* allocate FIFO descriptor space, but only if necessary */ + bchan->fifo_virt = dma_alloc_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, + &bchan->fifo_phys, GFP_KERNEL); + + if (!bchan->fifo_virt) { + dev_err(bdev->dev, "Failed to allocate desc fifo\n"); + return -ENOMEM; + } + + return 0; +} + +/** + * bam_free_chan - Frees dma resources associated with specific channel + * @chan: specified channel + * + * Free the allocated fifo descriptor memory and channel resources + * + */ +static void bam_free_chan(struct dma_chan *chan) +{ + struct bam_chan *bchan = to_bam_chan(chan); + struct bam_device *bdev = bchan->bdev; + u32 val; + unsigned long flags; + + vchan_free_chan_resources(to_virt_chan(chan)); + + if (bchan->curr_txd) { + dev_err(bchan->bdev->dev, "Cannot free busy channel\n"); + return; + } + + spin_lock_irqsave(&bchan->vc.lock, flags); + bam_reset_channel(bchan); + spin_unlock_irqrestore(&bchan->vc.lock, flags); + + dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, bchan->fifo_virt, + bchan->fifo_phys); + bchan->fifo_virt = NULL; + + /* mask irq for pipe/channel */ + val = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); + val &= ~BIT(bchan->id); + writel_relaxed(val, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); + + /* disable irq */ + writel_relaxed(0, bdev->regs + BAM_P_IRQ_EN(bchan->id)); +} + +/** + * bam_slave_config - set slave configuration for channel + * @chan: dma channel + * @cfg: slave configuration + * + * Sets slave configuration for channel + * + */ +static void bam_slave_config(struct bam_chan *bchan, + struct dma_slave_config *cfg) +{ + memcpy(&bchan->slave, cfg, sizeof(*cfg)); + bchan->reconfigure = 1; +} + +/** + * bam_prep_slave_sg - Prep slave sg transaction + * + * @chan: dma channel + * @sgl: scatter gather list + * @sg_len: length of sg + * @direction: DMA transfer direction + * @flags: DMA flags + * @context: transfer context (unused) + */ +static struct dma_async_tx_descriptor *bam_prep_slave_sg(struct dma_chan *chan, + struct scatterlist *sgl, unsigned int sg_len, + enum dma_transfer_direction direction, unsigned long flags, + void *context) +{ + struct bam_chan *bchan = to_bam_chan(chan); + struct bam_device *bdev = bchan->bdev; + struct bam_async_desc *async_desc; + struct scatterlist *sg; + u32 i; + struct bam_desc_hw *desc; + unsigned int num_alloc = 0; + + + if (!is_slave_direction(direction)) { + dev_err(bdev->dev, "invalid dma direction\n"); + return NULL; + } + + /* calculate number of required entries */ + for_each_sg(sgl, sg, sg_len, i) + num_alloc += DIV_ROUND_UP(sg_dma_len(sg), BAM_MAX_DATA_SIZE); + + /* allocate enough room to accomodate the number of entries */ + async_desc = kzalloc(sizeof(*async_desc) + + (num_alloc * sizeof(struct bam_desc_hw)), GFP_NOWAIT); + + if (!async_desc) + goto err_out; + + async_desc->num_desc = num_alloc; + async_desc->curr_desc = async_desc->desc; + async_desc->dir = direction; + + /* fill in temporary descriptors */ + desc = async_desc->desc; + for_each_sg(sgl, sg, sg_len, i) { + unsigned int remainder = sg_dma_len(sg); + unsigned int curr_offset = 0; + + do { + desc->addr = sg_dma_address(sg) + curr_offset; + + if (remainder > BAM_MAX_DATA_SIZE) { + desc->size = BAM_MAX_DATA_SIZE; + remainder -= BAM_MAX_DATA_SIZE; + curr_offset += BAM_MAX_DATA_SIZE; + } else { + desc->size = remainder; + remainder = 0; + } + + async_desc->length += desc->size; + desc++; + } while (remainder > 0); + } + + return vchan_tx_prep(&bchan->vc, &async_desc->vd, flags); + +err_out: + kfree(async_desc); + return NULL; +} + +/** + * bam_dma_terminate_all - terminate all transactions on a channel + * @bchan: bam dma channel + * + * Dequeues and frees all transactions + * No callbacks are done + * + */ +static void bam_dma_terminate_all(struct bam_chan *bchan) +{ + unsigned long flag; + LIST_HEAD(head); + + /* remove all transactions, including active transaction */ + spin_lock_irqsave(&bchan->vc.lock, flag); + if (bchan->curr_txd) { + list_add(&bchan->curr_txd->vd.node, &bchan->vc.desc_issued); + bchan->curr_txd = NULL; + } + + vchan_get_all_descriptors(&bchan->vc, &head); + spin_unlock_irqrestore(&bchan->vc.lock, flag); + + vchan_dma_desc_free_list(&bchan->vc, &head); +} + +/** + * bam_control - DMA device control + * @chan: dma channel + * @cmd: control cmd + * @arg: cmd argument + * + * Perform DMA control command + * + */ +static int bam_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + unsigned long arg) +{ + struct bam_chan *bchan = to_bam_chan(chan); + struct bam_device *bdev = bchan->bdev; + int ret = 0; + unsigned long flag; + + switch (cmd) { + case DMA_PAUSE: + spin_lock_irqsave(&bchan->vc.lock, flag); + writel_relaxed(1, bdev->regs + BAM_P_HALT(bchan->id)); + bchan->paused = 1; + spin_unlock_irqrestore(&bchan->vc.lock, flag); + break; + + case DMA_RESUME: + spin_lock_irqsave(&bchan->vc.lock, flag); + writel_relaxed(0, bdev->regs + BAM_P_HALT(bchan->id)); + bchan->paused = 0; + spin_unlock_irqrestore(&bchan->vc.lock, flag); + break; + + case DMA_TERMINATE_ALL: + bam_dma_terminate_all(bchan); + break; + + case DMA_SLAVE_CONFIG: + spin_lock_irqsave(&bchan->vc.lock, flag); + bam_slave_config(bchan, (struct dma_slave_config *)arg); + spin_unlock_irqrestore(&bchan->vc.lock, flag); + break; + + default: + ret = -ENXIO; + break; + } + + return ret; +} + +/** + * process_channel_irqs - processes the channel interrupts + * @bdev: bam controller + * + * This function processes the channel interrupts + * + */ +static u32 process_channel_irqs(struct bam_device *bdev) +{ + u32 i, srcs, pipe_stts; + unsigned long flags; + struct bam_async_desc *async_desc; + + srcs = readl_relaxed(bdev->regs + BAM_IRQ_SRCS_EE(bdev->ee)); + + /* return early if no pipe/channel interrupts are present */ + if (!(srcs & P_IRQ)) + return srcs; + + for (i = 0; i < bdev->num_channels; i++) { + struct bam_chan *bchan = &bdev->channels[i]; + + if (!(srcs & BIT(i))) + continue; + + /* clear pipe irq */ + pipe_stts = readl_relaxed(bdev->regs + + BAM_P_IRQ_STTS(i)); + + writel_relaxed(pipe_stts, bdev->regs + + BAM_P_IRQ_CLR(i)); + + spin_lock_irqsave(&bchan->vc.lock, flags); + async_desc = bchan->curr_txd; + + if (async_desc) { + async_desc->num_desc -= async_desc->xfer_len; + async_desc->curr_desc += async_desc->xfer_len; + bchan->curr_txd = NULL; + + /* manage FIFO */ + bchan->head += async_desc->xfer_len; + bchan->head %= MAX_DESCRIPTORS; + + /* + * if complete, process cookie. Otherwise + * push back to front of desc_issued so that + * it gets restarted by the tasklet + */ + if (!async_desc->num_desc) + vchan_cookie_complete(&async_desc->vd); + else + list_add(&async_desc->vd.node, + &bchan->vc.desc_issued); + } + + spin_unlock_irqrestore(&bchan->vc.lock, flags); + } + + return srcs; +} + +/** + * bam_dma_irq - irq handler for bam controller + * @irq: IRQ of interrupt + * @data: callback data + * + * IRQ handler for the bam controller + */ +static irqreturn_t bam_dma_irq(int irq, void *data) +{ + struct bam_device *bdev = data; + u32 clr_mask = 0, srcs = 0; + + srcs |= process_channel_irqs(bdev); + + /* kick off tasklet to start next dma transfer */ + if (srcs & P_IRQ) + tasklet_schedule(&bdev->task); + + if (srcs & BAM_IRQ) + clr_mask = readl_relaxed(bdev->regs + BAM_IRQ_STTS); + + /* don't allow reorder of the various accesses to the BAM registers */ + mb(); + + writel_relaxed(clr_mask, bdev->regs + BAM_IRQ_CLR); + + return IRQ_HANDLED; +} + +/** + * bam_tx_status - returns status of transaction + * @chan: dma channel + * @cookie: transaction cookie + * @txstate: DMA transaction state + * + * Return status of dma transaction + */ +static enum dma_status bam_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct bam_chan *bchan = to_bam_chan(chan); + struct virt_dma_desc *vd; + int ret; + size_t residue = 0; + unsigned int i; + unsigned long flags; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE) + return ret; + + if (!txstate) + return bchan->paused ? DMA_PAUSED : ret; + + spin_lock_irqsave(&bchan->vc.lock, flags); + vd = vchan_find_desc(&bchan->vc, cookie); + if (vd) + residue = container_of(vd, struct bam_async_desc, vd)->length; + else if (bchan->curr_txd && bchan->curr_txd->vd.tx.cookie == cookie) + for (i = 0; i < bchan->curr_txd->num_desc; i++) + residue += bchan->curr_txd->curr_desc[i].size; + + spin_unlock_irqrestore(&bchan->vc.lock, flags); + + dma_set_residue(txstate, residue); + + if (ret == DMA_IN_PROGRESS && bchan->paused) + ret = DMA_PAUSED; + + return ret; +} + +/** + * bam_apply_new_config + * @bchan: bam dma channel + * @dir: DMA direction + */ +static void bam_apply_new_config(struct bam_chan *bchan, + enum dma_transfer_direction dir) +{ + struct bam_device *bdev = bchan->bdev; + u32 maxburst; + + if (dir == DMA_DEV_TO_MEM) + maxburst = bchan->slave.src_maxburst; + else + maxburst = bchan->slave.dst_maxburst; + + writel_relaxed(maxburst, bdev->regs + BAM_DESC_CNT_TRSHLD); + + bchan->reconfigure = 0; +} + +/** + * bam_start_dma - start next transaction + * @bchan - bam dma channel + */ +static void bam_start_dma(struct bam_chan *bchan) +{ + struct virt_dma_desc *vd = vchan_next_desc(&bchan->vc); + struct bam_device *bdev = bchan->bdev; + struct bam_async_desc *async_desc; + struct bam_desc_hw *desc; + struct bam_desc_hw *fifo = PTR_ALIGN(bchan->fifo_virt, + sizeof(struct bam_desc_hw)); + + lockdep_assert_held(&bchan->vc.lock); + + if (!vd) + return; + + list_del(&vd->node); + + async_desc = container_of(vd, struct bam_async_desc, vd); + bchan->curr_txd = async_desc; + + /* on first use, initialize the channel hardware */ + if (!bchan->initialized) + bam_chan_init_hw(bchan, async_desc->dir); + + /* apply new slave config changes, if necessary */ + if (bchan->reconfigure) + bam_apply_new_config(bchan, async_desc->dir); + + desc = bchan->curr_txd->curr_desc; + + if (async_desc->num_desc > MAX_DESCRIPTORS) + async_desc->xfer_len = MAX_DESCRIPTORS; + else + async_desc->xfer_len = async_desc->num_desc; + + /* set INT on last descriptor */ + desc[async_desc->xfer_len - 1].flags |= DESC_FLAG_INT; + + if (bchan->tail + async_desc->xfer_len > MAX_DESCRIPTORS) { + u32 partial = MAX_DESCRIPTORS - bchan->tail; + + memcpy(&fifo[bchan->tail], desc, + partial * sizeof(struct bam_desc_hw)); + memcpy(fifo, &desc[partial], (async_desc->xfer_len - partial) * + sizeof(struct bam_desc_hw)); + } else { + memcpy(&fifo[bchan->tail], desc, + async_desc->xfer_len * sizeof(struct bam_desc_hw)); + } + + bchan->tail += async_desc->xfer_len; + bchan->tail %= MAX_DESCRIPTORS; + + /* ensure descriptor writes and dma start not reordered */ + wmb(); + writel_relaxed(bchan->tail * sizeof(struct bam_desc_hw), + bdev->regs + BAM_P_EVNT_REG(bchan->id)); +} + +/** + * dma_tasklet - DMA IRQ tasklet + * @data: tasklet argument (bam controller structure) + * + * Sets up next DMA operation and then processes all completed transactions + */ +static void dma_tasklet(unsigned long data) +{ + struct bam_device *bdev = (struct bam_device *)data; + struct bam_chan *bchan; + unsigned long flags; + unsigned int i; + + /* go through the channels and kick off transactions */ + for (i = 0; i < bdev->num_channels; i++) { + bchan = &bdev->channels[i]; + spin_lock_irqsave(&bchan->vc.lock, flags); + + if (!list_empty(&bchan->vc.desc_issued) && !bchan->curr_txd) + bam_start_dma(bchan); + spin_unlock_irqrestore(&bchan->vc.lock, flags); + } +} + +/** + * bam_issue_pending - starts pending transactions + * @chan: dma channel + * + * Calls tasklet directly which in turn starts any pending transactions + */ +static void bam_issue_pending(struct dma_chan *chan) +{ + struct bam_chan *bchan = to_bam_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&bchan->vc.lock, flags); + + /* if work pending and idle, start a transaction */ + if (vchan_issue_pending(&bchan->vc) && !bchan->curr_txd) + bam_start_dma(bchan); + + spin_unlock_irqrestore(&bchan->vc.lock, flags); +} + +/** + * bam_dma_free_desc - free descriptor memory + * @vd: virtual descriptor + * + */ +static void bam_dma_free_desc(struct virt_dma_desc *vd) +{ + struct bam_async_desc *async_desc = container_of(vd, + struct bam_async_desc, vd); + + kfree(async_desc); +} + +static struct dma_chan *bam_dma_xlate(struct of_phandle_args *dma_spec, + struct of_dma *of) +{ + struct bam_device *bdev = container_of(of->of_dma_data, + struct bam_device, common); + unsigned int request; + + if (dma_spec->args_count != 1) + return NULL; + + request = dma_spec->args[0]; + if (request >= bdev->num_channels) + return NULL; + + return dma_get_slave_channel(&(bdev->channels[request].vc.chan)); +} + +/** + * bam_init + * @bdev: bam device + * + * Initialization helper for global bam registers + */ +static int bam_init(struct bam_device *bdev) +{ + u32 val; + + /* read revision and configuration information */ + val = readl_relaxed(bdev->regs + BAM_REVISION) >> NUM_EES_SHIFT; + val &= NUM_EES_MASK; + + /* check that configured EE is within range */ + if (bdev->ee >= val) + return -EINVAL; + + val = readl_relaxed(bdev->regs + BAM_NUM_PIPES); + bdev->num_channels = val & BAM_NUM_PIPES_MASK; + + /* s/w reset bam */ + /* after reset all pipes are disabled and idle */ + val = readl_relaxed(bdev->regs + BAM_CTRL); + val |= BAM_SW_RST; + writel_relaxed(val, bdev->regs + BAM_CTRL); + val &= ~BAM_SW_RST; + writel_relaxed(val, bdev->regs + BAM_CTRL); + + /* make sure previous stores are visible before enabling BAM */ + wmb(); + + /* enable bam */ + val |= BAM_EN; + writel_relaxed(val, bdev->regs + BAM_CTRL); + + /* set descriptor threshhold, start with 4 bytes */ + writel_relaxed(DEFAULT_CNT_THRSHLD, bdev->regs + BAM_DESC_CNT_TRSHLD); + + /* Enable default set of h/w workarounds, ie all except BAM_FULL_PIPE */ + writel_relaxed(BAM_CNFG_BITS_DEFAULT, bdev->regs + BAM_CNFG_BITS); + + /* enable irqs for errors */ + writel_relaxed(BAM_ERROR_EN | BAM_HRESP_ERR_EN, + bdev->regs + BAM_IRQ_EN); + + /* unmask global bam interrupt */ + writel_relaxed(BAM_IRQ_MSK, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); + + return 0; +} + +static void bam_channel_init(struct bam_device *bdev, struct bam_chan *bchan, + u32 index) +{ + bchan->id = index; + bchan->bdev = bdev; + + vchan_init(&bchan->vc, &bdev->common); + bchan->vc.desc_free = bam_dma_free_desc; +} + +static int bam_dma_probe(struct platform_device *pdev) +{ + struct bam_device *bdev; + struct resource *iores; + int ret, i; + + bdev = devm_kzalloc(&pdev->dev, sizeof(*bdev), GFP_KERNEL); + if (!bdev) + return -ENOMEM; + + bdev->dev = &pdev->dev; + + iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); + bdev->regs = devm_ioremap_resource(&pdev->dev, iores); + if (IS_ERR(bdev->regs)) + return PTR_ERR(bdev->regs); + + bdev->irq = platform_get_irq(pdev, 0); + if (bdev->irq < 0) + return bdev->irq; + + ret = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &bdev->ee); + if (ret) { + dev_err(bdev->dev, "Execution environment unspecified\n"); + return ret; + } + + bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk"); + if (IS_ERR(bdev->bamclk)) + return PTR_ERR(bdev->bamclk); + + ret = clk_prepare_enable(bdev->bamclk); + if (ret) { + dev_err(bdev->dev, "failed to prepare/enable clock\n"); + return ret; + } + + ret = bam_init(bdev); + if (ret) + goto err_disable_clk; + + tasklet_init(&bdev->task, dma_tasklet, (unsigned long)bdev); + + bdev->channels = devm_kcalloc(bdev->dev, bdev->num_channels, + sizeof(*bdev->channels), GFP_KERNEL); + + if (!bdev->channels) { + ret = -ENOMEM; + goto err_disable_clk; + } + + /* allocate and initialize channels */ + INIT_LIST_HEAD(&bdev->common.channels); + + for (i = 0; i < bdev->num_channels; i++) + bam_channel_init(bdev, &bdev->channels[i], i); + + ret = devm_request_irq(bdev->dev, bdev->irq, bam_dma_irq, + IRQF_TRIGGER_HIGH, "bam_dma", bdev); + if (ret) + goto err_disable_clk; + + /* set max dma segment size */ + bdev->common.dev = bdev->dev; + bdev->common.dev->dma_parms = &bdev->dma_parms; + ret = dma_set_max_seg_size(bdev->common.dev, BAM_MAX_DATA_SIZE); + if (ret) { + dev_err(bdev->dev, "cannot set maximum segment size\n"); + goto err_disable_clk; + } + + platform_set_drvdata(pdev, bdev); + + /* set capabilities */ + dma_cap_zero(bdev->common.cap_mask); + dma_cap_set(DMA_SLAVE, bdev->common.cap_mask); + + /* initialize dmaengine apis */ + bdev->common.device_alloc_chan_resources = bam_alloc_chan; + bdev->common.device_free_chan_resources = bam_free_chan; + bdev->common.device_prep_slave_sg = bam_prep_slave_sg; + bdev->common.device_control = bam_control; + bdev->common.device_issue_pending = bam_issue_pending; + bdev->common.device_tx_status = bam_tx_status; + bdev->common.dev = bdev->dev; + + ret = dma_async_device_register(&bdev->common); + if (ret) { + dev_err(bdev->dev, "failed to register dma async device\n"); + goto err_disable_clk; + } + + ret = of_dma_controller_register(pdev->dev.of_node, bam_dma_xlate, + &bdev->common); + if (ret) + goto err_unregister_dma; + + return 0; + +err_unregister_dma: + dma_async_device_unregister(&bdev->common); +err_disable_clk: + clk_disable_unprepare(bdev->bamclk); + return ret; +} + +static int bam_dma_remove(struct platform_device *pdev) +{ + struct bam_device *bdev = platform_get_drvdata(pdev); + u32 i; + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&bdev->common); + + /* mask all interrupts for this execution environment */ + writel_relaxed(0, bdev->regs + BAM_IRQ_SRCS_MSK_EE(bdev->ee)); + + devm_free_irq(bdev->dev, bdev->irq, bdev); + + for (i = 0; i < bdev->num_channels; i++) { + bam_dma_terminate_all(&bdev->channels[i]); + tasklet_kill(&bdev->channels[i].vc.task); + + dma_free_writecombine(bdev->dev, BAM_DESC_FIFO_SIZE, + bdev->channels[i].fifo_virt, + bdev->channels[i].fifo_phys); + } + + tasklet_kill(&bdev->task); + + clk_disable_unprepare(bdev->bamclk); + + return 0; +} + +static const struct of_device_id bam_of_match[] = { + { .compatible = "qcom,bam-v1.4.0", }, + {} +}; +MODULE_DEVICE_TABLE(of, bam_of_match); + +static struct platform_driver bam_dma_driver = { + .probe = bam_dma_probe, + .remove = bam_dma_remove, + .driver = { + .name = "bam-dma-engine", + .owner = THIS_MODULE, + .of_match_table = bam_of_match, + }, +}; + +module_platform_driver(bam_dma_driver); + +MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); +MODULE_DESCRIPTION("QCOM BAM DMA engine driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 4eddedb6eb7d..b209a0f17344 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -192,7 +192,7 @@ struct s3c24xx_dma_phy { unsigned int id; bool valid; void __iomem *base; - unsigned int irq; + int irq; struct clk *clk; spinlock_t lock; struct s3c24xx_dma_chan *serving; diff --git a/drivers/dma/sh/Kconfig b/drivers/dma/sh/Kconfig index dadd9e010c0b..b4c813831006 100644 --- a/drivers/dma/sh/Kconfig +++ b/drivers/dma/sh/Kconfig @@ -29,6 +29,12 @@ config RCAR_HPB_DMAE help Enable support for the Renesas R-Car series DMA controllers. +config RCAR_AUDMAC_PP + tristate "Renesas R-Car Audio DMAC Peripheral Peripheral support" + depends on SH_DMAE_BASE + help + Enable support for the Renesas R-Car Audio DMAC Peripheral Peripheral controllers. + config SHDMA_R8A73A4 def_bool y depends on ARCH_R8A73A4 && SH_DMAE != n diff --git a/drivers/dma/sh/Makefile b/drivers/dma/sh/Makefile index e856af23b789..1ce88b28cfc6 100644 --- a/drivers/dma/sh/Makefile +++ b/drivers/dma/sh/Makefile @@ -7,3 +7,4 @@ endif shdma-objs := $(shdma-y) obj-$(CONFIG_SUDMAC) += sudmac.o obj-$(CONFIG_RCAR_HPB_DMAE) += rcar-hpbdma.o +obj-$(CONFIG_RCAR_AUDMAC_PP) += rcar-audmapp.o diff --git a/drivers/dma/sh/rcar-audmapp.c b/drivers/dma/sh/rcar-audmapp.c new file mode 100644 index 000000000000..2de77289a2e9 --- /dev/null +++ b/drivers/dma/sh/rcar-audmapp.c @@ -0,0 +1,320 @@ +/* + * This is for Renesas R-Car Audio-DMAC-peri-peri. + * + * Copyright (C) 2014 Renesas Electronics Corporation + * Copyright (C) 2014 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> + * + * based on the drivers/dma/sh/shdma.c + * + * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de> + * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com> + * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved. + * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved. + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + */ +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/dmaengine.h> +#include <linux/platform_data/dma-rcar-audmapp.h> +#include <linux/platform_device.h> +#include <linux/shdma-base.h> + +/* + * DMA register + */ +#define PDMASAR 0x00 +#define PDMADAR 0x04 +#define PDMACHCR 0x0c + +/* PDMACHCR */ +#define PDMACHCR_DE (1 << 0) + +#define AUDMAPP_MAX_CHANNELS 29 + +/* Default MEMCPY transfer size = 2^2 = 4 bytes */ +#define LOG2_DEFAULT_XFER_SIZE 2 +#define AUDMAPP_SLAVE_NUMBER 256 +#define AUDMAPP_LEN_MAX (16 * 1024 * 1024) + +struct audmapp_chan { + struct shdma_chan shdma_chan; + struct audmapp_slave_config *config; + void __iomem *base; +}; + +struct audmapp_device { + struct shdma_dev shdma_dev; + struct audmapp_pdata *pdata; + struct device *dev; + void __iomem *chan_reg; +}; + +#define to_chan(chan) container_of(chan, struct audmapp_chan, shdma_chan) +#define to_dev(chan) container_of(chan->shdma_chan.dma_chan.device, \ + struct audmapp_device, shdma_dev.dma_dev) + +static void audmapp_write(struct audmapp_chan *auchan, u32 data, u32 reg) +{ + struct audmapp_device *audev = to_dev(auchan); + struct device *dev = audev->dev; + + dev_dbg(dev, "w %p : %08x\n", auchan->base + reg, data); + + iowrite32(data, auchan->base + reg); +} + +static u32 audmapp_read(struct audmapp_chan *auchan, u32 reg) +{ + return ioread32(auchan->base + reg); +} + +static void audmapp_halt(struct shdma_chan *schan) +{ + struct audmapp_chan *auchan = to_chan(schan); + int i; + + audmapp_write(auchan, 0, PDMACHCR); + + for (i = 0; i < 1024; i++) { + if (0 == audmapp_read(auchan, PDMACHCR)) + return; + udelay(1); + } +} + +static void audmapp_start_xfer(struct shdma_chan *schan, + struct shdma_desc *sdecs) +{ + struct audmapp_chan *auchan = to_chan(schan); + struct audmapp_device *audev = to_dev(auchan); + struct audmapp_slave_config *cfg = auchan->config; + struct device *dev = audev->dev; + u32 chcr = cfg->chcr | PDMACHCR_DE; + + dev_dbg(dev, "src/dst/chcr = %pad/%pad/%x\n", + &cfg->src, &cfg->dst, cfg->chcr); + + audmapp_write(auchan, cfg->src, PDMASAR); + audmapp_write(auchan, cfg->dst, PDMADAR); + audmapp_write(auchan, chcr, PDMACHCR); +} + +static struct audmapp_slave_config * +audmapp_find_slave(struct audmapp_chan *auchan, int slave_id) +{ + struct audmapp_device *audev = to_dev(auchan); + struct audmapp_pdata *pdata = audev->pdata; + struct audmapp_slave_config *cfg; + int i; + + if (slave_id >= AUDMAPP_SLAVE_NUMBER) + return NULL; + + for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++) + if (cfg->slave_id == slave_id) + return cfg; + + return NULL; +} + +static int audmapp_set_slave(struct shdma_chan *schan, int slave_id, + dma_addr_t slave_addr, bool try) +{ + struct audmapp_chan *auchan = to_chan(schan); + struct audmapp_slave_config *cfg = + audmapp_find_slave(auchan, slave_id); + + if (!cfg) + return -ENODEV; + if (try) + return 0; + + auchan->config = cfg; + + return 0; +} + +static int audmapp_desc_setup(struct shdma_chan *schan, + struct shdma_desc *sdecs, + dma_addr_t src, dma_addr_t dst, size_t *len) +{ + struct audmapp_chan *auchan = to_chan(schan); + struct audmapp_slave_config *cfg = auchan->config; + + if (!cfg) + return -ENODEV; + + if (*len > (size_t)AUDMAPP_LEN_MAX) + *len = (size_t)AUDMAPP_LEN_MAX; + + return 0; +} + +static void audmapp_setup_xfer(struct shdma_chan *schan, + int slave_id) +{ +} + +static dma_addr_t audmapp_slave_addr(struct shdma_chan *schan) +{ + return 0; /* always fixed address */ +} + +static bool audmapp_channel_busy(struct shdma_chan *schan) +{ + struct audmapp_chan *auchan = to_chan(schan); + u32 chcr = audmapp_read(auchan, PDMACHCR); + + return chcr & ~PDMACHCR_DE; +} + +static bool audmapp_desc_completed(struct shdma_chan *schan, + struct shdma_desc *sdesc) +{ + return true; +} + +static struct shdma_desc *audmapp_embedded_desc(void *buf, int i) +{ + return &((struct shdma_desc *)buf)[i]; +} + +static const struct shdma_ops audmapp_shdma_ops = { + .halt_channel = audmapp_halt, + .desc_setup = audmapp_desc_setup, + .set_slave = audmapp_set_slave, + .start_xfer = audmapp_start_xfer, + .embedded_desc = audmapp_embedded_desc, + .setup_xfer = audmapp_setup_xfer, + .slave_addr = audmapp_slave_addr, + .channel_busy = audmapp_channel_busy, + .desc_completed = audmapp_desc_completed, +}; + +static int audmapp_chan_probe(struct platform_device *pdev, + struct audmapp_device *audev, int id) +{ + struct shdma_dev *sdev = &audev->shdma_dev; + struct audmapp_chan *auchan; + struct shdma_chan *schan; + struct device *dev = audev->dev; + + auchan = devm_kzalloc(dev, sizeof(*auchan), GFP_KERNEL); + if (!auchan) + return -ENOMEM; + + schan = &auchan->shdma_chan; + schan->max_xfer_len = AUDMAPP_LEN_MAX; + + shdma_chan_probe(sdev, schan, id); + + auchan->base = audev->chan_reg + 0x20 + (0x10 * id); + dev_dbg(dev, "%02d : %p / %p", id, auchan->base, audev->chan_reg); + + return 0; +} + +static void audmapp_chan_remove(struct audmapp_device *audev) +{ + struct dma_device *dma_dev = &audev->shdma_dev.dma_dev; + struct shdma_chan *schan; + int i; + + shdma_for_each_chan(schan, &audev->shdma_dev, i) { + BUG_ON(!schan); + shdma_chan_remove(schan); + } + dma_dev->chancnt = 0; +} + +static int audmapp_probe(struct platform_device *pdev) +{ + struct audmapp_pdata *pdata = pdev->dev.platform_data; + struct audmapp_device *audev; + struct shdma_dev *sdev; + struct dma_device *dma_dev; + struct resource *res; + int err, i; + + if (!pdata) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + audev = devm_kzalloc(&pdev->dev, sizeof(*audev), GFP_KERNEL); + if (!audev) + return -ENOMEM; + + audev->dev = &pdev->dev; + audev->pdata = pdata; + audev->chan_reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(audev->chan_reg)) + return PTR_ERR(audev->chan_reg); + + sdev = &audev->shdma_dev; + sdev->ops = &audmapp_shdma_ops; + sdev->desc_size = sizeof(struct shdma_desc); + + dma_dev = &sdev->dma_dev; + dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE; + dma_cap_set(DMA_SLAVE, dma_dev->cap_mask); + + err = shdma_init(&pdev->dev, sdev, AUDMAPP_MAX_CHANNELS); + if (err < 0) + return err; + + platform_set_drvdata(pdev, audev); + + /* Create DMA Channel */ + for (i = 0; i < AUDMAPP_MAX_CHANNELS; i++) { + err = audmapp_chan_probe(pdev, audev, i); + if (err) + goto chan_probe_err; + } + + err = dma_async_device_register(dma_dev); + if (err < 0) + goto chan_probe_err; + + return err; + +chan_probe_err: + audmapp_chan_remove(audev); + shdma_cleanup(sdev); + + return err; +} + +static int audmapp_remove(struct platform_device *pdev) +{ + struct audmapp_device *audev = platform_get_drvdata(pdev); + struct dma_device *dma_dev = &audev->shdma_dev.dma_dev; + + dma_async_device_unregister(dma_dev); + + audmapp_chan_remove(audev); + shdma_cleanup(&audev->shdma_dev); + + return 0; +} + +static struct platform_driver audmapp_driver = { + .probe = audmapp_probe, + .remove = audmapp_remove, + .driver = { + .owner = THIS_MODULE, + .name = "rcar-audmapp-engine", + }, +}; +module_platform_driver(audmapp_driver); + +MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>"); +MODULE_DESCRIPTION("Renesas R-Car Audio DMAC peri-peri driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 2e7b394def80..52396771acbe 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -227,7 +227,7 @@ bool shdma_chan_filter(struct dma_chan *chan, void *arg) struct shdma_chan *schan = to_shdma_chan(chan); struct shdma_dev *sdev = to_shdma_dev(schan->dma_chan.device); const struct shdma_ops *ops = sdev->ops; - int match = (int)arg; + int match = (long)arg; int ret; if (match < 0) @@ -491,8 +491,8 @@ static struct shdma_desc *shdma_add_desc(struct shdma_chan *schan, } dev_dbg(schan->dev, - "chaining (%u/%u)@%x -> %x with %p, cookie %d\n", - copy_size, *len, *src, *dst, &new->async_tx, + "chaining (%zu/%zu)@%pad -> %pad with %p, cookie %d\n", + copy_size, *len, src, dst, &new->async_tx, new->async_tx.cookie); new->mark = DESC_PREPARED; @@ -555,8 +555,8 @@ static struct dma_async_tx_descriptor *shdma_prep_sg(struct shdma_chan *schan, goto err_get_desc; do { - dev_dbg(schan->dev, "Add SG #%d@%p[%d], dma %llx\n", - i, sg, len, (unsigned long long)sg_addr); + dev_dbg(schan->dev, "Add SG #%d@%p[%zu], dma %pad\n", + i, sg, len, &sg_addr); if (direction == DMA_DEV_TO_MEM) new = shdma_add_desc(schan, flags, diff --git a/drivers/dma/sh/shdma-of.c b/drivers/dma/sh/shdma-of.c index 06473a05fe4e..b4ff9d3e56d1 100644 --- a/drivers/dma/sh/shdma-of.c +++ b/drivers/dma/sh/shdma-of.c @@ -33,7 +33,8 @@ static struct dma_chan *shdma_of_xlate(struct of_phandle_args *dma_spec, /* Only slave DMA channels can be allocated via DT */ dma_cap_set(DMA_SLAVE, mask); - chan = dma_request_channel(mask, shdma_chan_filter, (void *)id); + chan = dma_request_channel(mask, shdma_chan_filter, + (void *)(uintptr_t)id); if (chan) to_shdma_chan(chan)->hw_req = id; diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index 0d765c0e21ec..dda7e7563f5d 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -443,6 +443,7 @@ static bool sh_dmae_reset(struct sh_dmae_device *shdev) return ret; } +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM) static irqreturn_t sh_dmae_err(int irq, void *data) { struct sh_dmae_device *shdev = data; @@ -453,6 +454,7 @@ static irqreturn_t sh_dmae_err(int irq, void *data) sh_dmae_reset(shdev); return IRQ_HANDLED; } +#endif static bool sh_dmae_desc_completed(struct shdma_chan *schan, struct shdma_desc *sdesc) @@ -637,7 +639,7 @@ static int sh_dmae_resume(struct device *dev) #define sh_dmae_resume NULL #endif -const struct dev_pm_ops sh_dmae_pm = { +static const struct dev_pm_ops sh_dmae_pm = { .suspend = sh_dmae_suspend, .resume = sh_dmae_resume, .runtime_suspend = sh_dmae_runtime_suspend, @@ -685,9 +687,12 @@ MODULE_DEVICE_TABLE(of, sh_dmae_of_match); static int sh_dmae_probe(struct platform_device *pdev) { const struct sh_dmae_pdata *pdata; - unsigned long irqflags = 0, - chan_flag[SH_DMAE_MAX_CHANNELS] = {}; - int errirq, chan_irq[SH_DMAE_MAX_CHANNELS]; + unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {}; + int chan_irq[SH_DMAE_MAX_CHANNELS]; +#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARM) + unsigned long irqflags = 0; + int errirq; +#endif int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0; struct sh_dmae_device *shdev; struct dma_device *dma_dev; diff --git a/drivers/dma/sh/sudmac.c b/drivers/dma/sh/sudmac.c index c7e9cdff0708..4e7df43b50d6 100644 --- a/drivers/dma/sh/sudmac.c +++ b/drivers/dma/sh/sudmac.c @@ -178,8 +178,8 @@ static int sudmac_desc_setup(struct shdma_chan *schan, struct sudmac_chan *sc = to_chan(schan); struct sudmac_desc *sd = to_desc(sdesc); - dev_dbg(sc->shdma_chan.dev, "%s: src=%x, dst=%x, len=%d\n", - __func__, src, dst, *len); + dev_dbg(sc->shdma_chan.dev, "%s: src=%pad, dst=%pad, len=%zu\n", + __func__, &src, &dst, *len); if (*len > schan->max_xfer_len) *len = schan->max_xfer_len; diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c index d4d3a3109b16..a1bd8298d55f 100644 --- a/drivers/dma/sirf-dma.c +++ b/drivers/dma/sirf-dma.c @@ -18,6 +18,7 @@ #include <linux/of_device.h> #include <linux/of_platform.h> #include <linux/clk.h> +#include <linux/of_dma.h> #include <linux/sirfsoc_dma.h> #include "dmaengine.h" @@ -659,6 +660,18 @@ static int sirfsoc_dma_device_slave_caps(struct dma_chan *dchan, return 0; } +static struct dma_chan *of_dma_sirfsoc_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct sirfsoc_dma *sdma = ofdma->of_dma_data; + unsigned int request = dma_spec->args[0]; + + if (request > SIRFSOC_DMA_CHANNELS) + return NULL; + + return dma_get_slave_channel(&sdma->channels[request].chan); +} + static int sirfsoc_dma_probe(struct platform_device *op) { struct device_node *dn = op->dev.of_node; @@ -764,11 +777,20 @@ static int sirfsoc_dma_probe(struct platform_device *op) if (ret) goto free_irq; + /* Device-tree DMA controller registration */ + ret = of_dma_controller_register(dn, of_dma_sirfsoc_xlate, sdma); + if (ret) { + dev_err(dev, "failed to register DMA controller\n"); + goto unreg_dma_dev; + } + pm_runtime_enable(&op->dev); dev_info(dev, "initialized SIRFSOC DMAC driver\n"); return 0; +unreg_dma_dev: + dma_async_device_unregister(dma); free_irq: free_irq(sdma->irq, sdma); irq_dispose: @@ -781,6 +803,7 @@ static int sirfsoc_dma_remove(struct platform_device *op) struct device *dev = &op->dev; struct sirfsoc_dma *sdma = dev_get_drvdata(dev); + of_dma_controller_free(op->dev.of_node); dma_async_device_unregister(&sdma->dma); free_irq(sdma->irq, sdma); irq_dispose_mapping(sdma->irq); diff --git a/drivers/firmware/efi/efi-stub-helper.c b/drivers/firmware/efi/efi-stub-helper.c index ff50aeebf0d9..2c41eaece2c1 100644 --- a/drivers/firmware/efi/efi-stub-helper.c +++ b/drivers/firmware/efi/efi-stub-helper.c @@ -397,7 +397,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, else chunksize = size; - status = efi_file_read(fh, files[j].handle, + status = efi_file_read(files[j].handle, &chunksize, (void *)addr); if (status != EFI_SUCCESS) { @@ -408,7 +408,7 @@ static efi_status_t handle_cmdline_files(efi_system_table_t *sys_table_arg, size -= chunksize; } - efi_file_close(fh, files[j].handle); + efi_file_close(files[j].handle); } } @@ -425,7 +425,7 @@ free_file_total: close_handles: for (k = j; k < i; k++) - efi_file_close(fh, files[k].handle); + efi_file_close(files[k].handle); free_files: efi_call_early(free_pool, files); fail: diff --git a/drivers/idle/intel_idle.c b/drivers/idle/intel_idle.c index 51493ed4643b..a43220c2e3d9 100644 --- a/drivers/idle/intel_idle.c +++ b/drivers/idle/intel_idle.c @@ -196,6 +196,53 @@ static struct cpuidle_state snb_cstates[] = { .enter = NULL } }; +static struct cpuidle_state byt_cstates[] = { + { + .name = "C1-BYT", + .desc = "MWAIT 0x00", + .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 1, + .target_residency = 1, + .enter = &intel_idle }, + { + .name = "C1E-BYT", + .desc = "MWAIT 0x01", + .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 15, + .target_residency = 30, + .enter = &intel_idle }, + { + .name = "C6N-BYT", + .desc = "MWAIT 0x58", + .flags = MWAIT2flg(0x58) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 40, + .target_residency = 275, + .enter = &intel_idle }, + { + .name = "C6S-BYT", + .desc = "MWAIT 0x52", + .flags = MWAIT2flg(0x52) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 140, + .target_residency = 560, + .enter = &intel_idle }, + { + .name = "C7-BYT", + .desc = "MWAIT 0x60", + .flags = MWAIT2flg(0x60) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 1200, + .target_residency = 1500, + .enter = &intel_idle }, + { + .name = "C7S-BYT", + .desc = "MWAIT 0x64", + .flags = MWAIT2flg(0x64) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 10000, + .target_residency = 20000, + .enter = &intel_idle }, + { + .enter = NULL } +}; + static struct cpuidle_state ivb_cstates[] = { { .name = "C1-IVB", @@ -236,6 +283,105 @@ static struct cpuidle_state ivb_cstates[] = { .enter = NULL } }; +static struct cpuidle_state ivt_cstates[] = { + { + .name = "C1-IVT", + .desc = "MWAIT 0x00", + .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 1, + .target_residency = 1, + .enter = &intel_idle }, + { + .name = "C1E-IVT", + .desc = "MWAIT 0x01", + .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 10, + .target_residency = 80, + .enter = &intel_idle }, + { + .name = "C3-IVT", + .desc = "MWAIT 0x10", + .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 59, + .target_residency = 156, + .enter = &intel_idle }, + { + .name = "C6-IVT", + .desc = "MWAIT 0x20", + .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 82, + .target_residency = 300, + .enter = &intel_idle }, + { + .enter = NULL } +}; + +static struct cpuidle_state ivt_cstates_4s[] = { + { + .name = "C1-IVT-4S", + .desc = "MWAIT 0x00", + .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 1, + .target_residency = 1, + .enter = &intel_idle }, + { + .name = "C1E-IVT-4S", + .desc = "MWAIT 0x01", + .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 10, + .target_residency = 250, + .enter = &intel_idle }, + { + .name = "C3-IVT-4S", + .desc = "MWAIT 0x10", + .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 59, + .target_residency = 300, + .enter = &intel_idle }, + { + .name = "C6-IVT-4S", + .desc = "MWAIT 0x20", + .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 84, + .target_residency = 400, + .enter = &intel_idle }, + { + .enter = NULL } +}; + +static struct cpuidle_state ivt_cstates_8s[] = { + { + .name = "C1-IVT-8S", + .desc = "MWAIT 0x00", + .flags = MWAIT2flg(0x00) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 1, + .target_residency = 1, + .enter = &intel_idle }, + { + .name = "C1E-IVT-8S", + .desc = "MWAIT 0x01", + .flags = MWAIT2flg(0x01) | CPUIDLE_FLAG_TIME_VALID, + .exit_latency = 10, + .target_residency = 500, + .enter = &intel_idle }, + { + .name = "C3-IVT-8S", + .desc = "MWAIT 0x10", + .flags = MWAIT2flg(0x10) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 59, + .target_residency = 600, + .enter = &intel_idle }, + { + .name = "C6-IVT-8S", + .desc = "MWAIT 0x20", + .flags = MWAIT2flg(0x20) | CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TLB_FLUSHED, + .exit_latency = 88, + .target_residency = 700, + .enter = &intel_idle }, + { + .enter = NULL } +}; + static struct cpuidle_state hsw_cstates[] = { { .name = "C1-HSW", @@ -464,11 +610,21 @@ static const struct idle_cpu idle_cpu_snb = { .disable_promotion_to_c1e = true, }; +static const struct idle_cpu idle_cpu_byt = { + .state_table = byt_cstates, + .disable_promotion_to_c1e = true, +}; + static const struct idle_cpu idle_cpu_ivb = { .state_table = ivb_cstates, .disable_promotion_to_c1e = true, }; +static const struct idle_cpu idle_cpu_ivt = { + .state_table = ivt_cstates, + .disable_promotion_to_c1e = true, +}; + static const struct idle_cpu idle_cpu_hsw = { .state_table = hsw_cstates, .disable_promotion_to_c1e = true, @@ -494,8 +650,10 @@ static const struct x86_cpu_id intel_idle_ids[] = { ICPU(0x2f, idle_cpu_nehalem), ICPU(0x2a, idle_cpu_snb), ICPU(0x2d, idle_cpu_snb), + ICPU(0x36, idle_cpu_atom), + ICPU(0x37, idle_cpu_byt), ICPU(0x3a, idle_cpu_ivb), - ICPU(0x3e, idle_cpu_ivb), + ICPU(0x3e, idle_cpu_ivt), ICPU(0x3c, idle_cpu_hsw), ICPU(0x3f, idle_cpu_hsw), ICPU(0x45, idle_cpu_hsw), @@ -572,6 +730,39 @@ static void intel_idle_cpuidle_devices_uninit(void) free_percpu(intel_idle_cpuidle_devices); return; } + +/* + * intel_idle_state_table_update() + * + * Update the default state_table for this CPU-id + * + * Currently used to access tuned IVT multi-socket targets + * Assumption: num_sockets == (max_package_num + 1) + */ +void intel_idle_state_table_update(void) +{ + /* IVT uses a different table for 1-2, 3-4, and > 4 sockets */ + if (boot_cpu_data.x86_model == 0x3e) { /* IVT */ + int cpu, package_num, num_sockets = 1; + + for_each_online_cpu(cpu) { + package_num = topology_physical_package_id(cpu); + if (package_num + 1 > num_sockets) { + num_sockets = package_num + 1; + + if (num_sockets > 4) + cpuidle_state_table = ivt_cstates_8s; + return; + } + } + + if (num_sockets > 2) + cpuidle_state_table = ivt_cstates_4s; + /* else, 1 and 2 socket systems use default ivt_cstates */ + } + return; +} + /* * intel_idle_cpuidle_driver_init() * allocate, initialize cpuidle_states @@ -581,10 +772,12 @@ static int __init intel_idle_cpuidle_driver_init(void) int cstate; struct cpuidle_driver *drv = &intel_idle_driver; + intel_idle_state_table_update(); + drv->state_count = 1; for (cstate = 0; cstate < CPUIDLE_STATE_MAX; ++cstate) { - int num_substates, mwait_hint, mwait_cstate, mwait_substate; + int num_substates, mwait_hint, mwait_cstate; if (cpuidle_state_table[cstate].enter == NULL) break; @@ -597,14 +790,13 @@ static int __init intel_idle_cpuidle_driver_init(void) mwait_hint = flg2MWAIT(cpuidle_state_table[cstate].flags); mwait_cstate = MWAIT_HINT2CSTATE(mwait_hint); - mwait_substate = MWAIT_HINT2SUBSTATE(mwait_hint); - /* does the state exist in CPUID.MWAIT? */ + /* number of sub-states for this state in CPUID.MWAIT */ num_substates = (mwait_substates >> ((mwait_cstate + 1) * 4)) & MWAIT_SUBSTATE_MASK; - /* if sub-state in table is not enumerated by CPUID */ - if ((mwait_substate + 1) > num_substates) + /* if NO sub-states for this state in CPUID, skip it */ + if (num_substates == 0) continue; if (((mwait_cstate + 1) > 2) && diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c index 8ee228e9ab5a..c98fdb185931 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.c +++ b/drivers/infiniband/ulp/isert/ib_isert.c @@ -51,6 +51,8 @@ isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn); static int isert_reg_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct isert_rdma_wr *wr); +static int +isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd); static void isert_qp_event_callback(struct ib_event *e, void *context) @@ -87,7 +89,8 @@ isert_query_device(struct ib_device *ib_dev, struct ib_device_attr *devattr) } static int -isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id) +isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id, + u8 protection) { struct isert_device *device = isert_conn->conn_device; struct ib_qp_init_attr attr; @@ -119,6 +122,8 @@ isert_conn_setup_qp(struct isert_conn *isert_conn, struct rdma_cm_id *cma_id) attr.cap.max_recv_sge = 1; attr.sq_sig_type = IB_SIGNAL_REQ_WR; attr.qp_type = IB_QPT_RC; + if (protection) + attr.create_flags |= IB_QP_CREATE_SIGNATURE_EN; pr_debug("isert_conn_setup_qp cma_id->device: %p\n", cma_id->device); @@ -226,7 +231,8 @@ isert_create_device_ib_res(struct isert_device *device) return ret; /* asign function handlers */ - if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS) { + if (dev_attr->device_cap_flags & IB_DEVICE_MEM_MGT_EXTENSIONS && + dev_attr->device_cap_flags & IB_DEVICE_SIGNATURE_HANDOVER) { device->use_fastreg = 1; device->reg_rdma_mem = isert_reg_rdma; device->unreg_rdma_mem = isert_unreg_rdma; @@ -236,13 +242,18 @@ isert_create_device_ib_res(struct isert_device *device) device->unreg_rdma_mem = isert_unmap_cmd; } + /* Check signature cap */ + device->pi_capable = dev_attr->device_cap_flags & + IB_DEVICE_SIGNATURE_HANDOVER ? true : false; + device->cqs_used = min_t(int, num_online_cpus(), device->ib_device->num_comp_vectors); device->cqs_used = min(ISERT_MAX_CQ, device->cqs_used); pr_debug("Using %d CQs, device %s supports %d vectors support " - "Fast registration %d\n", + "Fast registration %d pi_capable %d\n", device->cqs_used, device->ib_device->name, - device->ib_device->num_comp_vectors, device->use_fastreg); + device->ib_device->num_comp_vectors, device->use_fastreg, + device->pi_capable); device->cq_desc = kzalloc(sizeof(struct isert_cq_desc) * device->cqs_used, GFP_KERNEL); if (!device->cq_desc) { @@ -395,6 +406,12 @@ isert_conn_free_fastreg_pool(struct isert_conn *isert_conn) list_del(&fr_desc->list); ib_free_fast_reg_page_list(fr_desc->data_frpl); ib_dereg_mr(fr_desc->data_mr); + if (fr_desc->pi_ctx) { + ib_free_fast_reg_page_list(fr_desc->pi_ctx->prot_frpl); + ib_dereg_mr(fr_desc->pi_ctx->prot_mr); + ib_destroy_mr(fr_desc->pi_ctx->sig_mr); + kfree(fr_desc->pi_ctx); + } kfree(fr_desc); ++i; } @@ -406,8 +423,10 @@ isert_conn_free_fastreg_pool(struct isert_conn *isert_conn) static int isert_create_fr_desc(struct ib_device *ib_device, struct ib_pd *pd, - struct fast_reg_descriptor *fr_desc) + struct fast_reg_descriptor *fr_desc, u8 protection) { + int ret; + fr_desc->data_frpl = ib_alloc_fast_reg_page_list(ib_device, ISCSI_ISER_SG_TABLESIZE); if (IS_ERR(fr_desc->data_frpl)) { @@ -420,27 +439,88 @@ isert_create_fr_desc(struct ib_device *ib_device, struct ib_pd *pd, if (IS_ERR(fr_desc->data_mr)) { pr_err("Failed to allocate data frmr err=%ld\n", PTR_ERR(fr_desc->data_mr)); - ib_free_fast_reg_page_list(fr_desc->data_frpl); - return PTR_ERR(fr_desc->data_mr); + ret = PTR_ERR(fr_desc->data_mr); + goto err_data_frpl; } pr_debug("Create fr_desc %p page_list %p\n", fr_desc, fr_desc->data_frpl->page_list); + fr_desc->ind |= ISERT_DATA_KEY_VALID; + + if (protection) { + struct ib_mr_init_attr mr_init_attr = {0}; + struct pi_context *pi_ctx; + + fr_desc->pi_ctx = kzalloc(sizeof(*fr_desc->pi_ctx), GFP_KERNEL); + if (!fr_desc->pi_ctx) { + pr_err("Failed to allocate pi context\n"); + ret = -ENOMEM; + goto err_data_mr; + } + pi_ctx = fr_desc->pi_ctx; + + pi_ctx->prot_frpl = ib_alloc_fast_reg_page_list(ib_device, + ISCSI_ISER_SG_TABLESIZE); + if (IS_ERR(pi_ctx->prot_frpl)) { + pr_err("Failed to allocate prot frpl err=%ld\n", + PTR_ERR(pi_ctx->prot_frpl)); + ret = PTR_ERR(pi_ctx->prot_frpl); + goto err_pi_ctx; + } - fr_desc->valid = true; + pi_ctx->prot_mr = ib_alloc_fast_reg_mr(pd, ISCSI_ISER_SG_TABLESIZE); + if (IS_ERR(pi_ctx->prot_mr)) { + pr_err("Failed to allocate prot frmr err=%ld\n", + PTR_ERR(pi_ctx->prot_mr)); + ret = PTR_ERR(pi_ctx->prot_mr); + goto err_prot_frpl; + } + fr_desc->ind |= ISERT_PROT_KEY_VALID; + + mr_init_attr.max_reg_descriptors = 2; + mr_init_attr.flags |= IB_MR_SIGNATURE_EN; + pi_ctx->sig_mr = ib_create_mr(pd, &mr_init_attr); + if (IS_ERR(pi_ctx->sig_mr)) { + pr_err("Failed to allocate signature enabled mr err=%ld\n", + PTR_ERR(pi_ctx->sig_mr)); + ret = PTR_ERR(pi_ctx->sig_mr); + goto err_prot_mr; + } + fr_desc->ind |= ISERT_SIG_KEY_VALID; + } + fr_desc->ind &= ~ISERT_PROTECTED; return 0; +err_prot_mr: + ib_dereg_mr(fr_desc->pi_ctx->prot_mr); +err_prot_frpl: + ib_free_fast_reg_page_list(fr_desc->pi_ctx->prot_frpl); +err_pi_ctx: + kfree(fr_desc->pi_ctx); +err_data_mr: + ib_dereg_mr(fr_desc->data_mr); +err_data_frpl: + ib_free_fast_reg_page_list(fr_desc->data_frpl); + + return ret; } static int -isert_conn_create_fastreg_pool(struct isert_conn *isert_conn) +isert_conn_create_fastreg_pool(struct isert_conn *isert_conn, u8 pi_support) { struct fast_reg_descriptor *fr_desc; struct isert_device *device = isert_conn->conn_device; - int i, ret; + struct se_session *se_sess = isert_conn->conn->sess->se_sess; + struct se_node_acl *se_nacl = se_sess->se_node_acl; + int i, ret, tag_num; + /* + * Setup the number of FRMRs based upon the number of tags + * available to session in iscsi_target_locate_portal(). + */ + tag_num = max_t(u32, ISCSIT_MIN_TAGS, se_nacl->queue_depth); + tag_num = (tag_num * 2) + ISCSIT_EXTRA_TAGS; - INIT_LIST_HEAD(&isert_conn->conn_fr_pool); isert_conn->conn_fr_pool_size = 0; - for (i = 0; i < ISCSI_DEF_XMIT_CMDS_MAX; i++) { + for (i = 0; i < tag_num; i++) { fr_desc = kzalloc(sizeof(*fr_desc), GFP_KERNEL); if (!fr_desc) { pr_err("Failed to allocate fast_reg descriptor\n"); @@ -449,7 +529,8 @@ isert_conn_create_fastreg_pool(struct isert_conn *isert_conn) } ret = isert_create_fr_desc(device->ib_device, - isert_conn->conn_pd, fr_desc); + isert_conn->conn_pd, fr_desc, + pi_support); if (ret) { pr_err("Failed to create fastreg descriptor err=%d\n", ret); @@ -480,6 +561,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) struct isert_device *device; struct ib_device *ib_dev = cma_id->device; int ret = 0; + u8 pi_support = np->tpg_np->tpg->tpg_attrib.t10_pi; pr_debug("Entering isert_connect_request cma_id: %p, context: %p\n", cma_id, cma_id->context); @@ -498,6 +580,7 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) kref_get(&isert_conn->conn_kref); mutex_init(&isert_conn->conn_mutex); spin_lock_init(&isert_conn->conn_lock); + INIT_LIST_HEAD(&isert_conn->conn_fr_pool); cma_id->context = isert_conn; isert_conn->conn_cm_id = cma_id; @@ -569,16 +652,13 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) goto out_mr; } - if (device->use_fastreg) { - ret = isert_conn_create_fastreg_pool(isert_conn); - if (ret) { - pr_err("Conn: %p failed to create fastreg pool\n", - isert_conn); - goto out_fastreg; - } + if (pi_support && !device->pi_capable) { + pr_err("Protection information requested but not supported\n"); + ret = -EINVAL; + goto out_mr; } - ret = isert_conn_setup_qp(isert_conn, cma_id); + ret = isert_conn_setup_qp(isert_conn, cma_id, pi_support); if (ret) goto out_conn_dev; @@ -591,9 +671,6 @@ isert_connect_request(struct rdma_cm_id *cma_id, struct rdma_cm_event *event) return 0; out_conn_dev: - if (device->use_fastreg) - isert_conn_free_fastreg_pool(isert_conn); -out_fastreg: ib_dereg_mr(isert_conn->conn_mr); out_mr: ib_dealloc_pd(isert_conn->conn_pd); @@ -967,6 +1044,18 @@ isert_put_login_tx(struct iscsi_conn *conn, struct iscsi_login *login, } if (!login->login_failed) { if (login->login_complete) { + if (isert_conn->conn_device->use_fastreg) { + u8 pi_support = login->np->tpg_np->tpg->tpg_attrib.t10_pi; + + ret = isert_conn_create_fastreg_pool(isert_conn, + pi_support); + if (ret) { + pr_err("Conn: %p failed to create" + " fastreg pool\n", isert_conn); + return ret; + } + } + ret = isert_alloc_rx_descriptors(isert_conn); if (ret) return ret; @@ -1392,19 +1481,60 @@ isert_rx_completion(struct iser_rx_desc *desc, struct isert_conn *isert_conn, } } +static int +isert_map_data_buf(struct isert_conn *isert_conn, struct isert_cmd *isert_cmd, + struct scatterlist *sg, u32 nents, u32 length, u32 offset, + enum iser_ib_op_code op, struct isert_data_buf *data) +{ + struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + + data->dma_dir = op == ISER_IB_RDMA_WRITE ? + DMA_TO_DEVICE : DMA_FROM_DEVICE; + + data->len = length - offset; + data->offset = offset; + data->sg_off = data->offset / PAGE_SIZE; + + data->sg = &sg[data->sg_off]; + data->nents = min_t(unsigned int, nents - data->sg_off, + ISCSI_ISER_SG_TABLESIZE); + data->len = min_t(unsigned int, data->len, ISCSI_ISER_SG_TABLESIZE * + PAGE_SIZE); + + data->dma_nents = ib_dma_map_sg(ib_dev, data->sg, data->nents, + data->dma_dir); + if (unlikely(!data->dma_nents)) { + pr_err("Cmd: unable to dma map SGs %p\n", sg); + return -EINVAL; + } + + pr_debug("Mapped cmd: %p count: %u sg: %p sg_nents: %u rdma_len %d\n", + isert_cmd, data->dma_nents, data->sg, data->nents, data->len); + + return 0; +} + +static void +isert_unmap_data_buf(struct isert_conn *isert_conn, struct isert_data_buf *data) +{ + struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + + ib_dma_unmap_sg(ib_dev, data->sg, data->nents, data->dma_dir); + memset(data, 0, sizeof(*data)); +} + + + static void isert_unmap_cmd(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn) { struct isert_rdma_wr *wr = &isert_cmd->rdma_wr; - struct ib_device *ib_dev = isert_conn->conn_cm_id->device; pr_debug("isert_unmap_cmd: %p\n", isert_cmd); - if (wr->sge) { + + if (wr->data.sg) { pr_debug("isert_unmap_cmd: %p unmap_sg op\n", isert_cmd); - ib_dma_unmap_sg(ib_dev, wr->sge, wr->num_sge, - (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - wr->sge = NULL; + isert_unmap_data_buf(isert_conn, &wr->data); } if (wr->send_wr) { @@ -1424,7 +1554,6 @@ static void isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn) { struct isert_rdma_wr *wr = &isert_cmd->rdma_wr; - struct ib_device *ib_dev = isert_conn->conn_cm_id->device; LIST_HEAD(unmap_list); pr_debug("unreg_fastreg_cmd: %p\n", isert_cmd); @@ -1432,18 +1561,19 @@ isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn) if (wr->fr_desc) { pr_debug("unreg_fastreg_cmd: %p free fr_desc %p\n", isert_cmd, wr->fr_desc); + if (wr->fr_desc->ind & ISERT_PROTECTED) { + isert_unmap_data_buf(isert_conn, &wr->prot); + wr->fr_desc->ind &= ~ISERT_PROTECTED; + } spin_lock_bh(&isert_conn->conn_lock); list_add_tail(&wr->fr_desc->list, &isert_conn->conn_fr_pool); spin_unlock_bh(&isert_conn->conn_lock); wr->fr_desc = NULL; } - if (wr->sge) { + if (wr->data.sg) { pr_debug("unreg_fastreg_cmd: %p unmap_sg op\n", isert_cmd); - ib_dma_unmap_sg(ib_dev, wr->sge, wr->num_sge, - (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - wr->sge = NULL; + isert_unmap_data_buf(isert_conn, &wr->data); } wr->ib_sge = NULL; @@ -1451,7 +1581,7 @@ isert_unreg_rdma(struct isert_cmd *isert_cmd, struct isert_conn *isert_conn) } static void -isert_put_cmd(struct isert_cmd *isert_cmd) +isert_put_cmd(struct isert_cmd *isert_cmd, bool comp_err) { struct iscsi_cmd *cmd = isert_cmd->iscsi_cmd; struct isert_conn *isert_conn = isert_cmd->conn; @@ -1467,8 +1597,21 @@ isert_put_cmd(struct isert_cmd *isert_cmd) list_del_init(&cmd->i_conn_node); spin_unlock_bh(&conn->cmd_lock); - if (cmd->data_direction == DMA_TO_DEVICE) + if (cmd->data_direction == DMA_TO_DEVICE) { iscsit_stop_dataout_timer(cmd); + /* + * Check for special case during comp_err where + * WRITE_PENDING has been handed off from core, + * but requires an extra target_put_sess_cmd() + * before transport_generic_free_cmd() below. + */ + if (comp_err && + cmd->se_cmd.t_state == TRANSPORT_WRITE_PENDING) { + struct se_cmd *se_cmd = &cmd->se_cmd; + + target_put_sess_cmd(se_cmd->se_sess, se_cmd); + } + } device->unreg_rdma_mem(isert_cmd, isert_conn); transport_generic_free_cmd(&cmd->se_cmd, 0); @@ -1523,7 +1666,7 @@ isert_unmap_tx_desc(struct iser_tx_desc *tx_desc, struct ib_device *ib_dev) static void isert_completion_put(struct iser_tx_desc *tx_desc, struct isert_cmd *isert_cmd, - struct ib_device *ib_dev) + struct ib_device *ib_dev, bool comp_err) { if (isert_cmd->pdu_buf_dma != 0) { pr_debug("Calling ib_dma_unmap_single for isert_cmd->pdu_buf_dma\n"); @@ -1533,7 +1676,77 @@ isert_completion_put(struct iser_tx_desc *tx_desc, struct isert_cmd *isert_cmd, } isert_unmap_tx_desc(tx_desc, ib_dev); - isert_put_cmd(isert_cmd); + isert_put_cmd(isert_cmd, comp_err); +} + +static int +isert_check_pi_status(struct se_cmd *se_cmd, struct ib_mr *sig_mr) +{ + struct ib_mr_status mr_status; + int ret; + + ret = ib_check_mr_status(sig_mr, IB_MR_CHECK_SIG_STATUS, &mr_status); + if (ret) { + pr_err("ib_check_mr_status failed, ret %d\n", ret); + goto fail_mr_status; + } + + if (mr_status.fail_status & IB_MR_CHECK_SIG_STATUS) { + u64 sec_offset_err; + u32 block_size = se_cmd->se_dev->dev_attrib.block_size + 8; + + switch (mr_status.sig_err.err_type) { + case IB_SIG_BAD_GUARD: + se_cmd->pi_err = TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED; + break; + case IB_SIG_BAD_REFTAG: + se_cmd->pi_err = TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED; + break; + case IB_SIG_BAD_APPTAG: + se_cmd->pi_err = TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED; + break; + } + sec_offset_err = mr_status.sig_err.sig_err_offset; + do_div(sec_offset_err, block_size); + se_cmd->bad_sector = sec_offset_err + se_cmd->t_task_lba; + + pr_err("isert: PI error found type %d at sector 0x%llx " + "expected 0x%x vs actual 0x%x\n", + mr_status.sig_err.err_type, + (unsigned long long)se_cmd->bad_sector, + mr_status.sig_err.expected, + mr_status.sig_err.actual); + ret = 1; + } + +fail_mr_status: + return ret; +} + +static void +isert_completion_rdma_write(struct iser_tx_desc *tx_desc, + struct isert_cmd *isert_cmd) +{ + struct isert_rdma_wr *wr = &isert_cmd->rdma_wr; + struct iscsi_cmd *cmd = isert_cmd->iscsi_cmd; + struct se_cmd *se_cmd = &cmd->se_cmd; + struct isert_conn *isert_conn = isert_cmd->conn; + struct isert_device *device = isert_conn->conn_device; + int ret = 0; + + if (wr->fr_desc && wr->fr_desc->ind & ISERT_PROTECTED) { + ret = isert_check_pi_status(se_cmd, + wr->fr_desc->pi_ctx->sig_mr); + wr->fr_desc->ind &= ~ISERT_PROTECTED; + } + + device->unreg_rdma_mem(isert_cmd, isert_conn); + wr->send_wr_num = 0; + if (ret) + transport_send_check_condition_and_sense(se_cmd, + se_cmd->pi_err, 0); + else + isert_put_response(isert_conn->conn, cmd); } static void @@ -1545,10 +1758,17 @@ isert_completion_rdma_read(struct iser_tx_desc *tx_desc, struct se_cmd *se_cmd = &cmd->se_cmd; struct isert_conn *isert_conn = isert_cmd->conn; struct isert_device *device = isert_conn->conn_device; + int ret = 0; + + if (wr->fr_desc && wr->fr_desc->ind & ISERT_PROTECTED) { + ret = isert_check_pi_status(se_cmd, + wr->fr_desc->pi_ctx->sig_mr); + wr->fr_desc->ind &= ~ISERT_PROTECTED; + } iscsit_stop_dataout_timer(cmd); device->unreg_rdma_mem(isert_cmd, isert_conn); - cmd->write_data_done = wr->cur_rdma_length; + cmd->write_data_done = wr->data.len; wr->send_wr_num = 0; pr_debug("Cmd: %p RDMA_READ comp calling execute_cmd\n", isert_cmd); @@ -1557,7 +1777,11 @@ isert_completion_rdma_read(struct iser_tx_desc *tx_desc, cmd->i_state = ISTATE_RECEIVED_LAST_DATAOUT; spin_unlock_bh(&cmd->istate_lock); - target_execute_cmd(se_cmd); + if (ret) + transport_send_check_condition_and_sense(se_cmd, + se_cmd->pi_err, 0); + else + target_execute_cmd(se_cmd); } static void @@ -1577,14 +1801,14 @@ isert_do_control_comp(struct work_struct *work) iscsit_tmr_post_handler(cmd, cmd->conn); cmd->i_state = ISTATE_SENT_STATUS; - isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev); + isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev, false); break; case ISTATE_SEND_REJECT: pr_debug("Got isert_do_control_comp ISTATE_SEND_REJECT: >>>\n"); atomic_dec(&isert_conn->post_send_buf_count); cmd->i_state = ISTATE_SENT_STATUS; - isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev); + isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev, false); break; case ISTATE_SEND_LOGOUTRSP: pr_debug("Calling iscsit_logout_post_handler >>>>>>>>>>>>>>\n"); @@ -1598,7 +1822,7 @@ isert_do_control_comp(struct work_struct *work) case ISTATE_SEND_TEXTRSP: atomic_dec(&isert_conn->post_send_buf_count); cmd->i_state = ISTATE_SENT_STATUS; - isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev); + isert_completion_put(&isert_cmd->tx_desc, isert_cmd, ib_dev, false); break; default: pr_err("Unknown do_control_comp i_state %d\n", cmd->i_state); @@ -1626,10 +1850,21 @@ isert_response_completion(struct iser_tx_desc *tx_desc, queue_work(isert_comp_wq, &isert_cmd->comp_work); return; } - atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); + + /** + * If send_wr_num is 0 this means that we got + * RDMA completion and we cleared it and we should + * simply decrement the response post. else the + * response is incorporated in send_wr_num, just + * sub it. + **/ + if (wr->send_wr_num) + atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count); + else + atomic_dec(&isert_conn->post_send_buf_count); cmd->i_state = ISTATE_SENT_STATUS; - isert_completion_put(tx_desc, isert_cmd, ib_dev); + isert_completion_put(tx_desc, isert_cmd, ib_dev, false); } static void @@ -1658,8 +1893,9 @@ __isert_send_completion(struct iser_tx_desc *tx_desc, isert_conn, ib_dev); break; case ISER_IB_RDMA_WRITE: - pr_err("isert_send_completion: Got ISER_IB_RDMA_WRITE\n"); - dump_stack(); + pr_debug("isert_send_completion: Got ISER_IB_RDMA_WRITE\n"); + atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count); + isert_completion_rdma_write(tx_desc, isert_cmd); break; case ISER_IB_RDMA_READ: pr_debug("isert_send_completion: Got ISER_IB_RDMA_READ:\n"); @@ -1709,8 +1945,20 @@ isert_cq_drain_comp_llist(struct isert_conn *isert_conn, struct ib_device *ib_de llnode = llist_next(llnode); wr = &t->isert_cmd->rdma_wr; - atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); - isert_completion_put(t, t->isert_cmd, ib_dev); + /** + * If send_wr_num is 0 this means that we got + * RDMA completion and we cleared it and we should + * simply decrement the response post. else the + * response is incorporated in send_wr_num, just + * sub it. + **/ + if (wr->send_wr_num) + atomic_sub(wr->send_wr_num, + &isert_conn->post_send_buf_count); + else + atomic_dec(&isert_conn->post_send_buf_count); + + isert_completion_put(t, t->isert_cmd, ib_dev, true); } } @@ -1728,15 +1976,27 @@ isert_cq_tx_comp_err(struct iser_tx_desc *tx_desc, struct isert_conn *isert_conn llnode = llist_next(llnode); wr = &t->isert_cmd->rdma_wr; - atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); - isert_completion_put(t, t->isert_cmd, ib_dev); + /** + * If send_wr_num is 0 this means that we got + * RDMA completion and we cleared it and we should + * simply decrement the response post. else the + * response is incorporated in send_wr_num, just + * sub it. + **/ + if (wr->send_wr_num) + atomic_sub(wr->send_wr_num, + &isert_conn->post_send_buf_count); + else + atomic_dec(&isert_conn->post_send_buf_count); + + isert_completion_put(t, t->isert_cmd, ib_dev, true); } tx_desc->comp_llnode_batch = NULL; if (!isert_cmd) isert_unmap_tx_desc(tx_desc, ib_dev); else - isert_completion_put(tx_desc, isert_cmd, ib_dev); + isert_completion_put(tx_desc, isert_cmd, ib_dev, true); } static void @@ -1918,6 +2178,36 @@ isert_put_response(struct iscsi_conn *conn, struct iscsi_cmd *cmd) return isert_post_response(isert_conn, isert_cmd); } +static void +isert_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd) +{ + struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd); + struct isert_conn *isert_conn = (struct isert_conn *)conn->context; + struct isert_device *device = isert_conn->conn_device; + + spin_lock_bh(&conn->cmd_lock); + if (!list_empty(&cmd->i_conn_node)) + list_del_init(&cmd->i_conn_node); + spin_unlock_bh(&conn->cmd_lock); + + if (cmd->data_direction == DMA_TO_DEVICE) + iscsit_stop_dataout_timer(cmd); + + device->unreg_rdma_mem(isert_cmd, isert_conn); +} + +static enum target_prot_op +isert_get_sup_prot_ops(struct iscsi_conn *conn) +{ + struct isert_conn *isert_conn = (struct isert_conn *)conn->context; + struct isert_device *device = isert_conn->conn_device; + + if (device->pi_capable) + return TARGET_PROT_ALL; + + return TARGET_PROT_NORMAL; +} + static int isert_put_nopin(struct iscsi_cmd *cmd, struct iscsi_conn *conn, bool nopout_response) @@ -2099,54 +2389,39 @@ isert_map_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd, struct se_cmd *se_cmd = &cmd->se_cmd; struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd); struct isert_conn *isert_conn = (struct isert_conn *)conn->context; - struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + struct isert_data_buf *data = &wr->data; struct ib_send_wr *send_wr; struct ib_sge *ib_sge; - struct scatterlist *sg_start; - u32 sg_off = 0, sg_nents; - u32 offset = 0, data_len, data_left, rdma_write_max, va_offset = 0; - int ret = 0, count, i, ib_sge_cnt; + u32 offset, data_len, data_left, rdma_write_max, va_offset = 0; + int ret = 0, i, ib_sge_cnt; - if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) { - data_left = se_cmd->data_length; - } else { - sg_off = cmd->write_data_done / PAGE_SIZE; - data_left = se_cmd->data_length - cmd->write_data_done; - offset = cmd->write_data_done; - isert_cmd->tx_desc.isert_cmd = isert_cmd; - } + isert_cmd->tx_desc.isert_cmd = isert_cmd; - sg_start = &cmd->se_cmd.t_data_sg[sg_off]; - sg_nents = se_cmd->t_data_nents - sg_off; + offset = wr->iser_ib_op == ISER_IB_RDMA_READ ? cmd->write_data_done : 0; + ret = isert_map_data_buf(isert_conn, isert_cmd, se_cmd->t_data_sg, + se_cmd->t_data_nents, se_cmd->data_length, + offset, wr->iser_ib_op, &wr->data); + if (ret) + return ret; - count = ib_dma_map_sg(ib_dev, sg_start, sg_nents, - (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (unlikely(!count)) { - pr_err("Cmd: %p unrable to map SGs\n", isert_cmd); - return -EINVAL; - } - wr->sge = sg_start; - wr->num_sge = sg_nents; - wr->cur_rdma_length = data_left; - pr_debug("Mapped cmd: %p count: %u sg: %p sg_nents: %u rdma_len %d\n", - isert_cmd, count, sg_start, sg_nents, data_left); + data_left = data->len; + offset = data->offset; - ib_sge = kzalloc(sizeof(struct ib_sge) * sg_nents, GFP_KERNEL); + ib_sge = kzalloc(sizeof(struct ib_sge) * data->nents, GFP_KERNEL); if (!ib_sge) { pr_warn("Unable to allocate ib_sge\n"); ret = -ENOMEM; - goto unmap_sg; + goto unmap_cmd; } wr->ib_sge = ib_sge; - wr->send_wr_num = DIV_ROUND_UP(sg_nents, isert_conn->max_sge); + wr->send_wr_num = DIV_ROUND_UP(data->nents, isert_conn->max_sge); wr->send_wr = kzalloc(sizeof(struct ib_send_wr) * wr->send_wr_num, GFP_KERNEL); if (!wr->send_wr) { pr_debug("Unable to allocate wr->send_wr\n"); ret = -ENOMEM; - goto unmap_sg; + goto unmap_cmd; } wr->isert_cmd = isert_cmd; @@ -2185,10 +2460,9 @@ isert_map_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd, } return 0; -unmap_sg: - ib_dma_unmap_sg(ib_dev, sg_start, sg_nents, - (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); +unmap_cmd: + isert_unmap_data_buf(isert_conn, data); + return ret; } @@ -2232,49 +2506,70 @@ isert_map_fr_pagelist(struct ib_device *ib_dev, } static int -isert_fast_reg_mr(struct fast_reg_descriptor *fr_desc, - struct isert_conn *isert_conn, struct scatterlist *sg_start, - struct ib_sge *ib_sge, u32 sg_nents, u32 offset, - unsigned int data_len) +isert_fast_reg_mr(struct isert_conn *isert_conn, + struct fast_reg_descriptor *fr_desc, + struct isert_data_buf *mem, + enum isert_indicator ind, + struct ib_sge *sge) { struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + struct ib_mr *mr; + struct ib_fast_reg_page_list *frpl; struct ib_send_wr fr_wr, inv_wr; struct ib_send_wr *bad_wr, *wr = NULL; int ret, pagelist_len; u32 page_off; u8 key; - sg_nents = min_t(unsigned int, sg_nents, ISCSI_ISER_SG_TABLESIZE); - page_off = offset % PAGE_SIZE; + if (mem->dma_nents == 1) { + sge->lkey = isert_conn->conn_mr->lkey; + sge->addr = ib_sg_dma_address(ib_dev, &mem->sg[0]); + sge->length = ib_sg_dma_len(ib_dev, &mem->sg[0]); + pr_debug("%s:%d sge: addr: 0x%llx length: %u lkey: %x\n", + __func__, __LINE__, sge->addr, sge->length, + sge->lkey); + return 0; + } + + if (ind == ISERT_DATA_KEY_VALID) { + /* Registering data buffer */ + mr = fr_desc->data_mr; + frpl = fr_desc->data_frpl; + } else { + /* Registering protection buffer */ + mr = fr_desc->pi_ctx->prot_mr; + frpl = fr_desc->pi_ctx->prot_frpl; + } + + page_off = mem->offset % PAGE_SIZE; pr_debug("Use fr_desc %p sg_nents %d offset %u\n", - fr_desc, sg_nents, offset); + fr_desc, mem->nents, mem->offset); - pagelist_len = isert_map_fr_pagelist(ib_dev, sg_start, sg_nents, - &fr_desc->data_frpl->page_list[0]); + pagelist_len = isert_map_fr_pagelist(ib_dev, mem->sg, mem->nents, + &frpl->page_list[0]); - if (!fr_desc->valid) { + if (!(fr_desc->ind & ISERT_DATA_KEY_VALID)) { memset(&inv_wr, 0, sizeof(inv_wr)); inv_wr.wr_id = ISER_FASTREG_LI_WRID; inv_wr.opcode = IB_WR_LOCAL_INV; - inv_wr.ex.invalidate_rkey = fr_desc->data_mr->rkey; + inv_wr.ex.invalidate_rkey = mr->rkey; wr = &inv_wr; /* Bump the key */ - key = (u8)(fr_desc->data_mr->rkey & 0x000000FF); - ib_update_fast_reg_key(fr_desc->data_mr, ++key); + key = (u8)(mr->rkey & 0x000000FF); + ib_update_fast_reg_key(mr, ++key); } /* Prepare FASTREG WR */ memset(&fr_wr, 0, sizeof(fr_wr)); fr_wr.wr_id = ISER_FASTREG_LI_WRID; fr_wr.opcode = IB_WR_FAST_REG_MR; - fr_wr.wr.fast_reg.iova_start = - fr_desc->data_frpl->page_list[0] + page_off; - fr_wr.wr.fast_reg.page_list = fr_desc->data_frpl; + fr_wr.wr.fast_reg.iova_start = frpl->page_list[0] + page_off; + fr_wr.wr.fast_reg.page_list = frpl; fr_wr.wr.fast_reg.page_list_len = pagelist_len; fr_wr.wr.fast_reg.page_shift = PAGE_SHIFT; - fr_wr.wr.fast_reg.length = data_len; - fr_wr.wr.fast_reg.rkey = fr_desc->data_mr->rkey; + fr_wr.wr.fast_reg.length = mem->len; + fr_wr.wr.fast_reg.rkey = mr->rkey; fr_wr.wr.fast_reg.access_flags = IB_ACCESS_LOCAL_WRITE; if (!wr) @@ -2287,15 +2582,157 @@ isert_fast_reg_mr(struct fast_reg_descriptor *fr_desc, pr_err("fast registration failed, ret:%d\n", ret); return ret; } - fr_desc->valid = false; + fr_desc->ind &= ~ind; + + sge->lkey = mr->lkey; + sge->addr = frpl->page_list[0] + page_off; + sge->length = mem->len; + + pr_debug("%s:%d sge: addr: 0x%llx length: %u lkey: %x\n", + __func__, __LINE__, sge->addr, sge->length, + sge->lkey); + + return ret; +} + +static inline enum ib_t10_dif_type +se2ib_prot_type(enum target_prot_type prot_type) +{ + switch (prot_type) { + case TARGET_DIF_TYPE0_PROT: + return IB_T10DIF_NONE; + case TARGET_DIF_TYPE1_PROT: + return IB_T10DIF_TYPE1; + case TARGET_DIF_TYPE2_PROT: + return IB_T10DIF_TYPE2; + case TARGET_DIF_TYPE3_PROT: + return IB_T10DIF_TYPE3; + default: + return IB_T10DIF_NONE; + } +} - ib_sge->lkey = fr_desc->data_mr->lkey; - ib_sge->addr = fr_desc->data_frpl->page_list[0] + page_off; - ib_sge->length = data_len; +static int +isert_set_sig_attrs(struct se_cmd *se_cmd, struct ib_sig_attrs *sig_attrs) +{ + enum ib_t10_dif_type ib_prot_type = se2ib_prot_type(se_cmd->prot_type); + + sig_attrs->mem.sig_type = IB_SIG_TYPE_T10_DIF; + sig_attrs->wire.sig_type = IB_SIG_TYPE_T10_DIF; + sig_attrs->mem.sig.dif.pi_interval = + se_cmd->se_dev->dev_attrib.block_size; + sig_attrs->wire.sig.dif.pi_interval = + se_cmd->se_dev->dev_attrib.block_size; + + switch (se_cmd->prot_op) { + case TARGET_PROT_DIN_INSERT: + case TARGET_PROT_DOUT_STRIP: + sig_attrs->mem.sig.dif.type = IB_T10DIF_NONE; + sig_attrs->wire.sig.dif.type = ib_prot_type; + sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC; + sig_attrs->wire.sig.dif.ref_tag = se_cmd->reftag_seed; + break; + case TARGET_PROT_DOUT_INSERT: + case TARGET_PROT_DIN_STRIP: + sig_attrs->mem.sig.dif.type = ib_prot_type; + sig_attrs->mem.sig.dif.bg_type = IB_T10DIF_CRC; + sig_attrs->mem.sig.dif.ref_tag = se_cmd->reftag_seed; + sig_attrs->wire.sig.dif.type = IB_T10DIF_NONE; + break; + case TARGET_PROT_DIN_PASS: + case TARGET_PROT_DOUT_PASS: + sig_attrs->mem.sig.dif.type = ib_prot_type; + sig_attrs->mem.sig.dif.bg_type = IB_T10DIF_CRC; + sig_attrs->mem.sig.dif.ref_tag = se_cmd->reftag_seed; + sig_attrs->wire.sig.dif.type = ib_prot_type; + sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC; + sig_attrs->wire.sig.dif.ref_tag = se_cmd->reftag_seed; + break; + default: + pr_err("Unsupported PI operation %d\n", se_cmd->prot_op); + return -EINVAL; + } - pr_debug("RDMA ib_sge: addr: 0x%16llx length: %u lkey: %08x\n", - ib_sge->addr, ib_sge->length, ib_sge->lkey); + return 0; +} + +static inline u8 +isert_set_prot_checks(u8 prot_checks) +{ + return (prot_checks & TARGET_DIF_CHECK_GUARD ? 0xc0 : 0) | + (prot_checks & TARGET_DIF_CHECK_REFTAG ? 0x30 : 0) | + (prot_checks & TARGET_DIF_CHECK_REFTAG ? 0x0f : 0); +} + +static int +isert_reg_sig_mr(struct isert_conn *isert_conn, struct se_cmd *se_cmd, + struct fast_reg_descriptor *fr_desc, + struct ib_sge *data_sge, struct ib_sge *prot_sge, + struct ib_sge *sig_sge) +{ + struct ib_send_wr sig_wr, inv_wr; + struct ib_send_wr *bad_wr, *wr = NULL; + struct pi_context *pi_ctx = fr_desc->pi_ctx; + struct ib_sig_attrs sig_attrs; + int ret; + u32 key; + + memset(&sig_attrs, 0, sizeof(sig_attrs)); + ret = isert_set_sig_attrs(se_cmd, &sig_attrs); + if (ret) + goto err; + + sig_attrs.check_mask = isert_set_prot_checks(se_cmd->prot_checks); + + if (!(fr_desc->ind & ISERT_SIG_KEY_VALID)) { + memset(&inv_wr, 0, sizeof(inv_wr)); + inv_wr.opcode = IB_WR_LOCAL_INV; + inv_wr.wr_id = ISER_FASTREG_LI_WRID; + inv_wr.ex.invalidate_rkey = pi_ctx->sig_mr->rkey; + wr = &inv_wr; + /* Bump the key */ + key = (u8)(pi_ctx->sig_mr->rkey & 0x000000FF); + ib_update_fast_reg_key(pi_ctx->sig_mr, ++key); + } + + memset(&sig_wr, 0, sizeof(sig_wr)); + sig_wr.opcode = IB_WR_REG_SIG_MR; + sig_wr.wr_id = ISER_FASTREG_LI_WRID; + sig_wr.sg_list = data_sge; + sig_wr.num_sge = 1; + sig_wr.wr.sig_handover.access_flags = IB_ACCESS_LOCAL_WRITE; + sig_wr.wr.sig_handover.sig_attrs = &sig_attrs; + sig_wr.wr.sig_handover.sig_mr = pi_ctx->sig_mr; + if (se_cmd->t_prot_sg) + sig_wr.wr.sig_handover.prot = prot_sge; + + if (!wr) + wr = &sig_wr; + else + wr->next = &sig_wr; + + ret = ib_post_send(isert_conn->conn_qp, wr, &bad_wr); + if (ret) { + pr_err("fast registration failed, ret:%d\n", ret); + goto err; + } + fr_desc->ind &= ~ISERT_SIG_KEY_VALID; + + sig_sge->lkey = pi_ctx->sig_mr->lkey; + sig_sge->addr = 0; + sig_sge->length = se_cmd->data_length; + if (se_cmd->prot_op != TARGET_PROT_DIN_STRIP && + se_cmd->prot_op != TARGET_PROT_DOUT_INSERT) + /* + * We have protection guards on the wire + * so we need to set a larget transfer + */ + sig_sge->length += se_cmd->prot_length; + pr_debug("sig_sge: addr: 0x%llx length: %u lkey: %x\n", + sig_sge->addr, sig_sge->length, + sig_sge->lkey); +err: return ret; } @@ -2305,62 +2742,82 @@ isert_reg_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd, { struct se_cmd *se_cmd = &cmd->se_cmd; struct isert_cmd *isert_cmd = iscsit_priv_cmd(cmd); - struct isert_conn *isert_conn = (struct isert_conn *)conn->context; - struct ib_device *ib_dev = isert_conn->conn_cm_id->device; + struct isert_conn *isert_conn = conn->context; + struct ib_sge data_sge; struct ib_send_wr *send_wr; - struct ib_sge *ib_sge; - struct scatterlist *sg_start; - struct fast_reg_descriptor *fr_desc; - u32 sg_off = 0, sg_nents; - u32 offset = 0, data_len, data_left, rdma_write_max; - int ret = 0, count; + struct fast_reg_descriptor *fr_desc = NULL; + u32 offset; + int ret = 0; unsigned long flags; - if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) { - data_left = se_cmd->data_length; - } else { - offset = cmd->write_data_done; - sg_off = offset / PAGE_SIZE; - data_left = se_cmd->data_length - cmd->write_data_done; - isert_cmd->tx_desc.isert_cmd = isert_cmd; - } + isert_cmd->tx_desc.isert_cmd = isert_cmd; - sg_start = &cmd->se_cmd.t_data_sg[sg_off]; - sg_nents = se_cmd->t_data_nents - sg_off; + offset = wr->iser_ib_op == ISER_IB_RDMA_READ ? cmd->write_data_done : 0; + ret = isert_map_data_buf(isert_conn, isert_cmd, se_cmd->t_data_sg, + se_cmd->t_data_nents, se_cmd->data_length, + offset, wr->iser_ib_op, &wr->data); + if (ret) + return ret; - count = ib_dma_map_sg(ib_dev, sg_start, sg_nents, - (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (unlikely(!count)) { - pr_err("Cmd: %p unrable to map SGs\n", isert_cmd); - return -EINVAL; + if (wr->data.dma_nents != 1 || + se_cmd->prot_op != TARGET_PROT_NORMAL) { + spin_lock_irqsave(&isert_conn->conn_lock, flags); + fr_desc = list_first_entry(&isert_conn->conn_fr_pool, + struct fast_reg_descriptor, list); + list_del(&fr_desc->list); + spin_unlock_irqrestore(&isert_conn->conn_lock, flags); + wr->fr_desc = fr_desc; } - wr->sge = sg_start; - wr->num_sge = sg_nents; - pr_debug("Mapped cmd: %p count: %u sg: %p sg_nents: %u rdma_len %d\n", - isert_cmd, count, sg_start, sg_nents, data_left); - memset(&wr->s_ib_sge, 0, sizeof(*ib_sge)); - ib_sge = &wr->s_ib_sge; - wr->ib_sge = ib_sge; + ret = isert_fast_reg_mr(isert_conn, fr_desc, &wr->data, + ISERT_DATA_KEY_VALID, &data_sge); + if (ret) + goto unmap_cmd; + + if (se_cmd->prot_op != TARGET_PROT_NORMAL) { + struct ib_sge prot_sge, sig_sge; + + if (se_cmd->t_prot_sg) { + ret = isert_map_data_buf(isert_conn, isert_cmd, + se_cmd->t_prot_sg, + se_cmd->t_prot_nents, + se_cmd->prot_length, + 0, wr->iser_ib_op, &wr->prot); + if (ret) + goto unmap_cmd; + + ret = isert_fast_reg_mr(isert_conn, fr_desc, &wr->prot, + ISERT_PROT_KEY_VALID, &prot_sge); + if (ret) + goto unmap_prot_cmd; + } + + ret = isert_reg_sig_mr(isert_conn, se_cmd, fr_desc, + &data_sge, &prot_sge, &sig_sge); + if (ret) + goto unmap_prot_cmd; + fr_desc->ind |= ISERT_PROTECTED; + memcpy(&wr->s_ib_sge, &sig_sge, sizeof(sig_sge)); + } else + memcpy(&wr->s_ib_sge, &data_sge, sizeof(data_sge)); + + wr->ib_sge = &wr->s_ib_sge; wr->send_wr_num = 1; memset(&wr->s_send_wr, 0, sizeof(*send_wr)); wr->send_wr = &wr->s_send_wr; - wr->isert_cmd = isert_cmd; - rdma_write_max = ISCSI_ISER_SG_TABLESIZE * PAGE_SIZE; send_wr = &isert_cmd->rdma_wr.s_send_wr; - send_wr->sg_list = ib_sge; + send_wr->sg_list = &wr->s_ib_sge; send_wr->num_sge = 1; send_wr->wr_id = (unsigned long)&isert_cmd->tx_desc; if (wr->iser_ib_op == ISER_IB_RDMA_WRITE) { send_wr->opcode = IB_WR_RDMA_WRITE; send_wr->wr.rdma.remote_addr = isert_cmd->read_va; send_wr->wr.rdma.rkey = isert_cmd->read_stag; - send_wr->send_flags = 0; - send_wr->next = &isert_cmd->tx_desc.send_wr; + send_wr->send_flags = se_cmd->prot_op == TARGET_PROT_NORMAL ? + 0 : IB_SEND_SIGNALED; } else { send_wr->opcode = IB_WR_RDMA_READ; send_wr->wr.rdma.remote_addr = isert_cmd->write_va; @@ -2368,37 +2825,18 @@ isert_reg_rdma(struct iscsi_conn *conn, struct iscsi_cmd *cmd, send_wr->send_flags = IB_SEND_SIGNALED; } - data_len = min(data_left, rdma_write_max); - wr->cur_rdma_length = data_len; - - /* if there is a single dma entry, dma mr is sufficient */ - if (count == 1) { - ib_sge->addr = ib_sg_dma_address(ib_dev, &sg_start[0]); - ib_sge->length = ib_sg_dma_len(ib_dev, &sg_start[0]); - ib_sge->lkey = isert_conn->conn_mr->lkey; - wr->fr_desc = NULL; - } else { + return 0; +unmap_prot_cmd: + if (se_cmd->t_prot_sg) + isert_unmap_data_buf(isert_conn, &wr->prot); +unmap_cmd: + if (fr_desc) { spin_lock_irqsave(&isert_conn->conn_lock, flags); - fr_desc = list_first_entry(&isert_conn->conn_fr_pool, - struct fast_reg_descriptor, list); - list_del(&fr_desc->list); + list_add_tail(&fr_desc->list, &isert_conn->conn_fr_pool); spin_unlock_irqrestore(&isert_conn->conn_lock, flags); - wr->fr_desc = fr_desc; - - ret = isert_fast_reg_mr(fr_desc, isert_conn, sg_start, - ib_sge, sg_nents, offset, data_len); - if (ret) { - list_add_tail(&fr_desc->list, &isert_conn->conn_fr_pool); - goto unmap_sg; - } } + isert_unmap_data_buf(isert_conn, &wr->data); - return 0; - -unmap_sg: - ib_dma_unmap_sg(ib_dev, sg_start, sg_nents, - (wr->iser_ib_op == ISER_IB_RDMA_WRITE) ? - DMA_TO_DEVICE : DMA_FROM_DEVICE); return ret; } @@ -2422,25 +2860,35 @@ isert_put_datain(struct iscsi_conn *conn, struct iscsi_cmd *cmd) return rc; } - /* - * Build isert_conn->tx_desc for iSCSI response PDU and attach - */ - isert_create_send_desc(isert_conn, isert_cmd, &isert_cmd->tx_desc); - iscsit_build_rsp_pdu(cmd, conn, true, (struct iscsi_scsi_rsp *) - &isert_cmd->tx_desc.iscsi_header); - isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); - isert_init_send_wr(isert_conn, isert_cmd, - &isert_cmd->tx_desc.send_wr, true); + if (se_cmd->prot_op == TARGET_PROT_NORMAL) { + /* + * Build isert_conn->tx_desc for iSCSI response PDU and attach + */ + isert_create_send_desc(isert_conn, isert_cmd, + &isert_cmd->tx_desc); + iscsit_build_rsp_pdu(cmd, conn, true, (struct iscsi_scsi_rsp *) + &isert_cmd->tx_desc.iscsi_header); + isert_init_tx_hdrs(isert_conn, &isert_cmd->tx_desc); + isert_init_send_wr(isert_conn, isert_cmd, + &isert_cmd->tx_desc.send_wr, true); + isert_cmd->rdma_wr.s_send_wr.next = &isert_cmd->tx_desc.send_wr; + wr->send_wr_num += 1; + } - atomic_add(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); + atomic_add(wr->send_wr_num, &isert_conn->post_send_buf_count); rc = ib_post_send(isert_conn->conn_qp, wr->send_wr, &wr_failed); if (rc) { pr_warn("ib_post_send() failed for IB_WR_RDMA_WRITE\n"); - atomic_sub(wr->send_wr_num + 1, &isert_conn->post_send_buf_count); + atomic_sub(wr->send_wr_num, &isert_conn->post_send_buf_count); } - pr_debug("Cmd: %p posted RDMA_WRITE + Response for iSER Data READ\n", - isert_cmd); + + if (se_cmd->prot_op == TARGET_PROT_NORMAL) + pr_debug("Cmd: %p posted RDMA_WRITE + Response for iSER Data " + "READ\n", isert_cmd); + else + pr_debug("Cmd: %p posted RDMA_WRITE for iSER Data READ\n", + isert_cmd); return 1; } @@ -2815,6 +3263,8 @@ static struct iscsit_transport iser_target_transport = { .iscsit_get_dataout = isert_get_dataout, .iscsit_queue_data_in = isert_put_datain, .iscsit_queue_status = isert_put_response, + .iscsit_aborted_task = isert_aborted_task, + .iscsit_get_sup_prot_ops = isert_get_sup_prot_ops, }; static int __init isert_init(void) diff --git a/drivers/infiniband/ulp/isert/ib_isert.h b/drivers/infiniband/ulp/isert/ib_isert.h index f6ae7f5dd408..4c072ae34c01 100644 --- a/drivers/infiniband/ulp/isert/ib_isert.h +++ b/drivers/infiniband/ulp/isert/ib_isert.h @@ -50,11 +50,35 @@ struct iser_tx_desc { struct ib_send_wr send_wr; } __packed; +enum isert_indicator { + ISERT_PROTECTED = 1 << 0, + ISERT_DATA_KEY_VALID = 1 << 1, + ISERT_PROT_KEY_VALID = 1 << 2, + ISERT_SIG_KEY_VALID = 1 << 3, +}; + +struct pi_context { + struct ib_mr *prot_mr; + struct ib_fast_reg_page_list *prot_frpl; + struct ib_mr *sig_mr; +}; + struct fast_reg_descriptor { - struct list_head list; - struct ib_mr *data_mr; - struct ib_fast_reg_page_list *data_frpl; - bool valid; + struct list_head list; + struct ib_mr *data_mr; + struct ib_fast_reg_page_list *data_frpl; + u8 ind; + struct pi_context *pi_ctx; +}; + +struct isert_data_buf { + struct scatterlist *sg; + int nents; + u32 sg_off; + u32 len; /* cur_rdma_length */ + u32 offset; + unsigned int dma_nents; + enum dma_data_direction dma_dir; }; struct isert_rdma_wr { @@ -63,12 +87,11 @@ struct isert_rdma_wr { enum iser_ib_op_code iser_ib_op; struct ib_sge *ib_sge; struct ib_sge s_ib_sge; - int num_sge; - struct scatterlist *sge; int send_wr_num; struct ib_send_wr *send_wr; struct ib_send_wr s_send_wr; - u32 cur_rdma_length; + struct isert_data_buf data; + struct isert_data_buf prot; struct fast_reg_descriptor *fr_desc; }; @@ -141,6 +164,7 @@ struct isert_cq_desc { struct isert_device { int use_fastreg; + bool pi_capable; int cqs_used; int refcount; int cq_active_qps[ISERT_MAX_CQ]; diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index 0e537d8d0e47..fe09f2788b15 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -1078,6 +1078,7 @@ static void srpt_unmap_sg_to_ib_sge(struct srpt_rdma_ch *ch, static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, struct srpt_send_ioctx *ioctx) { + struct ib_device *dev = ch->sport->sdev->device; struct se_cmd *cmd; struct scatterlist *sg, *sg_orig; int sg_cnt; @@ -1124,7 +1125,7 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, db = ioctx->rbufs; tsize = cmd->data_length; - dma_len = sg_dma_len(&sg[0]); + dma_len = ib_sg_dma_len(dev, &sg[0]); riu = ioctx->rdma_ius; /* @@ -1155,7 +1156,8 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, ++j; if (j < count) { sg = sg_next(sg); - dma_len = sg_dma_len(sg); + dma_len = ib_sg_dma_len( + dev, sg); } } } else { @@ -1192,8 +1194,8 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, tsize = cmd->data_length; riu = ioctx->rdma_ius; sg = sg_orig; - dma_len = sg_dma_len(&sg[0]); - dma_addr = sg_dma_address(&sg[0]); + dma_len = ib_sg_dma_len(dev, &sg[0]); + dma_addr = ib_sg_dma_address(dev, &sg[0]); /* this second loop is really mapped sg_addres to rdma_iu->ib_sge */ for (i = 0, j = 0; @@ -1216,8 +1218,10 @@ static int srpt_map_sg_to_ib_sge(struct srpt_rdma_ch *ch, ++j; if (j < count) { sg = sg_next(sg); - dma_len = sg_dma_len(sg); - dma_addr = sg_dma_address(sg); + dma_len = ib_sg_dma_len( + dev, sg); + dma_addr = ib_sg_dma_address( + dev, sg); } } } else { @@ -2580,7 +2584,7 @@ static int srpt_cm_req_recv(struct ib_cm_id *cm_id, goto destroy_ib; } - ch->sess = transport_init_session(); + ch->sess = transport_init_session(TARGET_PROT_NORMAL); if (IS_ERR(ch->sess)) { rej->reason = __constant_cpu_to_be32( SRP_LOGIN_REJ_INSUFFICIENT_RESOURCES); @@ -3081,6 +3085,14 @@ static void srpt_queue_tm_rsp(struct se_cmd *cmd) srpt_queue_response(cmd); } +static void srpt_aborted_task(struct se_cmd *cmd) +{ + struct srpt_send_ioctx *ioctx = container_of(cmd, + struct srpt_send_ioctx, cmd); + + srpt_unmap_sg_to_ib_sge(ioctx->ch, ioctx); +} + static int srpt_queue_status(struct se_cmd *cmd) { struct srpt_send_ioctx *ioctx; @@ -3928,6 +3940,7 @@ static struct target_core_fabric_ops srpt_template = { .queue_data_in = srpt_queue_data_in, .queue_status = srpt_queue_status, .queue_tm_rsp = srpt_queue_tm_rsp, + .aborted_task = srpt_aborted_task, /* * Setup function pointers for generic logic in * target_core_fabric_configfs.c diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig index 44c358ecf5a1..6de9dfbf61c1 100644 --- a/drivers/leds/Kconfig +++ b/drivers/leds/Kconfig @@ -416,7 +416,7 @@ config LEDS_MC13783 depends on MFD_MC13XXX help This option enable support for on-chip LED drivers found - on Freescale Semiconductor MC13783/MC13892 PMIC. + on Freescale Semiconductor MC13783/MC13892/MC34708 PMIC. config LEDS_NS2 tristate "LED support for Network Space v2 GPIO LEDs" @@ -474,7 +474,7 @@ config LEDS_LM355x config LEDS_OT200 tristate "LED support for the Bachmann OT200" - depends on LEDS_CLASS && HAS_IOMEM + depends on LEDS_CLASS && HAS_IOMEM && (X86_32 || COMPILE_TEST) help This option enables support for the LEDs on the Bachmann OT200. Say Y to enable LEDs on the Bachmann OT200. diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c index ce8921a753a3..71b40d3bf776 100644 --- a/drivers/leds/led-core.c +++ b/drivers/leds/led-core.c @@ -39,9 +39,11 @@ static void led_set_software_blink(struct led_classdev *led_cdev, led_cdev->blink_delay_on = delay_on; led_cdev->blink_delay_off = delay_off; - /* never on - don't blink */ - if (!delay_on) + /* never on - just set to off */ + if (!delay_on) { + __led_set_brightness(led_cdev, LED_OFF); return; + } /* never off - just set to brightness */ if (!delay_off) { diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c index e387f41a9cb7..c3734f10fdd5 100644 --- a/drivers/leds/led-triggers.c +++ b/drivers/leds/led-triggers.c @@ -13,7 +13,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/list.h> #include <linux/spinlock.h> #include <linux/device.h> @@ -220,9 +219,12 @@ void led_trigger_unregister(struct led_trigger *trig) { struct led_classdev *led_cdev; + if (list_empty_careful(&trig->next_trig)) + return; + /* Remove from the list of led triggers */ down_write(&triggers_list_lock); - list_del(&trig->next_trig); + list_del_init(&trig->next_trig); up_write(&triggers_list_lock); /* Remove anyone actively using this trigger */ diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 5f588c0a376e..d1e1bca90d11 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -11,7 +11,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/i2c.h> diff --git a/drivers/leds/leds-adp5520.c b/drivers/leds/leds-adp5520.c index 7e311a120b11..86b5bdb0c773 100644 --- a/drivers/leds/leds-adp5520.c +++ b/drivers/leds/leds-adp5520.c @@ -15,7 +15,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/workqueue.h> diff --git a/drivers/leds/leds-asic3.c b/drivers/leds/leds-asic3.c index 6de216a89a0c..70c74a7f0dfe 100644 --- a/drivers/leds/leds-asic3.c +++ b/drivers/leds/leds-asic3.c @@ -7,7 +7,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/slab.h> diff --git a/drivers/leds/leds-blinkm.c b/drivers/leds/leds-blinkm.c index 66d0a57db221..d0452b099aee 100644 --- a/drivers/leds/leds-blinkm.c +++ b/drivers/leds/leds-blinkm.c @@ -18,7 +18,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> @@ -444,7 +443,7 @@ static void led_work(struct work_struct *work) { int ret; struct blinkm_led *led; - struct blinkm_data *data ; + struct blinkm_data *data; struct blinkm_work *blm_work = work_to_blmwork(work); led = blm_work->blinkm_led; diff --git a/drivers/leds/leds-clevo-mail.c b/drivers/leds/leds-clevo-mail.c index d93e2455da5c..f58a354428e3 100644 --- a/drivers/leds/leds-clevo-mail.c +++ b/drivers/leds/leds-clevo-mail.c @@ -19,7 +19,7 @@ MODULE_AUTHOR("Márton Németh <nm127@freemail.hu>"); MODULE_DESCRIPTION("Clevo mail LED driver"); MODULE_LICENSE("GPL"); -static bool __initdata nodetect; +static bool nodetect; module_param_named(nodetect, nodetect, bool, 0); MODULE_PARM_DESC(nodetect, "Skip DMI hardware detection"); @@ -153,7 +153,7 @@ static struct led_classdev clevo_mail_led = { .flags = LED_CORE_SUSPENDRESUME, }; -static int clevo_mail_led_probe(struct platform_device *pdev) +static int __init clevo_mail_led_probe(struct platform_device *pdev) { return led_classdev_register(&pdev->dev, &clevo_mail_led); } @@ -165,7 +165,6 @@ static int clevo_mail_led_remove(struct platform_device *pdev) } static struct platform_driver clevo_mail_led_driver = { - .probe = clevo_mail_led_probe, .remove = clevo_mail_led_remove, .driver = { .name = KBUILD_MODNAME, diff --git a/drivers/leds/leds-cobalt-qube.c b/drivers/leds/leds-cobalt-qube.c index 8abcb66db01c..910339d86edf 100644 --- a/drivers/leds/leds-cobalt-qube.c +++ b/drivers/leds/leds-cobalt-qube.c @@ -3,7 +3,6 @@ * * Control the Cobalt Qube/RaQ front LED */ -#include <linux/init.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/leds.h> diff --git a/drivers/leds/leds-da903x.c b/drivers/leds/leds-da903x.c index 2a4b87f8091a..35dffb100388 100644 --- a/drivers/leds/leds-da903x.c +++ b/drivers/leds/leds-da903x.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/workqueue.h> diff --git a/drivers/leds/leds-da9052.c b/drivers/leds/leds-da9052.c index 865d4faf874a..01486adc7f8b 100644 --- a/drivers/leds/leds-da9052.c +++ b/drivers/leds/leds-da9052.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/workqueue.h> diff --git a/drivers/leds/leds-fsg.c b/drivers/leds/leds-fsg.c index b4d5a44cc41b..2b4dc738dcd6 100644 --- a/drivers/leds/leds-fsg.c +++ b/drivers/leds/leds-fsg.c @@ -16,7 +16,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/module.h> diff --git a/drivers/leds/leds-gpio.c b/drivers/leds/leds-gpio.c index 78b0e273a903..57ff20fecf57 100644 --- a/drivers/leds/leds-gpio.c +++ b/drivers/leds/leds-gpio.c @@ -11,7 +11,6 @@ * */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/gpio.h> #include <linux/leds.h> @@ -204,6 +203,9 @@ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) led.default_state = LEDS_GPIO_DEFSTATE_OFF; } + if (of_get_property(child, "retain-state-suspended", NULL)) + led.retain_state_suspended = 1; + ret = create_gpio_led(&led, &priv->leds[priv->num_leds++], &pdev->dev, NULL); if (ret < 0) { @@ -224,6 +226,8 @@ static const struct of_device_id of_gpio_leds_match[] = { { .compatible = "gpio-leds", }, {}, }; + +MODULE_DEVICE_TABLE(of, of_gpio_leds_match); #else /* CONFIG_OF_GPIO */ static struct gpio_leds_priv *gpio_leds_create_of(struct platform_device *pdev) { diff --git a/drivers/leds/leds-hp6xx.c b/drivers/leds/leds-hp6xx.c index 366b6055e330..d61a98896c71 100644 --- a/drivers/leds/leds-hp6xx.c +++ b/drivers/leds/leds-hp6xx.c @@ -12,7 +12,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <asm/hd64461.h> diff --git a/drivers/leds/leds-lm3533.c b/drivers/leds/leds-lm3533.c index 027ede73b80d..e2c642c1169b 100644 --- a/drivers/leds/leds-lm3533.c +++ b/drivers/leds/leds-lm3533.c @@ -12,7 +12,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/leds.h> #include <linux/mfd/core.h> #include <linux/mutex.h> diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c index 2ec34cfcedce..8ca197af2864 100644 --- a/drivers/leds/leds-lp5521.c +++ b/drivers/leds/leds-lp5521.c @@ -25,7 +25,6 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/i2c.h> -#include <linux/init.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/mutex.h> diff --git a/drivers/leds/leds-lp5523.c b/drivers/leds/leds-lp5523.c index 4ade66a2d9d4..cb5ed82994ba 100644 --- a/drivers/leds/leds-lp5523.c +++ b/drivers/leds/leds-lp5523.c @@ -25,7 +25,6 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/i2c.h> -#include <linux/init.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/mutex.h> diff --git a/drivers/leds/leds-lp5562.c b/drivers/leds/leds-lp5562.c index bf006f4e44a0..ca85724ab138 100644 --- a/drivers/leds/leds-lp5562.c +++ b/drivers/leds/leds-lp5562.c @@ -13,7 +13,6 @@ #include <linux/delay.h> #include <linux/firmware.h> #include <linux/i2c.h> -#include <linux/init.h> #include <linux/leds.h> #include <linux/module.h> #include <linux/mutex.h> @@ -347,9 +346,9 @@ static void lp5562_write_program_memory(struct lp55xx_chip *chip, /* check the size of program count */ static inline bool _is_pc_overflow(struct lp55xx_predef_pattern *ptn) { - return (ptn->size_r >= LP5562_PROGRAM_LENGTH || - ptn->size_g >= LP5562_PROGRAM_LENGTH || - ptn->size_b >= LP5562_PROGRAM_LENGTH); + return ptn->size_r >= LP5562_PROGRAM_LENGTH || + ptn->size_g >= LP5562_PROGRAM_LENGTH || + ptn->size_b >= LP5562_PROGRAM_LENGTH; } static int lp5562_run_predef_led_pattern(struct lp55xx_chip *chip, int mode) diff --git a/drivers/leds/leds-lt3593.c b/drivers/leds/leds-lt3593.c index 3417e5be7b57..059f5b1f3553 100644 --- a/drivers/leds/leds-lt3593.c +++ b/drivers/leds/leds-lt3593.c @@ -17,7 +17,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/workqueue.h> diff --git a/drivers/leds/leds-mc13783.c b/drivers/leds/leds-mc13783.c index ca87a1b4a0db..f1db88e25138 100644 --- a/drivers/leds/leds-mc13783.c +++ b/drivers/leds/leds-mc13783.c @@ -1,5 +1,5 @@ /* - * LEDs driver for Freescale MC13783/MC13892 + * LEDs driver for Freescale MC13783/MC13892/MC34708 * * Copyright (C) 2010 Philippe Rétornaz * @@ -17,57 +17,56 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> +#include <linux/of.h> #include <linux/workqueue.h> #include <linux/mfd/mc13xxx.h> -#define MC13XXX_REG_LED_CONTROL(x) (51 + (x)) - struct mc13xxx_led_devtype { int led_min; int led_max; int num_regs; + u32 ledctrl_base; }; struct mc13xxx_led { struct led_classdev cdev; struct work_struct work; - struct mc13xxx *master; enum led_brightness new_brightness; int id; + struct mc13xxx_leds *leds; }; struct mc13xxx_leds { + struct mc13xxx *master; struct mc13xxx_led_devtype *devtype; int num_leds; - struct mc13xxx_led led[0]; + struct mc13xxx_led *led; }; +static unsigned int mc13xxx_max_brightness(int id) +{ + if (id >= MC13783_LED_MD && id <= MC13783_LED_KP) + return 0x0f; + else if (id >= MC13783_LED_R1 && id <= MC13783_LED_B3) + return 0x1f; + + return 0x3f; +} + static void mc13xxx_led_work(struct work_struct *work) { struct mc13xxx_led *led = container_of(work, struct mc13xxx_led, work); - int reg, mask, value, bank, off, shift; + struct mc13xxx_leds *leds = led->leds; + unsigned int reg, bank, off, shift; switch (led->id) { case MC13783_LED_MD: - reg = MC13XXX_REG_LED_CONTROL(2); - shift = 9; - mask = 0x0f; - value = led->new_brightness >> 4; - break; case MC13783_LED_AD: - reg = MC13XXX_REG_LED_CONTROL(2); - shift = 13; - mask = 0x0f; - value = led->new_brightness >> 4; - break; case MC13783_LED_KP: - reg = MC13XXX_REG_LED_CONTROL(2); - shift = 17; - mask = 0x0f; - value = led->new_brightness >> 4; + reg = 2; + shift = 9 + (led->id - MC13783_LED_MD) * 4; break; case MC13783_LED_R1: case MC13783_LED_G1: @@ -80,44 +79,35 @@ static void mc13xxx_led_work(struct work_struct *work) case MC13783_LED_B3: off = led->id - MC13783_LED_R1; bank = off / 3; - reg = MC13XXX_REG_LED_CONTROL(3) + bank; + reg = 3 + bank; shift = (off - bank * 3) * 5 + 6; - value = led->new_brightness >> 3; - mask = 0x1f; break; case MC13892_LED_MD: - reg = MC13XXX_REG_LED_CONTROL(0); - shift = 3; - mask = 0x3f; - value = led->new_brightness >> 2; - break; case MC13892_LED_AD: - reg = MC13XXX_REG_LED_CONTROL(0); - shift = 15; - mask = 0x3f; - value = led->new_brightness >> 2; - break; case MC13892_LED_KP: - reg = MC13XXX_REG_LED_CONTROL(1); - shift = 3; - mask = 0x3f; - value = led->new_brightness >> 2; + reg = (led->id - MC13892_LED_MD) / 2; + shift = 3 + (led->id - MC13892_LED_MD) * 12; break; case MC13892_LED_R: case MC13892_LED_G: case MC13892_LED_B: off = led->id - MC13892_LED_R; bank = off / 2; - reg = MC13XXX_REG_LED_CONTROL(2) + bank; + reg = 2 + bank; shift = (off - bank * 2) * 12 + 3; - value = led->new_brightness >> 2; - mask = 0x3f; + break; + case MC34708_LED_R: + case MC34708_LED_G: + reg = 0; + shift = 3 + (led->id - MC34708_LED_R) * 12; break; default: BUG(); } - mc13xxx_reg_rmw(led->master, reg, mask << shift, value << shift); + mc13xxx_reg_rmw(leds->master, leds->devtype->ledctrl_base + reg, + mc13xxx_max_brightness(led->id) << shift, + led->new_brightness << shift); } static void mc13xxx_led_set(struct led_classdev *led_cdev, @@ -130,47 +120,121 @@ static void mc13xxx_led_set(struct led_classdev *led_cdev, schedule_work(&led->work); } -static int __init mc13xxx_led_probe(struct platform_device *pdev) +#ifdef CONFIG_OF +static struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( + struct platform_device *pdev) { - struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent); - struct mc13xxx_led_devtype *devtype = - (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data; - struct mc13xxx_leds *leds; - int i, id, num_leds, ret = -ENODATA; - u32 reg, init_led = 0; + struct mc13xxx_leds *leds = platform_get_drvdata(pdev); + struct mc13xxx_leds_platform_data *pdata; + struct device_node *parent, *child; + struct device *dev = &pdev->dev; + int i = 0, ret = -ENODATA; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + of_node_get(dev->parent->of_node); + + parent = of_find_node_by_name(dev->parent->of_node, "leds"); + if (!parent) + goto out_node_put; - if (!pdata) { - dev_err(&pdev->dev, "Missing platform data\n"); - return -ENODEV; + ret = of_property_read_u32_array(parent, "led-control", + pdata->led_control, + leds->devtype->num_regs); + if (ret) + goto out_node_put; + + pdata->num_leds = of_get_child_count(parent); + + pdata->led = devm_kzalloc(dev, pdata->num_leds * sizeof(*pdata->led), + GFP_KERNEL); + if (!pdata->led) { + ret = -ENOMEM; + goto out_node_put; } - num_leds = pdata->num_leds; + for_each_child_of_node(parent, child) { + const char *str; + u32 tmp; - if ((num_leds < 1) || - (num_leds > (devtype->led_max - devtype->led_min + 1))) { - dev_err(&pdev->dev, "Invalid LED count %d\n", num_leds); - return -EINVAL; + if (of_property_read_u32(child, "reg", &tmp)) + continue; + pdata->led[i].id = leds->devtype->led_min + tmp; + + if (!of_property_read_string(child, "label", &str)) + pdata->led[i].name = str; + if (!of_property_read_string(child, "linux,default-trigger", + &str)) + pdata->led[i].default_trigger = str; + + i++; } - leds = devm_kzalloc(&pdev->dev, num_leds * sizeof(struct mc13xxx_led) + - sizeof(struct mc13xxx_leds), GFP_KERNEL); + pdata->num_leds = i; + ret = i > 0 ? 0 : -ENODATA; + +out_node_put: + of_node_put(parent); + + return ret ? ERR_PTR(ret) : pdata; +} +#else +static inline struct mc13xxx_leds_platform_data __init *mc13xxx_led_probe_dt( + struct platform_device *pdev) +{ + return ERR_PTR(-ENOSYS); +} +#endif + +static int __init mc13xxx_led_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mc13xxx_leds_platform_data *pdata = dev_get_platdata(dev); + struct mc13xxx *mcdev = dev_get_drvdata(dev->parent); + struct mc13xxx_led_devtype *devtype = + (struct mc13xxx_led_devtype *)pdev->id_entry->driver_data; + struct mc13xxx_leds *leds; + int i, id, ret = -ENODATA; + u32 init_led = 0; + + leds = devm_kzalloc(dev, sizeof(*leds), GFP_KERNEL); if (!leds) return -ENOMEM; leds->devtype = devtype; - leds->num_leds = num_leds; + leds->master = mcdev; platform_set_drvdata(pdev, leds); + if (dev->parent->of_node) { + pdata = mc13xxx_led_probe_dt(pdev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } else if (!pdata) + return -ENODATA; + + leds->num_leds = pdata->num_leds; + + if ((leds->num_leds < 1) || + (leds->num_leds > (devtype->led_max - devtype->led_min + 1))) { + dev_err(dev, "Invalid LED count %d\n", leds->num_leds); + return -EINVAL; + } + + leds->led = devm_kzalloc(dev, leds->num_leds * sizeof(*leds->led), + GFP_KERNEL); + if (!leds->led) + return -ENOMEM; + for (i = 0; i < devtype->num_regs; i++) { - reg = pdata->led_control[i]; - WARN_ON(reg >= (1 << 24)); - ret = mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), reg); + ret = mc13xxx_reg_write(mcdev, leds->devtype->ledctrl_base + i, + pdata->led_control[i]); if (ret) return ret; } - for (i = 0; i < num_leds; i++) { + for (i = 0; i < leds->num_leds; i++) { const char *name, *trig; ret = -EINVAL; @@ -180,30 +244,29 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) trig = pdata->led[i].default_trigger; if ((id > devtype->led_max) || (id < devtype->led_min)) { - dev_err(&pdev->dev, "Invalid ID %i\n", id); + dev_err(dev, "Invalid ID %i\n", id); break; } if (init_led & (1 << id)) { - dev_warn(&pdev->dev, - "LED %i already initialized\n", id); + dev_warn(dev, "LED %i already initialized\n", id); break; } init_led |= 1 << id; leds->led[i].id = id; - leds->led[i].master = mcdev; + leds->led[i].leds = leds; leds->led[i].cdev.name = name; leds->led[i].cdev.default_trigger = trig; + leds->led[i].cdev.flags = LED_CORE_SUSPENDRESUME; leds->led[i].cdev.brightness_set = mc13xxx_led_set; - leds->led[i].cdev.brightness = LED_OFF; + leds->led[i].cdev.max_brightness = mc13xxx_max_brightness(id); INIT_WORK(&leds->led[i].work, mc13xxx_led_work); - ret = led_classdev_register(pdev->dev.parent, - &leds->led[i].cdev); + ret = led_classdev_register(dev->parent, &leds->led[i].cdev); if (ret) { - dev_err(&pdev->dev, "Failed to register LED %i\n", id); + dev_err(dev, "Failed to register LED %i\n", id); break; } } @@ -219,7 +282,6 @@ static int __init mc13xxx_led_probe(struct platform_device *pdev) static int mc13xxx_led_remove(struct platform_device *pdev) { - struct mc13xxx *mcdev = dev_get_drvdata(pdev->dev.parent); struct mc13xxx_leds *leds = platform_get_drvdata(pdev); int i; @@ -228,9 +290,6 @@ static int mc13xxx_led_remove(struct platform_device *pdev) cancel_work_sync(&leds->led[i].work); } - for (i = 0; i < leds->devtype->num_regs; i++) - mc13xxx_reg_write(mcdev, MC13XXX_REG_LED_CONTROL(i), 0); - return 0; } @@ -238,17 +297,27 @@ static const struct mc13xxx_led_devtype mc13783_led_devtype = { .led_min = MC13783_LED_MD, .led_max = MC13783_LED_B3, .num_regs = 6, + .ledctrl_base = 51, }; static const struct mc13xxx_led_devtype mc13892_led_devtype = { .led_min = MC13892_LED_MD, .led_max = MC13892_LED_B, .num_regs = 4, + .ledctrl_base = 51, +}; + +static const struct mc13xxx_led_devtype mc34708_led_devtype = { + .led_min = MC34708_LED_R, + .led_max = MC34708_LED_G, + .num_regs = 1, + .ledctrl_base = 54, }; static const struct platform_device_id mc13xxx_led_id_table[] = { { "mc13783-led", (kernel_ulong_t)&mc13783_led_devtype, }, { "mc13892-led", (kernel_ulong_t)&mc13892_led_devtype, }, + { "mc34708-led", (kernel_ulong_t)&mc34708_led_devtype, }, { } }; MODULE_DEVICE_TABLE(platform, mc13xxx_led_id_table); diff --git a/drivers/leds/leds-netxbig.c b/drivers/leds/leds-netxbig.c index 2f9f141084ba..e97f443a6e07 100644 --- a/drivers/leds/leds-netxbig.c +++ b/drivers/leds/leds-netxbig.c @@ -21,7 +21,6 @@ */ #include <linux/module.h> -#include <linux/init.h> #include <linux/irq.h> #include <linux/slab.h> #include <linux/spinlock.h> diff --git a/drivers/leds/leds-ns2.c b/drivers/leds/leds-ns2.c index c7a4230233ea..efa625883c83 100644 --- a/drivers/leds/leds-ns2.c +++ b/drivers/leds/leds-ns2.c @@ -23,7 +23,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/gpio.h> diff --git a/drivers/leds/leds-ot200.c b/drivers/leds/leds-ot200.c index 98cae529373f..c9d906098466 100644 --- a/drivers/leds/leds-ot200.c +++ b/drivers/leds/leds-ot200.c @@ -8,7 +8,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/leds.h> diff --git a/drivers/leds/leds-pwm.c b/drivers/leds/leds-pwm.c index 605047428b5a..7d0aaed1e23a 100644 --- a/drivers/leds/leds-pwm.c +++ b/drivers/leds/leds-pwm.c @@ -14,7 +14,6 @@ #include <linux/module.h> #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/of_platform.h> #include <linux/fb.h> @@ -84,6 +83,15 @@ static inline size_t sizeof_pwm_leds_priv(int num_leds) (sizeof(struct led_pwm_data) * num_leds); } +static void led_pwm_cleanup(struct led_pwm_priv *priv) +{ + while (priv->num_leds--) { + led_classdev_unregister(&priv->leds[priv->num_leds].cdev); + if (priv->leds[priv->num_leds].can_sleep) + cancel_work_sync(&priv->leds[priv->num_leds].work); + } +} + static int led_pwm_create_of(struct platform_device *pdev, struct led_pwm_priv *priv) { @@ -131,8 +139,7 @@ static int led_pwm_create_of(struct platform_device *pdev, return 0; err: - while (priv->num_leds--) - led_classdev_unregister(&priv->leds[priv->num_leds].cdev); + led_pwm_cleanup(priv); return ret; } @@ -200,8 +207,8 @@ static int led_pwm_probe(struct platform_device *pdev) return 0; err: - while (i--) - led_classdev_unregister(&priv->leds[i].cdev); + priv->num_leds = i; + led_pwm_cleanup(priv); return ret; } @@ -209,13 +216,8 @@ err: static int led_pwm_remove(struct platform_device *pdev) { struct led_pwm_priv *priv = platform_get_drvdata(pdev); - int i; - for (i = 0; i < priv->num_leds; i++) { - led_classdev_unregister(&priv->leds[i].cdev); - if (priv->leds[i].can_sleep) - cancel_work_sync(&priv->leds[i].work); - } + led_pwm_cleanup(priv); return 0; } diff --git a/drivers/leds/leds-s3c24xx.c b/drivers/leds/leds-s3c24xx.c index 98174e7240ee..28988b7b4fab 100644 --- a/drivers/leds/leds-s3c24xx.c +++ b/drivers/leds/leds-s3c24xx.c @@ -12,7 +12,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/gpio.h> diff --git a/drivers/leds/leds-ss4200.c b/drivers/leds/leds-ss4200.c index 5b8f938a8d73..2eb3ef62962b 100644 --- a/drivers/leds/leds-ss4200.c +++ b/drivers/leds/leds-ss4200.c @@ -63,7 +63,7 @@ MODULE_LICENSE("GPL"); /* * PCI ID of the Intel ICH7 LPC Device within which the GPIO block lives. */ -static DEFINE_PCI_DEVICE_TABLE(ich7_lpc_pci_id) = { +static const struct pci_device_id ich7_lpc_pci_id[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_30) }, @@ -78,7 +78,7 @@ static int __init ss4200_led_dmi_callback(const struct dmi_system_id *id) return 1; } -static bool __initdata nodetect; +static bool nodetect; module_param_named(nodetect, nodetect, bool, 0); MODULE_PARM_DESC(nodetect, "Skip DMI-based hardware detection"); diff --git a/drivers/leds/leds-wm831x-status.c b/drivers/leds/leds-wm831x-status.c index 0a1a13f3a6a5..e72c974142d0 100644 --- a/drivers/leds/leds-wm831x-status.c +++ b/drivers/leds/leds-wm831x-status.c @@ -10,7 +10,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/leds.h> diff --git a/drivers/leds/leds-wm8350.c b/drivers/leds/leds-wm8350.c index 3f75fd22fd49..4133ffe29015 100644 --- a/drivers/leds/leds-wm8350.c +++ b/drivers/leds/leds-wm8350.c @@ -10,7 +10,6 @@ */ #include <linux/kernel.h> -#include <linux/init.h> #include <linux/platform_device.h> #include <linux/leds.h> #include <linux/err.h> diff --git a/drivers/leds/trigger/ledtrig-cpu.c b/drivers/leds/trigger/ledtrig-cpu.c index 118335eccc56..1c3ee9fcaf34 100644 --- a/drivers/leds/trigger/ledtrig-cpu.c +++ b/drivers/leds/trigger/ledtrig-cpu.c @@ -26,6 +26,7 @@ #include <linux/percpu.h> #include <linux/syscore_ops.h> #include <linux/rwsem.h> +#include <linux/cpu.h> #include "../leds.h" #define MAX_NAME_LEN 8 @@ -92,6 +93,26 @@ static struct syscore_ops ledtrig_cpu_syscore_ops = { .resume = ledtrig_cpu_syscore_resume, }; +static int ledtrig_cpu_notify(struct notifier_block *self, + unsigned long action, void *hcpu) +{ + switch (action & ~CPU_TASKS_FROZEN) { + case CPU_STARTING: + ledtrig_cpu(CPU_LED_START); + break; + case CPU_DYING: + ledtrig_cpu(CPU_LED_STOP); + break; + } + + return NOTIFY_OK; +} + + +static struct notifier_block ledtrig_cpu_nb = { + .notifier_call = ledtrig_cpu_notify, +}; + static int __init ledtrig_cpu_init(void) { int cpu; @@ -113,6 +134,7 @@ static int __init ledtrig_cpu_init(void) } register_syscore_ops(&ledtrig_cpu_syscore_ops); + register_cpu_notifier(&ledtrig_cpu_nb); pr_info("ledtrig-cpu: registered to indicate activity on CPUs\n"); @@ -124,6 +146,8 @@ static void __exit ledtrig_cpu_exit(void) { int cpu; + unregister_cpu_notifier(&ledtrig_cpu_nb); + for_each_possible_cpu(cpu) { struct led_trigger_cpu *trig = &per_cpu(cpu_trig, cpu); diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 4195a01b1535..9a8e66ae04f5 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -1988,7 +1988,6 @@ location_store(struct mddev *mddev, const char *buf, size_t len) if (mddev->bitmap_info.file) { struct file *f = mddev->bitmap_info.file; mddev->bitmap_info.file = NULL; - restore_bitmap_write_access(f); fput(f); } } else { diff --git a/drivers/md/md.c b/drivers/md/md.c index 4ad5cc4e63e8..8fda38d23e38 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -5181,32 +5181,6 @@ static int restart_array(struct mddev *mddev) return 0; } -/* similar to deny_write_access, but accounts for our holding a reference - * to the file ourselves */ -static int deny_bitmap_write_access(struct file * file) -{ - struct inode *inode = file->f_mapping->host; - - spin_lock(&inode->i_lock); - if (atomic_read(&inode->i_writecount) > 1) { - spin_unlock(&inode->i_lock); - return -ETXTBSY; - } - atomic_set(&inode->i_writecount, -1); - spin_unlock(&inode->i_lock); - - return 0; -} - -void restore_bitmap_write_access(struct file *file) -{ - struct inode *inode = file->f_mapping->host; - - spin_lock(&inode->i_lock); - atomic_set(&inode->i_writecount, 1); - spin_unlock(&inode->i_lock); -} - static void md_clean(struct mddev *mddev) { mddev->array_sectors = 0; @@ -5427,7 +5401,6 @@ static int do_md_stop(struct mddev * mddev, int mode, bitmap_destroy(mddev); if (mddev->bitmap_info.file) { - restore_bitmap_write_access(mddev->bitmap_info.file); fput(mddev->bitmap_info.file); mddev->bitmap_info.file = NULL; } @@ -5979,7 +5952,7 @@ abort_export: static int set_bitmap_file(struct mddev *mddev, int fd) { - int err; + int err = 0; if (mddev->pers) { if (!mddev->pers->quiesce) @@ -5991,6 +5964,7 @@ static int set_bitmap_file(struct mddev *mddev, int fd) if (fd >= 0) { + struct inode *inode; if (mddev->bitmap) return -EEXIST; /* cannot add when bitmap is present */ mddev->bitmap_info.file = fget(fd); @@ -6001,10 +5975,21 @@ static int set_bitmap_file(struct mddev *mddev, int fd) return -EBADF; } - err = deny_bitmap_write_access(mddev->bitmap_info.file); - if (err) { + inode = mddev->bitmap_info.file->f_mapping->host; + if (!S_ISREG(inode->i_mode)) { + printk(KERN_ERR "%s: error: bitmap file must be a regular file\n", + mdname(mddev)); + err = -EBADF; + } else if (!(mddev->bitmap_info.file->f_mode & FMODE_WRITE)) { + printk(KERN_ERR "%s: error: bitmap file must open for write\n", + mdname(mddev)); + err = -EBADF; + } else if (atomic_read(&inode->i_writecount) != 1) { printk(KERN_ERR "%s: error: bitmap file is already in use\n", mdname(mddev)); + err = -EBUSY; + } + if (err) { fput(mddev->bitmap_info.file); mddev->bitmap_info.file = NULL; return err; @@ -6027,10 +6012,8 @@ static int set_bitmap_file(struct mddev *mddev, int fd) mddev->pers->quiesce(mddev, 0); } if (fd < 0) { - if (mddev->bitmap_info.file) { - restore_bitmap_write_access(mddev->bitmap_info.file); + if (mddev->bitmap_info.file) fput(mddev->bitmap_info.file); - } mddev->bitmap_info.file = NULL; } @@ -7182,11 +7165,14 @@ static int md_seq_open(struct inode *inode, struct file *file) return error; } +static int md_unloading; static unsigned int mdstat_poll(struct file *filp, poll_table *wait) { struct seq_file *seq = filp->private_data; int mask; + if (md_unloading) + return POLLIN|POLLRDNORM|POLLERR|POLLPRI;; poll_wait(filp, &md_event_waiters, wait); /* always allow read */ @@ -8672,6 +8658,7 @@ static __exit void md_exit(void) { struct mddev *mddev; struct list_head *tmp; + int delay = 1; blk_unregister_region(MKDEV(MD_MAJOR,0), 1U << MINORBITS); blk_unregister_region(MKDEV(mdp_major,0), 1U << MINORBITS); @@ -8680,7 +8667,19 @@ static __exit void md_exit(void) unregister_blkdev(mdp_major, "mdp"); unregister_reboot_notifier(&md_notifier); unregister_sysctl_table(raid_table_header); + + /* We cannot unload the modules while some process is + * waiting for us in select() or poll() - wake them up + */ + md_unloading = 1; + while (waitqueue_active(&md_event_waiters)) { + /* not safe to leave yet */ + wake_up(&md_event_waiters); + msleep(delay); + delay += delay; + } remove_proc_entry("mdstat", NULL); + for_each_mddev(mddev, tmp) { export_array(mddev); mddev->hold_active = 0; diff --git a/drivers/md/md.h b/drivers/md/md.h index 07bba96de260..a49d991f3fe1 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -605,7 +605,6 @@ extern int md_check_no_bitmap(struct mddev *mddev); extern int md_integrity_register(struct mddev *mddev); extern void md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev); extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale); -extern void restore_bitmap_write_access(struct file *file); extern void mddev_init(struct mddev *mddev); extern int md_run(struct mddev *mddev); diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 4a6ca1cb2e78..56e24c072b62 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -97,6 +97,7 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data) struct pool_info *pi = data; struct r1bio *r1_bio; struct bio *bio; + int need_pages; int i, j; r1_bio = r1bio_pool_alloc(gfp_flags, pi); @@ -119,15 +120,15 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data) * RESYNC_PAGES for each bio. */ if (test_bit(MD_RECOVERY_REQUESTED, &pi->mddev->recovery)) - j = pi->raid_disks; + need_pages = pi->raid_disks; else - j = 1; - while(j--) { + need_pages = 1; + for (j = 0; j < need_pages; j++) { bio = r1_bio->bios[j]; bio->bi_vcnt = RESYNC_PAGES; if (bio_alloc_pages(bio, gfp_flags)) - goto out_free_bio; + goto out_free_pages; } /* If not user-requests, copy the page pointers to all bios */ if (!test_bit(MD_RECOVERY_REQUESTED, &pi->mddev->recovery)) { @@ -141,6 +142,14 @@ static void * r1buf_pool_alloc(gfp_t gfp_flags, void *data) return r1_bio; +out_free_pages: + while (--j >= 0) { + struct bio_vec *bv; + + bio_for_each_segment_all(bv, r1_bio->bios[j], i) + __free_page(bv->bv_page); + } + out_free_bio: while (++j < pi->raid_disks) bio_put(r1_bio->bios[j]); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 16f5c21963db..25247a852912 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -679,14 +679,9 @@ get_active_stripe(struct r5conf *conf, sector_t sector, init_stripe(sh, sector, previous); atomic_inc(&sh->count); } - } else { + } else if (!atomic_inc_not_zero(&sh->count)) { spin_lock(&conf->device_lock); - if (atomic_read(&sh->count)) { - BUG_ON(!list_empty(&sh->lru) - && !test_bit(STRIPE_EXPANDING, &sh->state) - && !test_bit(STRIPE_ON_UNPLUG_LIST, &sh->state) - ); - } else { + if (!atomic_read(&sh->count)) { if (!test_bit(STRIPE_HANDLE, &sh->state)) atomic_inc(&conf->active_stripes); BUG_ON(list_empty(&sh->lru) && @@ -4552,6 +4547,8 @@ static void make_request(struct mddev *mddev, struct bio * bi) struct stripe_head *sh; const int rw = bio_data_dir(bi); int remaining; + DEFINE_WAIT(w); + bool do_prepare; if (unlikely(bi->bi_rw & REQ_FLUSH)) { md_flush_request(mddev, bi); @@ -4575,15 +4572,18 @@ static void make_request(struct mddev *mddev, struct bio * bi) bi->bi_next = NULL; bi->bi_phys_segments = 1; /* over-loaded to count active stripes */ + prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) { - DEFINE_WAIT(w); int previous; int seq; + do_prepare = false; retry: seq = read_seqcount_begin(&conf->gen_lock); previous = 0; - prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); + if (do_prepare) + prepare_to_wait(&conf->wait_for_overlap, &w, + TASK_UNINTERRUPTIBLE); if (unlikely(conf->reshape_progress != MaxSector)) { /* spinlock is needed as reshape_progress may be * 64bit on a 32bit platform, and so it might be @@ -4604,6 +4604,7 @@ static void make_request(struct mddev *mddev, struct bio * bi) : logical_sector >= conf->reshape_safe) { spin_unlock_irq(&conf->device_lock); schedule(); + do_prepare = true; goto retry; } } @@ -4640,6 +4641,7 @@ static void make_request(struct mddev *mddev, struct bio * bi) if (must_retry) { release_stripe(sh); schedule(); + do_prepare = true; goto retry; } } @@ -4663,8 +4665,10 @@ static void make_request(struct mddev *mddev, struct bio * bi) prepare_to_wait(&conf->wait_for_overlap, &w, TASK_INTERRUPTIBLE); if (logical_sector >= mddev->suspend_lo && - logical_sector < mddev->suspend_hi) + logical_sector < mddev->suspend_hi) { schedule(); + do_prepare = true; + } goto retry; } @@ -4677,9 +4681,9 @@ static void make_request(struct mddev *mddev, struct bio * bi) md_wakeup_thread(mddev->thread); release_stripe(sh); schedule(); + do_prepare = true; goto retry; } - finish_wait(&conf->wait_for_overlap, &w); set_bit(STRIPE_HANDLE, &sh->state); clear_bit(STRIPE_DELAYED, &sh->state); if ((bi->bi_rw & REQ_SYNC) && @@ -4689,10 +4693,10 @@ static void make_request(struct mddev *mddev, struct bio * bi) } else { /* cannot get stripe for read-ahead, just give-up */ clear_bit(BIO_UPTODATE, &bi->bi_flags); - finish_wait(&conf->wait_for_overlap, &w); break; } } + finish_wait(&conf->wait_for_overlap, &w); remaining = raid5_dec_bi_active_stripes(bi); if (remaining == 0) { diff --git a/drivers/media/dvb-frontends/drx39xyj/Kconfig b/drivers/media/dvb-frontends/drx39xyj/Kconfig index 15628eb5cf0c..6c2ccb6a506b 100644 --- a/drivers/media/dvb-frontends/drx39xyj/Kconfig +++ b/drivers/media/dvb-frontends/drx39xyj/Kconfig @@ -1,7 +1,7 @@ config DVB_DRX39XYJ tristate "Micronas DRX-J demodulator" depends on DVB_CORE && I2C - default m if DVB_FE_CUSTOMISE + default m if !MEDIA_SUBDRV_AUTOSELECT help An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. diff --git a/drivers/media/dvb-frontends/lgdt3305.c b/drivers/media/dvb-frontends/lgdt3305.c index 1d2c47378cf8..92c891a571ab 100644 --- a/drivers/media/dvb-frontends/lgdt3305.c +++ b/drivers/media/dvb-frontends/lgdt3305.c @@ -1176,6 +1176,7 @@ static struct dvb_frontend_ops lgdt3304_ops = { }, .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl, .init = lgdt3305_init, + .sleep = lgdt3305_sleep, .set_frontend = lgdt3304_set_parameters, .get_frontend = lgdt3305_get_frontend, .get_tune_settings = lgdt3305_get_tune_settings, diff --git a/drivers/media/dvb-frontends/m88rs2000.c b/drivers/media/dvb-frontends/m88rs2000.c index 32cffca14d0b..d63bc9c13dce 100644 --- a/drivers/media/dvb-frontends/m88rs2000.c +++ b/drivers/media/dvb-frontends/m88rs2000.c @@ -297,7 +297,7 @@ struct inittab { u8 val; }; -struct inittab m88rs2000_setup[] = { +static struct inittab m88rs2000_setup[] = { {DEMOD_WRITE, 0x9a, 0x30}, {DEMOD_WRITE, 0x00, 0x01}, {WRITE_DELAY, 0x19, 0x00}, @@ -315,7 +315,7 @@ struct inittab m88rs2000_setup[] = { {0xff, 0xaa, 0xff} }; -struct inittab m88rs2000_shutdown[] = { +static struct inittab m88rs2000_shutdown[] = { {DEMOD_WRITE, 0x9a, 0x30}, {DEMOD_WRITE, 0xb0, 0x00}, {DEMOD_WRITE, 0xf1, 0x89}, @@ -325,7 +325,7 @@ struct inittab m88rs2000_shutdown[] = { {0xff, 0xaa, 0xff} }; -struct inittab fe_reset[] = { +static struct inittab fe_reset[] = { {DEMOD_WRITE, 0x00, 0x01}, {DEMOD_WRITE, 0x20, 0x81}, {DEMOD_WRITE, 0x21, 0x80}, @@ -363,7 +363,7 @@ struct inittab fe_reset[] = { {0xff, 0xaa, 0xff} }; -struct inittab fe_trigger[] = { +static struct inittab fe_trigger[] = { {DEMOD_WRITE, 0x97, 0x04}, {DEMOD_WRITE, 0x99, 0x77}, {DEMOD_WRITE, 0x9b, 0x64}, diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index 7a77a5b7a075..5c421886d97c 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -49,8 +49,8 @@ #define VPE_MODULE_NAME "vpe" /* minimum and maximum frame sizes */ -#define MIN_W 128 -#define MIN_H 128 +#define MIN_W 32 +#define MIN_H 32 #define MAX_W 1920 #define MAX_H 1080 @@ -887,6 +887,9 @@ static int job_ready(void *priv) if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < needed) return 0; + if (v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < needed) + return 0; + return 1; } @@ -1277,18 +1280,17 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) s_buf = &s_vb->v4l2_buf; d_buf = &d_vb->v4l2_buf; + d_buf->flags = s_buf->flags; + d_buf->timestamp = s_buf->timestamp; - d_buf->flags &= ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - d_buf->flags |= s_buf->flags & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) { - d_buf->flags |= V4L2_BUF_FLAG_TIMECODE; + if (s_buf->flags & V4L2_BUF_FLAG_TIMECODE) d_buf->timecode = s_buf->timecode; - } + d_buf->sequence = ctx->sequence; - d_buf->field = ctx->field; d_q_data = &ctx->q_data[Q_DATA_DST]; if (d_q_data->flags & Q_DATA_INTERLACED) { + d_buf->field = ctx->field; if (ctx->field == V4L2_FIELD_BOTTOM) { ctx->sequence++; ctx->field = V4L2_FIELD_TOP; @@ -1297,6 +1299,7 @@ static irqreturn_t vpe_irq(int irq_vpe, void *data) ctx->field = V4L2_FIELD_BOTTOM; } } else { + d_buf->field = V4L2_FIELD_NONE; ctx->sequence++; } @@ -1335,8 +1338,9 @@ static int vpe_querycap(struct file *file, void *priv, { strncpy(cap->driver, VPE_MODULE_NAME, sizeof(cap->driver) - 1); strncpy(cap->card, VPE_MODULE_NAME, sizeof(cap->card) - 1); - strlcpy(cap->bus_info, VPE_MODULE_NAME, sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING; + snprintf(cap->bus_info, sizeof(cap->bus_info), "platform:%s", + VPE_MODULE_NAME); + cap->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -1476,6 +1480,7 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, } } + memset(pix->reserved, 0, sizeof(pix->reserved)); for (i = 0; i < pix->num_planes; i++) { plane_fmt = &pix->plane_fmt[i]; depth = fmt->vpdma_fmt[i]->depth; @@ -1487,6 +1492,8 @@ static int __vpe_try_fmt(struct vpe_ctx *ctx, struct v4l2_format *f, plane_fmt->sizeimage = (pix->height * pix->width * depth) >> 3; + + memset(plane_fmt->reserved, 0, sizeof(plane_fmt->reserved)); } return 0; @@ -1717,6 +1724,16 @@ static int vpe_buf_prepare(struct vb2_buffer *vb) q_data = get_q_data(ctx, vb->vb2_queue->type); num_planes = q_data->fmt->coplanar ? 2 : 1; + if (vb->vb2_queue->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + if (!(q_data->flags & Q_DATA_INTERLACED)) { + vb->v4l2_buf.field = V4L2_FIELD_NONE; + } else { + if (vb->v4l2_buf.field != V4L2_FIELD_TOP && + vb->v4l2_buf.field != V4L2_FIELD_BOTTOM) + return -EINVAL; + } + } + for (i = 0; i < num_planes; i++) { if (vb2_plane_size(vb, i) < q_data->sizeimage[i]) { vpe_err(ctx->dev, @@ -1866,9 +1883,11 @@ static int vpe_open(struct file *file) s_q_data->fmt = &vpe_formats[2]; s_q_data->width = 1920; s_q_data->height = 1080; - s_q_data->sizeimage[VPE_LUMA] = (s_q_data->width * s_q_data->height * + s_q_data->bytesperline[VPE_LUMA] = (s_q_data->width * s_q_data->fmt->vpdma_fmt[VPE_LUMA]->depth) >> 3; - s_q_data->colorspace = V4L2_COLORSPACE_SMPTE170M; + s_q_data->sizeimage[VPE_LUMA] = (s_q_data->bytesperline[VPE_LUMA] * + s_q_data->height); + s_q_data->colorspace = V4L2_COLORSPACE_REC709; s_q_data->field = V4L2_FIELD_NONE; s_q_data->c_rect.left = 0; s_q_data->c_rect.top = 0; @@ -2002,7 +2021,7 @@ static struct video_device vpe_videodev = { .fops = &vpe_fops, .ioctl_ops = &vpe_ioctl_ops, .minor = -1, - .release = video_device_release, + .release = video_device_release_empty, .vfl_dir = VFL_DIR_M2M, }; diff --git a/drivers/media/rc/img-ir/img-ir-hw.c b/drivers/media/rc/img-ir/img-ir-hw.c index 579a52b3edce..0127dd257a57 100644 --- a/drivers/media/rc/img-ir/img-ir-hw.c +++ b/drivers/media/rc/img-ir/img-ir-hw.c @@ -504,6 +504,18 @@ unlock: return ret; } +static int img_ir_set_normal_filter(struct rc_dev *dev, + struct rc_scancode_filter *sc_filter) +{ + return img_ir_set_filter(dev, RC_FILTER_NORMAL, sc_filter); +} + +static int img_ir_set_wakeup_filter(struct rc_dev *dev, + struct rc_scancode_filter *sc_filter) +{ + return img_ir_set_filter(dev, RC_FILTER_WAKEUP, sc_filter); +} + /** * img_ir_set_decoder() - Set the current decoder. * @priv: IR private data. @@ -986,7 +998,8 @@ int img_ir_probe_hw(struct img_ir_priv *priv) rdev->map_name = RC_MAP_EMPTY; rc_set_allowed_protocols(rdev, img_ir_allowed_protos(priv)); rdev->input_name = "IMG Infrared Decoder"; - rdev->s_filter = img_ir_set_filter; + rdev->s_filter = img_ir_set_normal_filter; + rdev->s_wakeup_filter = img_ir_set_wakeup_filter; /* Register hardware decoder */ error = rc_register_device(rdev); diff --git a/drivers/media/rc/img-ir/img-ir-nec.c b/drivers/media/rc/img-ir/img-ir-nec.c index e7a731bc3a9b..751d9d945269 100644 --- a/drivers/media/rc/img-ir/img-ir-nec.c +++ b/drivers/media/rc/img-ir/img-ir-nec.c @@ -5,6 +5,7 @@ */ #include "img-ir-hw.h" +#include <linux/bitrev.h> /* Convert NEC data to a scancode */ static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols) @@ -22,11 +23,11 @@ static int img_ir_nec_scancode(int len, u64 raw, int *scancode, u64 protocols) data_inv = (raw >> 24) & 0xff; if ((data_inv ^ data) != 0xff) { /* 32-bit NEC (used by Apple and TiVo remotes) */ - /* scan encoding: aaAAddDD */ - *scancode = addr_inv << 24 | - addr << 16 | - data_inv << 8 | - data; + /* scan encoding: as transmitted, MSBit = first received bit */ + *scancode = bitrev8(addr) << 24 | + bitrev8(addr_inv) << 16 | + bitrev8(data) << 8 | + bitrev8(data_inv); } else if ((addr_inv ^ addr) != 0xff) { /* Extended NEC */ /* scan encoding: AAaaDD */ @@ -54,13 +55,15 @@ static int img_ir_nec_filter(const struct rc_scancode_filter *in, if ((in->data | in->mask) & 0xff000000) { /* 32-bit NEC (used by Apple and TiVo remotes) */ - /* scan encoding: aaAAddDD */ - addr_inv = (in->data >> 24) & 0xff; - addr_inv_m = (in->mask >> 24) & 0xff; - addr = (in->data >> 16) & 0xff; - addr_m = (in->mask >> 16) & 0xff; - data_inv = (in->data >> 8) & 0xff; - data_inv_m = (in->mask >> 8) & 0xff; + /* scan encoding: as transmitted, MSBit = first received bit */ + addr = bitrev8(in->data >> 24); + addr_m = bitrev8(in->mask >> 24); + addr_inv = bitrev8(in->data >> 16); + addr_inv_m = bitrev8(in->mask >> 16); + data = bitrev8(in->data >> 8); + data_m = bitrev8(in->mask >> 8); + data_inv = bitrev8(in->data >> 0); + data_inv_m = bitrev8(in->mask >> 0); } else if ((in->data | in->mask) & 0x00ff0000) { /* Extended NEC */ /* scan encoding AAaaDD */ diff --git a/drivers/media/rc/ir-nec-decoder.c b/drivers/media/rc/ir-nec-decoder.c index 9de1791d2494..35c42e5e270b 100644 --- a/drivers/media/rc/ir-nec-decoder.c +++ b/drivers/media/rc/ir-nec-decoder.c @@ -172,10 +172,7 @@ static int ir_nec_decode(struct rc_dev *dev, struct ir_raw_event ev) if (send_32bits) { /* NEC transport, but modified protocol, used by at * least Apple and TiVo remotes */ - scancode = not_address << 24 | - address << 16 | - not_command << 8 | - command; + scancode = data->bits; IR_dprintk(1, "NEC (modified) scancode 0x%08x\n", scancode); } else if ((address ^ not_address) != 0xff) { /* Extended NEC */ diff --git a/drivers/media/rc/keymaps/rc-tivo.c b/drivers/media/rc/keymaps/rc-tivo.c index 5cc1b456e329..454e06295692 100644 --- a/drivers/media/rc/keymaps/rc-tivo.c +++ b/drivers/media/rc/keymaps/rc-tivo.c @@ -15,62 +15,62 @@ * Initial mapping is for the TiVo remote included in the Nero LiquidTV bundle, * which also ships with a TiVo-branded IR transceiver, supported by the mceusb * driver. Note that the remote uses an NEC-ish protocol, but instead of having - * a command/not_command pair, it has a vendor ID of 0x3085, but some keys, the + * a command/not_command pair, it has a vendor ID of 0xa10c, but some keys, the * NEC extended checksums do pass, so the table presently has the intended * values and the checksum-passed versions for those keys. */ static struct rc_map_table tivo[] = { - { 0x3085f009, KEY_MEDIA }, /* TiVo Button */ - { 0x3085e010, KEY_POWER2 }, /* TV Power */ - { 0x3085e011, KEY_TV }, /* Live TV/Swap */ - { 0x3085c034, KEY_VIDEO_NEXT }, /* TV Input */ - { 0x3085e013, KEY_INFO }, - { 0x3085a05f, KEY_CYCLEWINDOWS }, /* Window */ + { 0xa10c900f, KEY_MEDIA }, /* TiVo Button */ + { 0xa10c0807, KEY_POWER2 }, /* TV Power */ + { 0xa10c8807, KEY_TV }, /* Live TV/Swap */ + { 0xa10c2c03, KEY_VIDEO_NEXT }, /* TV Input */ + { 0xa10cc807, KEY_INFO }, + { 0xa10cfa05, KEY_CYCLEWINDOWS }, /* Window */ { 0x0085305f, KEY_CYCLEWINDOWS }, - { 0x3085c036, KEY_EPG }, /* Guide */ + { 0xa10c6c03, KEY_EPG }, /* Guide */ - { 0x3085e014, KEY_UP }, - { 0x3085e016, KEY_DOWN }, - { 0x3085e017, KEY_LEFT }, - { 0x3085e015, KEY_RIGHT }, + { 0xa10c2807, KEY_UP }, + { 0xa10c6807, KEY_DOWN }, + { 0xa10ce807, KEY_LEFT }, + { 0xa10ca807, KEY_RIGHT }, - { 0x3085e018, KEY_SCROLLDOWN }, /* Red Thumbs Down */ - { 0x3085e019, KEY_SELECT }, - { 0x3085e01a, KEY_SCROLLUP }, /* Green Thumbs Up */ + { 0xa10c1807, KEY_SCROLLDOWN }, /* Red Thumbs Down */ + { 0xa10c9807, KEY_SELECT }, + { 0xa10c5807, KEY_SCROLLUP }, /* Green Thumbs Up */ - { 0x3085e01c, KEY_VOLUMEUP }, - { 0x3085e01d, KEY_VOLUMEDOWN }, - { 0x3085e01b, KEY_MUTE }, - { 0x3085d020, KEY_RECORD }, - { 0x3085e01e, KEY_CHANNELUP }, - { 0x3085e01f, KEY_CHANNELDOWN }, + { 0xa10c3807, KEY_VOLUMEUP }, + { 0xa10cb807, KEY_VOLUMEDOWN }, + { 0xa10cd807, KEY_MUTE }, + { 0xa10c040b, KEY_RECORD }, + { 0xa10c7807, KEY_CHANNELUP }, + { 0xa10cf807, KEY_CHANNELDOWN }, { 0x0085301f, KEY_CHANNELDOWN }, - { 0x3085d021, KEY_PLAY }, - { 0x3085d023, KEY_PAUSE }, - { 0x3085d025, KEY_SLOW }, - { 0x3085d022, KEY_REWIND }, - { 0x3085d024, KEY_FASTFORWARD }, - { 0x3085d026, KEY_PREVIOUS }, - { 0x3085d027, KEY_NEXT }, /* ->| */ + { 0xa10c840b, KEY_PLAY }, + { 0xa10cc40b, KEY_PAUSE }, + { 0xa10ca40b, KEY_SLOW }, + { 0xa10c440b, KEY_REWIND }, + { 0xa10c240b, KEY_FASTFORWARD }, + { 0xa10c640b, KEY_PREVIOUS }, + { 0xa10ce40b, KEY_NEXT }, /* ->| */ - { 0x3085b044, KEY_ZOOM }, /* Aspect */ - { 0x3085b048, KEY_STOP }, - { 0x3085b04a, KEY_DVD }, /* DVD Menu */ + { 0xa10c220d, KEY_ZOOM }, /* Aspect */ + { 0xa10c120d, KEY_STOP }, + { 0xa10c520d, KEY_DVD }, /* DVD Menu */ - { 0x3085d028, KEY_NUMERIC_1 }, - { 0x3085d029, KEY_NUMERIC_2 }, - { 0x3085d02a, KEY_NUMERIC_3 }, - { 0x3085d02b, KEY_NUMERIC_4 }, - { 0x3085d02c, KEY_NUMERIC_5 }, - { 0x3085d02d, KEY_NUMERIC_6 }, - { 0x3085d02e, KEY_NUMERIC_7 }, - { 0x3085d02f, KEY_NUMERIC_8 }, + { 0xa10c140b, KEY_NUMERIC_1 }, + { 0xa10c940b, KEY_NUMERIC_2 }, + { 0xa10c540b, KEY_NUMERIC_3 }, + { 0xa10cd40b, KEY_NUMERIC_4 }, + { 0xa10c340b, KEY_NUMERIC_5 }, + { 0xa10cb40b, KEY_NUMERIC_6 }, + { 0xa10c740b, KEY_NUMERIC_7 }, + { 0xa10cf40b, KEY_NUMERIC_8 }, { 0x0085302f, KEY_NUMERIC_8 }, - { 0x3085c030, KEY_NUMERIC_9 }, - { 0x3085c031, KEY_NUMERIC_0 }, - { 0x3085c033, KEY_ENTER }, - { 0x3085c032, KEY_CLEAR }, + { 0xa10c0c03, KEY_NUMERIC_9 }, + { 0xa10c8c03, KEY_NUMERIC_0 }, + { 0xa10ccc03, KEY_ENTER }, + { 0xa10c4c03, KEY_CLEAR }, }; static struct rc_map_list tivo_map = { diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 99697aae92ff..970b93d6f399 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -633,19 +633,13 @@ EXPORT_SYMBOL_GPL(rc_repeat); static void ir_do_keydown(struct rc_dev *dev, int scancode, u32 keycode, u8 toggle) { - struct rc_scancode_filter *filter; - bool new_event = !dev->keypressed || - dev->last_scancode != scancode || - dev->last_toggle != toggle; + bool new_event = (!dev->keypressed || + dev->last_scancode != scancode || + dev->last_toggle != toggle); if (new_event && dev->keypressed) ir_do_keyup(dev, false); - /* Generic scancode filtering */ - filter = &dev->scancode_filters[RC_FILTER_NORMAL]; - if (filter->mask && ((scancode ^ filter->data) & filter->mask)) - return; - input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode); if (new_event && keycode != KEY_RESERVED) { @@ -923,6 +917,7 @@ static ssize_t store_protocols(struct device *device, int rc, i, count = 0; ssize_t ret; int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); + int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); struct rc_scancode_filter local_filter, *filter; /* Device is being removed */ @@ -1007,24 +1002,23 @@ static ssize_t store_protocols(struct device *device, * Fall back to clearing the filter. */ filter = &dev->scancode_filters[fattr->type]; - if (old_type != type && filter->mask) { + set_filter = (fattr->type == RC_FILTER_NORMAL) + ? dev->s_filter : dev->s_wakeup_filter; + + if (set_filter && old_type != type && filter->mask) { local_filter = *filter; if (!type) { /* no protocol => clear filter */ ret = -1; - } else if (!dev->s_filter) { - /* generic filtering => accept any filter */ - ret = 0; } else { /* hardware filtering => try setting, otherwise clear */ - ret = dev->s_filter(dev, fattr->type, &local_filter); + ret = set_filter(dev, &local_filter); } if (ret < 0) { /* clear the filter */ local_filter.data = 0; local_filter.mask = 0; - if (dev->s_filter) - dev->s_filter(dev, fattr->type, &local_filter); + set_filter(dev, &local_filter); } /* commit the new filter */ @@ -1068,7 +1062,10 @@ static ssize_t show_filter(struct device *device, return -EINVAL; mutex_lock(&dev->lock); - if (fattr->mask) + if ((fattr->type == RC_FILTER_NORMAL && !dev->s_filter) || + (fattr->type == RC_FILTER_WAKEUP && !dev->s_wakeup_filter)) + val = 0; + else if (fattr->mask) val = dev->scancode_filters[fattr->type].mask; else val = dev->scancode_filters[fattr->type].data; @@ -1106,6 +1103,7 @@ static ssize_t store_filter(struct device *device, struct rc_scancode_filter local_filter, *filter; int ret; unsigned long val; + int (*set_filter)(struct rc_dev *dev, struct rc_scancode_filter *filter); /* Device is being removed */ if (!dev) @@ -1115,9 +1113,11 @@ static ssize_t store_filter(struct device *device, if (ret < 0) return ret; - /* Scancode filter not supported (but still accept 0) */ - if (!dev->s_filter && fattr->type != RC_FILTER_NORMAL) - return val ? -EINVAL : count; + /* Can the scancode filter be set? */ + set_filter = (fattr->type == RC_FILTER_NORMAL) ? dev->s_filter : + dev->s_wakeup_filter; + if (!set_filter) + return -EINVAL; mutex_lock(&dev->lock); @@ -1128,16 +1128,16 @@ static ssize_t store_filter(struct device *device, local_filter.mask = val; else local_filter.data = val; + if (!dev->enabled_protocols[fattr->type] && local_filter.mask) { /* refuse to set a filter unless a protocol is enabled */ ret = -EINVAL; goto unlock; } - if (dev->s_filter) { - ret = dev->s_filter(dev, fattr->type, &local_filter); - if (ret < 0) - goto unlock; - } + + ret = set_filter(dev, &local_filter); + if (ret < 0) + goto unlock; /* Success, commit the new filter */ *filter = local_filter; @@ -1189,27 +1189,45 @@ static RC_FILTER_ATTR(wakeup_filter, S_IRUGO|S_IWUSR, static RC_FILTER_ATTR(wakeup_filter_mask, S_IRUGO|S_IWUSR, show_filter, store_filter, RC_FILTER_WAKEUP, true); -static struct attribute *rc_dev_attrs[] = { +static struct attribute *rc_dev_protocol_attrs[] = { &dev_attr_protocols.attr.attr, + NULL, +}; + +static struct attribute_group rc_dev_protocol_attr_grp = { + .attrs = rc_dev_protocol_attrs, +}; + +static struct attribute *rc_dev_wakeup_protocol_attrs[] = { &dev_attr_wakeup_protocols.attr.attr, + NULL, +}; + +static struct attribute_group rc_dev_wakeup_protocol_attr_grp = { + .attrs = rc_dev_wakeup_protocol_attrs, +}; + +static struct attribute *rc_dev_filter_attrs[] = { &dev_attr_filter.attr.attr, &dev_attr_filter_mask.attr.attr, - &dev_attr_wakeup_filter.attr.attr, - &dev_attr_wakeup_filter_mask.attr.attr, NULL, }; -static struct attribute_group rc_dev_attr_grp = { - .attrs = rc_dev_attrs, +static struct attribute_group rc_dev_filter_attr_grp = { + .attrs = rc_dev_filter_attrs, }; -static const struct attribute_group *rc_dev_attr_groups[] = { - &rc_dev_attr_grp, - NULL +static struct attribute *rc_dev_wakeup_filter_attrs[] = { + &dev_attr_wakeup_filter.attr.attr, + &dev_attr_wakeup_filter_mask.attr.attr, + NULL, +}; + +static struct attribute_group rc_dev_wakeup_filter_attr_grp = { + .attrs = rc_dev_wakeup_filter_attrs, }; static struct device_type rc_dev_type = { - .groups = rc_dev_attr_groups, .release = rc_dev_release, .uevent = rc_dev_uevent, }; @@ -1266,7 +1284,7 @@ int rc_register_device(struct rc_dev *dev) static bool raw_init = false; /* raw decoders loaded? */ struct rc_map *rc_map; const char *path; - int rc, devno; + int rc, devno, attr = 0; if (!dev || !dev->map_name) return -EINVAL; @@ -1294,6 +1312,16 @@ int rc_register_device(struct rc_dev *dev) return -ENOMEM; } while (test_and_set_bit(devno, ir_core_dev_number)); + dev->dev.groups = dev->sysfs_groups; + dev->sysfs_groups[attr++] = &rc_dev_protocol_attr_grp; + if (dev->s_filter) + dev->sysfs_groups[attr++] = &rc_dev_filter_attr_grp; + if (dev->s_wakeup_filter) + dev->sysfs_groups[attr++] = &rc_dev_wakeup_filter_attr_grp; + if (dev->change_wakeup_protocol) + dev->sysfs_groups[attr++] = &rc_dev_wakeup_protocol_attr_grp; + dev->sysfs_groups[attr++] = NULL; + /* * Take the lock here, as the device sysfs node will appear * when device_add() is called, which may trigger an ir-keytable udev diff --git a/drivers/media/tuners/r820t.c b/drivers/media/tuners/r820t.c index 319adc4f0561..96ccfebce7ca 100644 --- a/drivers/media/tuners/r820t.c +++ b/drivers/media/tuners/r820t.c @@ -1468,7 +1468,8 @@ static int r820t_imr_prepare(struct r820t_priv *priv) static int r820t_multi_read(struct r820t_priv *priv) { int rc, i; - u8 data[2], min = 0, max = 255, sum = 0; + u16 sum = 0; + u8 data[2], min = 255, max = 0; usleep_range(5000, 6000); diff --git a/drivers/media/tuners/tuner-xc2028.c b/drivers/media/tuners/tuner-xc2028.c index 76a816511f2f..6ef93ee1fdcb 100644 --- a/drivers/media/tuners/tuner-xc2028.c +++ b/drivers/media/tuners/tuner-xc2028.c @@ -1107,6 +1107,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, offset += 200000; } #endif + break; default: tuner_err("Unsupported tuner type %d.\n", new_type); break; diff --git a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c index c83c16cece01..61d196e8b3ab 100644 --- a/drivers/media/usb/dvb-usb-v2/rtl28xxu.c +++ b/drivers/media/usb/dvb-usb-v2/rtl28xxu.c @@ -1503,8 +1503,6 @@ static const struct usb_device_id rtl28xxu_id_table[] = { /* RTL2832P devices: */ { DVB_USB_DEVICE(USB_VID_HANFTEK, 0x0131, &rtl2832u_props, "Astrometa DVB-T2", NULL) }, - { DVB_USB_DEVICE(USB_VID_KYE, 0x707f, - &rtl2832u_props, "Genius TVGo DVB-T03", NULL) }, { } }; MODULE_DEVICE_TABLE(usb, rtl28xxu_id_table); diff --git a/drivers/media/usb/gspca/jpeg.h b/drivers/media/usb/gspca/jpeg.h index ab54910418b4..0aa2b671faa4 100644 --- a/drivers/media/usb/gspca/jpeg.h +++ b/drivers/media/usb/gspca/jpeg.h @@ -154,7 +154,9 @@ static void jpeg_set_qual(u8 *jpeg_hdr, { int i, sc; - if (quality < 50) + if (quality <= 0) + sc = 5000; + else if (quality < 50) sc = 5000 / quality; else sc = 200 - quality * 2; diff --git a/drivers/media/usb/stk1160/stk1160-ac97.c b/drivers/media/usb/stk1160/stk1160-ac97.c index c46c8be89602..2dd308f9541f 100644 --- a/drivers/media/usb/stk1160/stk1160-ac97.c +++ b/drivers/media/usb/stk1160/stk1160-ac97.c @@ -108,7 +108,7 @@ int stk1160_ac97_register(struct stk1160 *dev) "stk1160-mixer"); snprintf(card->longname, sizeof(card->longname), "stk1160 ac97 codec mixer control"); - strncpy(card->driver, dev->dev->driver->name, sizeof(card->driver)); + strlcpy(card->driver, dev->dev->driver->name, sizeof(card->driver)); rc = snd_ac97_bus(card, 0, &stk1160_ac97_ops, NULL, &ac97_bus); if (rc) diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index f3cdf64997d6..63aa9d9e34c5 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -78,11 +78,19 @@ static void ntb_netdev_event_handler(void *data, int status) netdev_dbg(ndev, "Event %x, Link %x\n", status, ntb_transport_link_query(dev->qp)); - /* Currently, only link status event is supported */ - if (status) - netif_carrier_on(ndev); - else + switch (status) { + case NTB_LINK_DOWN: netif_carrier_off(ndev); + break; + case NTB_LINK_UP: + if (!ntb_transport_link_query(dev->qp)) + return; + + netif_carrier_on(ndev); + break; + default: + netdev_warn(ndev, "Unsupported event type %d\n", status); + } } static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, @@ -182,8 +190,10 @@ static int ntb_netdev_open(struct net_device *ndev) rc = ntb_transport_rx_enqueue(dev->qp, skb, skb->data, ndev->mtu + ETH_HLEN); - if (rc == -EINVAL) + if (rc == -EINVAL) { + dev_kfree_skb(skb); goto err; + } } netif_carrier_off(ndev); @@ -367,12 +377,15 @@ static void ntb_netdev_remove(struct pci_dev *pdev) { struct net_device *ndev; struct ntb_netdev *dev; + bool found = false; list_for_each_entry(dev, &dev_list, list) { - if (dev->pdev == pdev) + if (dev->pdev == pdev) { + found = true; break; + } } - if (dev == NULL) + if (!found) return; list_del(&dev->list); diff --git a/drivers/ntb/ntb_hw.c b/drivers/ntb/ntb_hw.c index 170e8e60cdb7..372e08c4ffef 100644 --- a/drivers/ntb/ntb_hw.c +++ b/drivers/ntb/ntb_hw.c @@ -91,7 +91,7 @@ static struct dentry *debugfs_dir; /* Translate memory window 0,1 to BAR 2,4 */ #define MW_TO_BAR(mw) (mw * NTB_MAX_NUM_MW + 2) -static DEFINE_PCI_DEVICE_TABLE(ntb_pci_tbl) = { +static const struct pci_device_id ntb_pci_tbl[] = { {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_BWD)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_JSF)}, {PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_NTB_B2B_SNB)}, @@ -120,7 +120,8 @@ MODULE_DEVICE_TABLE(pci, ntb_pci_tbl); * RETURNS: An appropriate -ERRNO error value on error, or zero for success. */ int ntb_register_event_callback(struct ntb_device *ndev, - void (*func)(void *handle, enum ntb_hw_event event)) + void (*func)(void *handle, + enum ntb_hw_event event)) { if (ndev->event_cb) return -EINVAL; @@ -715,9 +716,9 @@ static int ntb_xeon_setup(struct ntb_device *ndev) SNB_PBAR4LMT_OFFSET); /* HW errata on the Limit registers. They can only be * written when the base register is 4GB aligned and - * < 32bit. This should already be the case based on the - * driver defaults, but write the Limit registers first - * just in case. + * < 32bit. This should already be the case based on + * the driver defaults, but write the Limit registers + * first just in case. */ } else { ndev->limits.max_mw = SNB_MAX_MW; @@ -739,9 +740,9 @@ static int ntb_xeon_setup(struct ntb_device *ndev) writeq(0, ndev->reg_base + SNB_PBAR4LMT_OFFSET); /* HW errata on the Limit registers. They can only be * written when the base register is 4GB aligned and - * < 32bit. This should already be the case based on the - * driver defaults, but write the Limit registers first - * just in case. + * < 32bit. This should already be the case based on + * the driver defaults, but write the Limit registers + * first just in case. */ } @@ -785,7 +786,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev) /* B2B_XLAT_OFFSET is a 64bit register, but can * only take 32bit writes */ - writel(SNB_MBAR01_DSD_ADDR & 0xffffffff, + writel(SNB_MBAR01_USD_ADDR & 0xffffffff, ndev->reg_base + SNB_B2B_XLAT_OFFSETL); writel(SNB_MBAR01_USD_ADDR >> 32, ndev->reg_base + SNB_B2B_XLAT_OFFSETU); @@ -803,7 +804,7 @@ static int ntb_xeon_setup(struct ntb_device *ndev) ndev->conn_type = NTB_CONN_RP; if (xeon_errata_workaround) { - dev_err(&ndev->pdev->dev, + dev_err(&ndev->pdev->dev, "NTB-RP disabled due to hardware errata. To disregard this warning and potentially lock-up the system, add the parameter 'xeon_errata_workaround=0'.\n"); return -EINVAL; } @@ -1079,111 +1080,131 @@ static irqreturn_t ntb_interrupt(int irq, void *dev) return IRQ_HANDLED; } -static int ntb_setup_msix(struct ntb_device *ndev) +static int ntb_setup_snb_msix(struct ntb_device *ndev, int msix_entries) { struct pci_dev *pdev = ndev->pdev; struct msix_entry *msix; - int msix_entries; int rc, i; - u16 val; - if (!pdev->msix_cap) { - rc = -EIO; - goto err; - } + if (msix_entries < ndev->limits.msix_cnt) + return -ENOSPC; - rc = pci_read_config_word(pdev, pdev->msix_cap + PCI_MSIX_FLAGS, &val); - if (rc) - goto err; + rc = pci_enable_msix_exact(pdev, ndev->msix_entries, msix_entries); + if (rc < 0) + return rc; - msix_entries = msix_table_size(val); - if (msix_entries > ndev->limits.msix_cnt) { - rc = -EINVAL; - goto err; + for (i = 0; i < msix_entries; i++) { + msix = &ndev->msix_entries[i]; + WARN_ON(!msix->vector); + + if (i == msix_entries - 1) { + rc = request_irq(msix->vector, + xeon_event_msix_irq, 0, + "ntb-event-msix", ndev); + if (rc) + goto err; + } else { + rc = request_irq(msix->vector, + xeon_callback_msix_irq, 0, + "ntb-callback-msix", + &ndev->db_cb[i]); + if (rc) + goto err; + } } - ndev->msix_entries = kmalloc(sizeof(struct msix_entry) * msix_entries, - GFP_KERNEL); - if (!ndev->msix_entries) { - rc = -ENOMEM; - goto err; + ndev->num_msix = msix_entries; + ndev->max_cbs = msix_entries - 1; + + return 0; + +err: + while (--i >= 0) { + /* Code never reaches here for entry nr 'ndev->num_msix - 1' */ + msix = &ndev->msix_entries[i]; + free_irq(msix->vector, &ndev->db_cb[i]); } - for (i = 0; i < msix_entries; i++) - ndev->msix_entries[i].entry = i; + pci_disable_msix(pdev); + ndev->num_msix = 0; - rc = pci_enable_msix(pdev, ndev->msix_entries, msix_entries); - if (rc < 0) - goto err1; - if (rc > 0) { - /* On SNB, the link interrupt is always tied to 4th vector. If - * we can't get all 4, then we can't use MSI-X. - */ - if (ndev->hw_type != BWD_HW) { - rc = -EIO; - goto err1; - } + return rc; +} - dev_warn(&pdev->dev, - "Only %d MSI-X vectors. Limiting the number of queues to that number.\n", - rc); - msix_entries = rc; +static int ntb_setup_bwd_msix(struct ntb_device *ndev, int msix_entries) +{ + struct pci_dev *pdev = ndev->pdev; + struct msix_entry *msix; + int rc, i; - rc = pci_enable_msix(pdev, ndev->msix_entries, msix_entries); - if (rc) - goto err1; - } + msix_entries = pci_enable_msix_range(pdev, ndev->msix_entries, + 1, msix_entries); + if (msix_entries < 0) + return msix_entries; for (i = 0; i < msix_entries; i++) { msix = &ndev->msix_entries[i]; WARN_ON(!msix->vector); - /* Use the last MSI-X vector for Link status */ - if (ndev->hw_type == BWD_HW) { - rc = request_irq(msix->vector, bwd_callback_msix_irq, 0, - "ntb-callback-msix", &ndev->db_cb[i]); - if (rc) - goto err2; - } else { - if (i == msix_entries - 1) { - rc = request_irq(msix->vector, - xeon_event_msix_irq, 0, - "ntb-event-msix", ndev); - if (rc) - goto err2; - } else { - rc = request_irq(msix->vector, - xeon_callback_msix_irq, 0, - "ntb-callback-msix", - &ndev->db_cb[i]); - if (rc) - goto err2; - } - } + rc = request_irq(msix->vector, bwd_callback_msix_irq, 0, + "ntb-callback-msix", &ndev->db_cb[i]); + if (rc) + goto err; } ndev->num_msix = msix_entries; + ndev->max_cbs = msix_entries; + + return 0; + +err: + while (--i >= 0) + free_irq(msix->vector, &ndev->db_cb[i]); + + pci_disable_msix(pdev); + ndev->num_msix = 0; + + return rc; +} + +static int ntb_setup_msix(struct ntb_device *ndev) +{ + struct pci_dev *pdev = ndev->pdev; + int msix_entries; + int rc, i; + + msix_entries = pci_msix_vec_count(pdev); + if (msix_entries < 0) { + rc = msix_entries; + goto err; + } else if (msix_entries > ndev->limits.msix_cnt) { + rc = -EINVAL; + goto err; + } + + ndev->msix_entries = kmalloc(sizeof(struct msix_entry) * msix_entries, + GFP_KERNEL); + if (!ndev->msix_entries) { + rc = -ENOMEM; + goto err; + } + + for (i = 0; i < msix_entries; i++) + ndev->msix_entries[i].entry = i; + if (ndev->hw_type == BWD_HW) - ndev->max_cbs = msix_entries; + rc = ntb_setup_bwd_msix(ndev, msix_entries); else - ndev->max_cbs = msix_entries - 1; + rc = ntb_setup_snb_msix(ndev, msix_entries); + if (rc) + goto err1; return 0; -err2: - while (--i >= 0) { - msix = &ndev->msix_entries[i]; - if (ndev->hw_type != BWD_HW && i == ndev->num_msix - 1) - free_irq(msix->vector, ndev); - else - free_irq(msix->vector, &ndev->db_cb[i]); - } - pci_disable_msix(pdev); err1: kfree(ndev->msix_entries); - dev_err(&pdev->dev, "Error allocating MSI-X interrupt\n"); err: - ndev->num_msix = 0; + dev_err(&pdev->dev, "Error allocating MSI-X interrupt\n"); return rc; } @@ -1281,6 +1302,7 @@ static void ntb_free_interrupts(struct ntb_device *ndev) free_irq(msix->vector, &ndev->db_cb[i]); } pci_disable_msix(pdev); + kfree(ndev->msix_entries); } else { free_irq(pdev->irq, ndev); diff --git a/drivers/ntb/ntb_hw.h b/drivers/ntb/ntb_hw.h index bbdb7edca10c..465517b7393e 100644 --- a/drivers/ntb/ntb_hw.h +++ b/drivers/ntb/ntb_hw.h @@ -45,6 +45,7 @@ * Contact Information: * Jon Mason <jon.mason@intel.com> */ +#include <linux/ntb.h> #define PCI_DEVICE_ID_INTEL_NTB_B2B_JSF 0x3725 #define PCI_DEVICE_ID_INTEL_NTB_PS_JSF 0x3726 @@ -60,8 +61,6 @@ #define PCI_DEVICE_ID_INTEL_NTB_SS_HSX 0x2F0F #define PCI_DEVICE_ID_INTEL_NTB_B2B_BWD 0x0C4E -#define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) - #ifndef readq static inline u64 readq(void __iomem *addr) { @@ -83,9 +82,6 @@ static inline void writeq(u64 val, void __iomem *addr) #define NTB_BAR_MASK ((1 << NTB_BAR_MMIO) | (1 << NTB_BAR_23) |\ (1 << NTB_BAR_45)) -#define NTB_LINK_DOWN 0 -#define NTB_LINK_UP 1 - #define NTB_HB_TIMEOUT msecs_to_jiffies(1000) #define NTB_MAX_NUM_MW 2 @@ -233,7 +229,7 @@ int ntb_register_db_callback(struct ntb_device *ndev, unsigned int idx, int db_num)); void ntb_unregister_db_callback(struct ntb_device *ndev, unsigned int idx); int ntb_register_event_callback(struct ntb_device *ndev, - void (*event_cb_func) (void *handle, + void (*event_cb_func)(void *handle, enum ntb_hw_event event)); void ntb_unregister_event_callback(struct ntb_device *ndev); int ntb_get_max_spads(struct ntb_device *ndev); diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index 3217f394d45b..9dd63b822025 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -56,7 +56,6 @@ #include <linux/pci.h> #include <linux/slab.h> #include <linux/types.h> -#include <linux/ntb.h> #include "ntb_hw.h" #define NTB_TRANSPORT_VERSION 3 @@ -107,8 +106,8 @@ struct ntb_transport_qp { struct ntb_rx_info __iomem *rx_info; struct ntb_rx_info *remote_rx_info; - void (*tx_handler) (struct ntb_transport_qp *qp, void *qp_data, - void *data, int len); + void (*tx_handler)(struct ntb_transport_qp *qp, void *qp_data, + void *data, int len); struct list_head tx_free_q; spinlock_t ntb_tx_free_q_lock; void __iomem *tx_mw; @@ -117,8 +116,8 @@ struct ntb_transport_qp { unsigned int tx_max_entry; unsigned int tx_max_frame; - void (*rx_handler) (struct ntb_transport_qp *qp, void *qp_data, - void *data, int len); + void (*rx_handler)(struct ntb_transport_qp *qp, void *qp_data, + void *data, int len); struct list_head rx_pend_q; struct list_head rx_free_q; spinlock_t ntb_rx_pend_q_lock; @@ -129,7 +128,7 @@ struct ntb_transport_qp { unsigned int rx_max_frame; dma_cookie_t last_cookie; - void (*event_handler) (void *data, int status); + void (*event_handler)(void *data, int status); struct delayed_work link_work; struct work_struct link_cleanup; @@ -480,7 +479,7 @@ static void ntb_list_add(spinlock_t *lock, struct list_head *entry, } static struct ntb_queue_entry *ntb_list_rm(spinlock_t *lock, - struct list_head *list) + struct list_head *list) { struct ntb_queue_entry *entry; unsigned long flags; @@ -839,7 +838,7 @@ static void ntb_qp_link_work(struct work_struct *work) } static int ntb_transport_init_queue(struct ntb_transport *nt, - unsigned int qp_num) + unsigned int qp_num) { struct ntb_transport_qp *qp; unsigned int num_qps_mw, tx_size; @@ -1055,7 +1054,7 @@ static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset, if (!chan) goto err; - if (len < copy_bytes) + if (len < copy_bytes) goto err_wait; device = chan->device; @@ -1190,8 +1189,7 @@ out: return 0; err: - ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry, - &qp->rx_pend_q); + ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry, &qp->rx_pend_q); /* Ensure that the data is fully copied out before clearing the flag */ wmb(); hdr->flags = 0; diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 5f67843c7fb7..27df2c533b09 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -53,6 +53,18 @@ config ACERHDF If you have an Acer Aspire One netbook, say Y or M here. +config ALIENWARE_WMI + tristate "Alienware Special feature control" + depends on ACPI + depends on LEDS_CLASS + depends on NEW_LEDS + depends on ACPI_WMI + ---help--- + This is a driver for controlling Alienware BIOS driven + features. It exposes an interface for controlling the AlienFX + zones on Alienware machines that don't contain a dedicated AlienFX + USB MCU such as the X51 and X51-R2. + config ASUS_LAPTOP tristate "Asus Laptop Extras" depends on ACPI @@ -196,7 +208,7 @@ config HP_ACCEL be called hp_accel. config HP_WIRELESS - tristate "HP WIRELESS" + tristate "HP wireless button" depends on ACPI depends on INPUT help @@ -817,12 +829,4 @@ config PVPANIC a paravirtualized device provided by QEMU; it lets a virtual machine (guest) communicate panic events to the host. -config INTEL_BAYTRAIL_MBI - tristate - depends on PCI - ---help--- - Needed on Baytrail platforms for access to the IOSF Sideband Mailbox - Interface. This is a requirement for systems that need to configure - the PUNIT for power management features such as RAPL. - endif # X86_PLATFORM_DEVICES diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 9b87cfc42b84..1a2eafc9d48e 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -55,4 +55,4 @@ obj-$(CONFIG_INTEL_RST) += intel-rst.o obj-$(CONFIG_INTEL_SMARTCONNECT) += intel-smartconnect.o obj-$(CONFIG_PVPANIC) += pvpanic.o -obj-$(CONFIG_INTEL_BAYTRAIL_MBI) += intel_baytrail.o +obj-$(CONFIG_ALIENWARE_WMI) += alienware-wmi.o diff --git a/drivers/platform/x86/alienware-wmi.c b/drivers/platform/x86/alienware-wmi.c new file mode 100644 index 000000000000..541f9514f76f --- /dev/null +++ b/drivers/platform/x86/alienware-wmi.c @@ -0,0 +1,565 @@ +/* + * Alienware AlienFX control + * + * Copyright (C) 2014 Dell Inc <mario_limonciello@dell.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. + * + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/acpi.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/dmi.h> +#include <linux/acpi.h> +#include <linux/leds.h> + +#define LEGACY_CONTROL_GUID "A90597CE-A997-11DA-B012-B622A1EF5492" +#define LEGACY_POWER_CONTROL_GUID "A80593CE-A997-11DA-B012-B622A1EF5492" +#define WMAX_CONTROL_GUID "A70591CE-A997-11DA-B012-B622A1EF5492" + +#define WMAX_METHOD_HDMI_SOURCE 0x1 +#define WMAX_METHOD_HDMI_STATUS 0x2 +#define WMAX_METHOD_BRIGHTNESS 0x3 +#define WMAX_METHOD_ZONE_CONTROL 0x4 + +MODULE_AUTHOR("Mario Limonciello <mario_limonciello@dell.com>"); +MODULE_DESCRIPTION("Alienware special feature control"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("wmi:" LEGACY_CONTROL_GUID); +MODULE_ALIAS("wmi:" WMAX_CONTROL_GUID); + +enum INTERFACE_FLAGS { + LEGACY, + WMAX, +}; + +enum LEGACY_CONTROL_STATES { + LEGACY_RUNNING = 1, + LEGACY_BOOTING = 0, + LEGACY_SUSPEND = 3, +}; + +enum WMAX_CONTROL_STATES { + WMAX_RUNNING = 0xFF, + WMAX_BOOTING = 0, + WMAX_SUSPEND = 3, +}; + +struct quirk_entry { + u8 num_zones; +}; + +static struct quirk_entry *quirks; + +static struct quirk_entry quirk_unknown = { + .num_zones = 2, +}; + +static struct quirk_entry quirk_x51_family = { + .num_zones = 3, +}; + +static int dmi_matched(const struct dmi_system_id *dmi) +{ + quirks = dmi->driver_data; + return 1; +} + +static struct dmi_system_id alienware_quirks[] = { + { + .callback = dmi_matched, + .ident = "Alienware X51 R1", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51"), + }, + .driver_data = &quirk_x51_family, + }, + { + .callback = dmi_matched, + .ident = "Alienware X51 R2", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Alienware"), + DMI_MATCH(DMI_PRODUCT_NAME, "Alienware X51 R2"), + }, + .driver_data = &quirk_x51_family, + }, + {} +}; + +struct color_platform { + u8 blue; + u8 green; + u8 red; +} __packed; + +struct platform_zone { + u8 location; + struct device_attribute *attr; + struct color_platform colors; +}; + +struct wmax_brightness_args { + u32 led_mask; + u32 percentage; +}; + +struct hdmi_args { + u8 arg; +}; + +struct legacy_led_args { + struct color_platform colors; + u8 brightness; + u8 state; +} __packed; + +struct wmax_led_args { + u32 led_mask; + struct color_platform colors; + u8 state; +} __packed; + +static struct platform_device *platform_device; +static struct device_attribute *zone_dev_attrs; +static struct attribute **zone_attrs; +static struct platform_zone *zone_data; + +static struct platform_driver platform_driver = { + .driver = { + .name = "alienware-wmi", + .owner = THIS_MODULE, + } +}; + +static struct attribute_group zone_attribute_group = { + .name = "rgb_zones", +}; + +static u8 interface; +static u8 lighting_control_state; +static u8 global_brightness; + +/* + * Helpers used for zone control +*/ +static int parse_rgb(const char *buf, struct platform_zone *zone) +{ + long unsigned int rgb; + int ret; + union color_union { + struct color_platform cp; + int package; + } repackager; + + ret = kstrtoul(buf, 16, &rgb); + if (ret) + return ret; + + /* RGB triplet notation is 24-bit hexadecimal */ + if (rgb > 0xFFFFFF) + return -EINVAL; + + repackager.package = rgb & 0x0f0f0f0f; + pr_debug("alienware-wmi: r: %d g:%d b: %d\n", + repackager.cp.red, repackager.cp.green, repackager.cp.blue); + zone->colors = repackager.cp; + return 0; +} + +static struct platform_zone *match_zone(struct device_attribute *attr) +{ + int i; + for (i = 0; i < quirks->num_zones; i++) { + if ((struct device_attribute *)zone_data[i].attr == attr) { + pr_debug("alienware-wmi: matched zone location: %d\n", + zone_data[i].location); + return &zone_data[i]; + } + } + return NULL; +} + +/* + * Individual RGB zone control +*/ +static int alienware_update_led(struct platform_zone *zone) +{ + int method_id; + acpi_status status; + char *guid; + struct acpi_buffer input; + struct legacy_led_args legacy_args; + struct wmax_led_args wmax_args; + if (interface == WMAX) { + wmax_args.led_mask = 1 << zone->location; + wmax_args.colors = zone->colors; + wmax_args.state = lighting_control_state; + guid = WMAX_CONTROL_GUID; + method_id = WMAX_METHOD_ZONE_CONTROL; + + input.length = (acpi_size) sizeof(wmax_args); + input.pointer = &wmax_args; + } else { + legacy_args.colors = zone->colors; + legacy_args.brightness = global_brightness; + legacy_args.state = 0; + if (lighting_control_state == LEGACY_BOOTING || + lighting_control_state == LEGACY_SUSPEND) { + guid = LEGACY_POWER_CONTROL_GUID; + legacy_args.state = lighting_control_state; + } else + guid = LEGACY_CONTROL_GUID; + method_id = zone->location + 1; + + input.length = (acpi_size) sizeof(legacy_args); + input.pointer = &legacy_args; + } + pr_debug("alienware-wmi: guid %s method %d\n", guid, method_id); + + status = wmi_evaluate_method(guid, 1, method_id, &input, NULL); + if (ACPI_FAILURE(status)) + pr_err("alienware-wmi: zone set failure: %u\n", status); + return ACPI_FAILURE(status); +} + +static ssize_t zone_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct platform_zone *target_zone; + target_zone = match_zone(attr); + if (target_zone == NULL) + return sprintf(buf, "red: -1, green: -1, blue: -1\n"); + return sprintf(buf, "red: %d, green: %d, blue: %d\n", + target_zone->colors.red, + target_zone->colors.green, target_zone->colors.blue); + +} + +static ssize_t zone_set(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct platform_zone *target_zone; + int ret; + target_zone = match_zone(attr); + if (target_zone == NULL) { + pr_err("alienware-wmi: invalid target zone\n"); + return 1; + } + ret = parse_rgb(buf, target_zone); + if (ret) + return ret; + ret = alienware_update_led(target_zone); + return ret ? ret : count; +} + +/* + * LED Brightness (Global) +*/ +static int wmax_brightness(int brightness) +{ + acpi_status status; + struct acpi_buffer input; + struct wmax_brightness_args args = { + .led_mask = 0xFF, + .percentage = brightness, + }; + input.length = (acpi_size) sizeof(args); + input.pointer = &args; + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + WMAX_METHOD_BRIGHTNESS, &input, NULL); + if (ACPI_FAILURE(status)) + pr_err("alienware-wmi: brightness set failure: %u\n", status); + return ACPI_FAILURE(status); +} + +static void global_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int ret; + global_brightness = brightness; + if (interface == WMAX) + ret = wmax_brightness(brightness); + else + ret = alienware_update_led(&zone_data[0]); + if (ret) + pr_err("LED brightness update failed\n"); +} + +static enum led_brightness global_led_get(struct led_classdev *led_cdev) +{ + return global_brightness; +} + +static struct led_classdev global_led = { + .brightness_set = global_led_set, + .brightness_get = global_led_get, + .name = "alienware::global_brightness", +}; + +/* + * Lighting control state device attribute (Global) +*/ +static ssize_t show_control_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + if (lighting_control_state == LEGACY_BOOTING) + return scnprintf(buf, PAGE_SIZE, "[booting] running suspend\n"); + else if (lighting_control_state == LEGACY_SUSPEND) + return scnprintf(buf, PAGE_SIZE, "booting running [suspend]\n"); + return scnprintf(buf, PAGE_SIZE, "booting [running] suspend\n"); +} + +static ssize_t store_control_state(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + long unsigned int val; + if (strcmp(buf, "booting\n") == 0) + val = LEGACY_BOOTING; + else if (strcmp(buf, "suspend\n") == 0) + val = LEGACY_SUSPEND; + else if (interface == LEGACY) + val = LEGACY_RUNNING; + else + val = WMAX_RUNNING; + lighting_control_state = val; + pr_debug("alienware-wmi: updated control state to %d\n", + lighting_control_state); + return count; +} + +static DEVICE_ATTR(lighting_control_state, 0644, show_control_state, + store_control_state); + +static int alienware_zone_init(struct platform_device *dev) +{ + int i; + char buffer[10]; + char *name; + + if (interface == WMAX) { + global_led.max_brightness = 100; + lighting_control_state = WMAX_RUNNING; + } else if (interface == LEGACY) { + global_led.max_brightness = 0x0F; + lighting_control_state = LEGACY_RUNNING; + } + global_brightness = global_led.max_brightness; + + /* + * - zone_dev_attrs num_zones + 1 is for individual zones and then + * null terminated + * - zone_attrs num_zones + 2 is for all attrs in zone_dev_attrs + + * the lighting control + null terminated + * - zone_data num_zones is for the distinct zones + */ + zone_dev_attrs = + kzalloc(sizeof(struct device_attribute) * (quirks->num_zones + 1), + GFP_KERNEL); + if (!zone_dev_attrs) + return -ENOMEM; + + zone_attrs = + kzalloc(sizeof(struct attribute *) * (quirks->num_zones + 2), + GFP_KERNEL); + if (!zone_attrs) + return -ENOMEM; + + zone_data = + kzalloc(sizeof(struct platform_zone) * (quirks->num_zones), + GFP_KERNEL); + if (!zone_data) + return -ENOMEM; + + for (i = 0; i < quirks->num_zones; i++) { + sprintf(buffer, "zone%02X", i); + name = kstrdup(buffer, GFP_KERNEL); + if (name == NULL) + return 1; + sysfs_attr_init(&zone_dev_attrs[i].attr); + zone_dev_attrs[i].attr.name = name; + zone_dev_attrs[i].attr.mode = 0644; + zone_dev_attrs[i].show = zone_show; + zone_dev_attrs[i].store = zone_set; + zone_data[i].location = i; + zone_attrs[i] = &zone_dev_attrs[i].attr; + zone_data[i].attr = &zone_dev_attrs[i]; + } + zone_attrs[quirks->num_zones] = &dev_attr_lighting_control_state.attr; + zone_attribute_group.attrs = zone_attrs; + + led_classdev_register(&dev->dev, &global_led); + + return sysfs_create_group(&dev->dev.kobj, &zone_attribute_group); +} + +static void alienware_zone_exit(struct platform_device *dev) +{ + sysfs_remove_group(&dev->dev.kobj, &zone_attribute_group); + led_classdev_unregister(&global_led); + if (zone_dev_attrs) { + int i; + for (i = 0; i < quirks->num_zones; i++) + kfree(zone_dev_attrs[i].attr.name); + } + kfree(zone_dev_attrs); + kfree(zone_data); + kfree(zone_attrs); +} + +/* + The HDMI mux sysfs node indicates the status of the HDMI input mux. + It can toggle between standard system GPU output and HDMI input. +*/ +static ssize_t show_hdmi(struct device *dev, struct device_attribute *attr, + char *buf) +{ + acpi_status status; + struct acpi_buffer input; + union acpi_object *obj; + u32 tmp = 0; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + struct hdmi_args in_args = { + .arg = 0, + }; + input.length = (acpi_size) sizeof(in_args); + input.pointer = &in_args; + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + WMAX_METHOD_HDMI_STATUS, &input, &output); + + if (ACPI_SUCCESS(status)) { + obj = (union acpi_object *)output.pointer; + if (obj && obj->type == ACPI_TYPE_INTEGER) + tmp = (u32) obj->integer.value; + if (tmp == 1) + return scnprintf(buf, PAGE_SIZE, + "[input] gpu unknown\n"); + else if (tmp == 2) + return scnprintf(buf, PAGE_SIZE, + "input [gpu] unknown\n"); + } + pr_err("alienware-wmi: unknown HDMI status: %d\n", status); + return scnprintf(buf, PAGE_SIZE, "input gpu [unknown]\n"); +} + +static ssize_t toggle_hdmi(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct acpi_buffer input; + acpi_status status; + struct hdmi_args args; + if (strcmp(buf, "gpu\n") == 0) + args.arg = 1; + else if (strcmp(buf, "input\n") == 0) + args.arg = 2; + else + args.arg = 3; + pr_debug("alienware-wmi: setting hdmi to %d : %s", args.arg, buf); + input.length = (acpi_size) sizeof(args); + input.pointer = &args; + status = wmi_evaluate_method(WMAX_CONTROL_GUID, 1, + WMAX_METHOD_HDMI_SOURCE, &input, NULL); + if (ACPI_FAILURE(status)) + pr_err("alienware-wmi: HDMI toggle failed: results: %u\n", + status); + return count; +} + +static DEVICE_ATTR(hdmi, S_IRUGO | S_IWUSR, show_hdmi, toggle_hdmi); + +static void remove_hdmi(struct platform_device *device) +{ + device_remove_file(&device->dev, &dev_attr_hdmi); +} + +static int create_hdmi(void) +{ + int ret = -ENOMEM; + ret = device_create_file(&platform_device->dev, &dev_attr_hdmi); + if (ret) + goto error_create_hdmi; + return 0; + +error_create_hdmi: + remove_hdmi(platform_device); + return ret; +} + +static int __init alienware_wmi_init(void) +{ + int ret; + + if (wmi_has_guid(LEGACY_CONTROL_GUID)) + interface = LEGACY; + else if (wmi_has_guid(WMAX_CONTROL_GUID)) + interface = WMAX; + else { + pr_warn("alienware-wmi: No known WMI GUID found\n"); + return -ENODEV; + } + + dmi_check_system(alienware_quirks); + if (quirks == NULL) + quirks = &quirk_unknown; + + ret = platform_driver_register(&platform_driver); + if (ret) + goto fail_platform_driver; + platform_device = platform_device_alloc("alienware-wmi", -1); + if (!platform_device) { + ret = -ENOMEM; + goto fail_platform_device1; + } + ret = platform_device_add(platform_device); + if (ret) + goto fail_platform_device2; + + if (interface == WMAX) { + ret = create_hdmi(); + if (ret) + goto fail_prep_hdmi; + } + + ret = alienware_zone_init(platform_device); + if (ret) + goto fail_prep_zones; + + return 0; + +fail_prep_zones: + alienware_zone_exit(platform_device); +fail_prep_hdmi: + platform_device_del(platform_device); +fail_platform_device2: + platform_device_put(platform_device); +fail_platform_device1: + platform_driver_unregister(&platform_driver); +fail_platform_driver: + return ret; +} + +module_init(alienware_wmi_init); + +static void __exit alienware_wmi_exit(void) +{ + if (platform_device) { + alienware_zone_exit(platform_device); + remove_hdmi(platform_device); + platform_device_unregister(platform_device); + platform_driver_unregister(&platform_driver); + } +} + +module_exit(alienware_wmi_exit); diff --git a/drivers/platform/x86/fujitsu-tablet.c b/drivers/platform/x86/fujitsu-tablet.c index 570926c10014..c3784baceae3 100644 --- a/drivers/platform/x86/fujitsu-tablet.c +++ b/drivers/platform/x86/fujitsu-tablet.c @@ -71,6 +71,44 @@ static unsigned short keymap_Lifebook_Tseries[KEYMAP_LEN] __initdata = { KEY_LEFTALT }; +static unsigned short keymap_Lifebook_T901[KEYMAP_LEN] __initdata = { + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_SCROLLDOWN, + KEY_SCROLLUP, + KEY_CYCLEWINDOWS, + KEY_LEFTCTRL, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_LEFTMETA +}; + +static unsigned short keymap_Lifebook_T902[KEYMAP_LEN] __initdata = { + KEY_RESERVED, + KEY_VOLUMEDOWN, + KEY_VOLUMEUP, + KEY_CYCLEWINDOWS, + KEY_PROG1, + KEY_PROG2, + KEY_LEFTMETA, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, + KEY_RESERVED, +}; + static unsigned short keymap_Lifebook_U810[KEYMAP_LEN] __initdata = { KEY_RESERVED, KEY_RESERVED, @@ -302,6 +340,33 @@ static int fujitsu_dmi_stylistic(const struct dmi_system_id *dmi) static const struct dmi_system_id dmi_ids[] __initconst = { { .callback = fujitsu_dmi_lifebook, + .ident = "Fujitsu Lifebook T901", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook T901") + }, + .driver_data = keymap_Lifebook_T901 + }, + { + .callback = fujitsu_dmi_lifebook, + .ident = "Fujitsu Lifebook T901", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T901") + }, + .driver_data = keymap_Lifebook_T901 + }, + { + .callback = fujitsu_dmi_lifebook, + .ident = "Fujitsu Lifebook T902", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T902") + }, + .driver_data = keymap_Lifebook_T902 + }, + { + .callback = fujitsu_dmi_lifebook, .ident = "Fujitsu Siemens P/T Series", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), diff --git a/drivers/platform/x86/intel_baytrail.c b/drivers/platform/x86/intel_baytrail.c deleted file mode 100644 index f96626b17260..000000000000 --- a/drivers/platform/x86/intel_baytrail.c +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Baytrail IOSF-SB MailBox Interface Driver - * Copyright (c) 2013, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * - * The IOSF-SB is a fabric bus available on Atom based SOC's that uses a - * mailbox interface (MBI) to communicate with mutiple devices. This - * driver implements BayTrail-specific access to this interface. - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/pci.h> - -#include "intel_baytrail.h" - -static DEFINE_SPINLOCK(iosf_mbi_lock); - -static inline u32 iosf_mbi_form_mcr(u8 op, u8 port, u8 offset) -{ - return (op << 24) | (port << 16) | (offset << 8) | BT_MBI_ENABLE; -} - -static struct pci_dev *mbi_pdev; /* one mbi device */ - -/* Hold lock before calling */ -static int iosf_mbi_pci_read_mdr(u32 mcrx, u32 mcr, u32 *mdr) -{ - int result; - - if (!mbi_pdev) - return -ENODEV; - - if (mcrx) { - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCRX_OFFSET, mcrx); - if (result < 0) - goto iosf_mbi_read_err; - } - - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCR_OFFSET, mcr); - if (result < 0) - goto iosf_mbi_read_err; - - result = pci_read_config_dword(mbi_pdev, - BT_MBI_MDR_OFFSET, mdr); - if (result < 0) - goto iosf_mbi_read_err; - - return 0; - -iosf_mbi_read_err: - dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n", - result); - return result; -} - -/* Hold lock before calling */ -static int iosf_mbi_pci_write_mdr(u32 mcrx, u32 mcr, u32 mdr) -{ - int result; - - if (!mbi_pdev) - return -ENODEV; - - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MDR_OFFSET, mdr); - if (result < 0) - goto iosf_mbi_write_err; - - if (mcrx) { - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCRX_OFFSET, mcrx); - if (result < 0) - goto iosf_mbi_write_err; - } - - result = pci_write_config_dword(mbi_pdev, - BT_MBI_MCR_OFFSET, mcr); - if (result < 0) - goto iosf_mbi_write_err; - - return 0; - -iosf_mbi_write_err: - dev_err(&mbi_pdev->dev, "error: PCI config operation returned %d\n", - result); - return result; -} - -int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr) -{ - u32 mcr, mcrx; - unsigned long flags; - int ret; - - /*Access to the GFX unit is handled by GPU code */ - BUG_ON(port == BT_MBI_UNIT_GFX); - - mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); - mcrx = offset & BT_MBI_MASK_HI; - - spin_lock_irqsave(&iosf_mbi_lock, flags); - ret = iosf_mbi_pci_read_mdr(mcrx, mcr, mdr); - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - - return ret; -} -EXPORT_SYMBOL(bt_mbi_read); - -int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr) -{ - u32 mcr, mcrx; - unsigned long flags; - int ret; - - /*Access to the GFX unit is handled by GPU code */ - BUG_ON(port == BT_MBI_UNIT_GFX); - - mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); - mcrx = offset & BT_MBI_MASK_HI; - - spin_lock_irqsave(&iosf_mbi_lock, flags); - ret = iosf_mbi_pci_write_mdr(mcrx, mcr, mdr); - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - - return ret; -} -EXPORT_SYMBOL(bt_mbi_write); - -int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask) -{ - u32 mcr, mcrx; - u32 value; - unsigned long flags; - int ret; - - /*Access to the GFX unit is handled by GPU code */ - BUG_ON(port == BT_MBI_UNIT_GFX); - - mcr = iosf_mbi_form_mcr(opcode, port, offset & BT_MBI_MASK_LO); - mcrx = offset & BT_MBI_MASK_HI; - - spin_lock_irqsave(&iosf_mbi_lock, flags); - - /* Read current mdr value */ - ret = iosf_mbi_pci_read_mdr(mcrx, mcr & BT_MBI_RD_MASK, &value); - if (ret < 0) { - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - return ret; - } - - /* Apply mask */ - value &= ~mask; - mdr &= mask; - value |= mdr; - - /* Write back */ - ret = iosf_mbi_pci_write_mdr(mcrx, mcr | BT_MBI_WR_MASK, value); - - spin_unlock_irqrestore(&iosf_mbi_lock, flags); - - return ret; -} -EXPORT_SYMBOL(bt_mbi_modify); - -static int iosf_mbi_probe(struct pci_dev *pdev, - const struct pci_device_id *unused) -{ - int ret; - - ret = pci_enable_device(pdev); - if (ret < 0) { - dev_err(&pdev->dev, "error: could not enable device\n"); - return ret; - } - - mbi_pdev = pci_dev_get(pdev); - return 0; -} - -static DEFINE_PCI_DEVICE_TABLE(iosf_mbi_pci_ids) = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0F00) }, - { 0, }, -}; -MODULE_DEVICE_TABLE(pci, iosf_mbi_pci_ids); - -static struct pci_driver iosf_mbi_pci_driver = { - .name = "iosf_mbi_pci", - .probe = iosf_mbi_probe, - .id_table = iosf_mbi_pci_ids, -}; - -static int __init bt_mbi_init(void) -{ - return pci_register_driver(&iosf_mbi_pci_driver); -} - -static void __exit bt_mbi_exit(void) -{ - pci_unregister_driver(&iosf_mbi_pci_driver); - if (mbi_pdev) { - pci_dev_put(mbi_pdev); - mbi_pdev = NULL; - } -} - -module_init(bt_mbi_init); -module_exit(bt_mbi_exit); - -MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); -MODULE_DESCRIPTION("BayTrail Mailbox Interface accessor"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/platform/x86/intel_baytrail.h b/drivers/platform/x86/intel_baytrail.h deleted file mode 100644 index 8bcc311262e9..000000000000 --- a/drivers/platform/x86/intel_baytrail.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * intel_baytrail.h: MailBox access support for Intel BayTrail platforms - */ - -#ifndef INTEL_BAYTRAIL_MBI_SYMS_H -#define INTEL_BAYTRAIL_MBI_SYMS_H - -#define BT_MBI_MCR_OFFSET 0xD0 -#define BT_MBI_MDR_OFFSET 0xD4 -#define BT_MBI_MCRX_OFFSET 0xD8 - -#define BT_MBI_RD_MASK 0xFEFFFFFF -#define BT_MBI_WR_MASK 0X01000000 - -#define BT_MBI_MASK_HI 0xFFFFFF00 -#define BT_MBI_MASK_LO 0x000000FF -#define BT_MBI_ENABLE 0xF0 - -/* BT-SB unit access methods */ -#define BT_MBI_UNIT_AUNIT 0x00 -#define BT_MBI_UNIT_SMC 0x01 -#define BT_MBI_UNIT_CPU 0x02 -#define BT_MBI_UNIT_BUNIT 0x03 -#define BT_MBI_UNIT_PMC 0x04 -#define BT_MBI_UNIT_GFX 0x06 -#define BT_MBI_UNIT_SMI 0x0C -#define BT_MBI_UNIT_USB 0x43 -#define BT_MBI_UNIT_SATA 0xA3 -#define BT_MBI_UNIT_PCIE 0xA6 - -/* Read/write opcodes */ -#define BT_MBI_AUNIT_READ 0x10 -#define BT_MBI_AUNIT_WRITE 0x11 -#define BT_MBI_SMC_READ 0x10 -#define BT_MBI_SMC_WRITE 0x11 -#define BT_MBI_CPU_READ 0x10 -#define BT_MBI_CPU_WRITE 0x11 -#define BT_MBI_BUNIT_READ 0x10 -#define BT_MBI_BUNIT_WRITE 0x11 -#define BT_MBI_PMC_READ 0x06 -#define BT_MBI_PMC_WRITE 0x07 -#define BT_MBI_GFX_READ 0x00 -#define BT_MBI_GFX_WRITE 0x01 -#define BT_MBI_SMIO_READ 0x06 -#define BT_MBI_SMIO_WRITE 0x07 -#define BT_MBI_USB_READ 0x06 -#define BT_MBI_USB_WRITE 0x07 -#define BT_MBI_SATA_READ 0x00 -#define BT_MBI_SATA_WRITE 0x01 -#define BT_MBI_PCIE_READ 0x00 -#define BT_MBI_PCIE_WRITE 0x01 - -/** - * bt_mbi_read() - MailBox Interface read command - * @port: port indicating subunit being accessed - * @opcode: port specific read or write opcode - * @offset: register address offset - * @mdr: register data to be read - * - * Locking is handled by spinlock - cannot sleep. - * Return: Nonzero on error - */ -int bt_mbi_read(u8 port, u8 opcode, u32 offset, u32 *mdr); - -/** - * bt_mbi_write() - MailBox unmasked write command - * @port: port indicating subunit being accessed - * @opcode: port specific read or write opcode - * @offset: register address offset - * @mdr: register data to be written - * - * Locking is handled by spinlock - cannot sleep. - * Return: Nonzero on error - */ -int bt_mbi_write(u8 port, u8 opcode, u32 offset, u32 mdr); - -/** - * bt_mbi_modify() - MailBox masked write command - * @port: port indicating subunit being accessed - * @opcode: port specific read or write opcode - * @offset: register address offset - * @mdr: register data being modified - * @mask: mask indicating bits in mdr to be modified - * - * Locking is handled by spinlock - cannot sleep. - * Return: Nonzero on error - */ -int bt_mbi_modify(u8 port, u8 opcode, u32 offset, u32 mdr, u32 mask); - -#endif /* INTEL_BAYTRAIL_MBI_SYMS_H */ diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 609d38779b26..3f870972247c 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -449,6 +449,7 @@ static struct attribute_group pcc_attr_group = { /* hotkey input device driver */ +static int sleep_keydown_seen; static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) { struct input_dev *hotk_input_dev = pcc->input_dev; @@ -462,6 +463,16 @@ static void acpi_pcc_generate_keyinput(struct pcc_acpi *pcc) "error getting hotkey status\n")); return; } + + /* hack: some firmware sends no key down for sleep / hibernate */ + if ((result & 0xf) == 0x7 || (result & 0xf) == 0xa) { + if (result & 0x80) + sleep_keydown_seen = 1; + if (!sleep_keydown_seen) + sparse_keymap_report_event(hotk_input_dev, + result & 0xf, 0x80, false); + } + if (!sparse_keymap_report_event(hotk_input_dev, result & 0xf, result & 0x80, false)) ACPI_DEBUG_PRINT((ACPI_DB_ERROR, diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 8f8551a63cc0..9c5a07417b2b 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -76,8 +76,6 @@ do { \ pr_warn(fmt, ##__VA_ARGS__); \ } while (0) -#define SONY_LAPTOP_DRIVER_VERSION "0.6" - #define SONY_NC_CLASS "sony-nc" #define SONY_NC_HID "SNY5001" #define SONY_NC_DRIVER_NAME "Sony Notebook Control Driver" @@ -89,7 +87,6 @@ do { \ MODULE_AUTHOR("Stelian Pop, Mattia Dongili"); MODULE_DESCRIPTION("Sony laptop extras driver (SPIC and SNC ACPI device)"); MODULE_LICENSE("GPL"); -MODULE_VERSION(SONY_LAPTOP_DRIVER_VERSION); static int debug; module_param(debug, int, 0); @@ -129,7 +126,8 @@ static int kbd_backlight = -1; module_param(kbd_backlight, int, 0444); MODULE_PARM_DESC(kbd_backlight, "set this to 0 to disable keyboard backlight, " - "1 to enable it (default: no change from current value)"); + "1 to enable it with automatic control and 2 to have it always " + "on (default: no change from current value)"); static int kbd_backlight_timeout = -1; module_param(kbd_backlight_timeout, int, 0444); @@ -152,7 +150,8 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd); static int sony_nc_thermal_setup(struct platform_device *pd); static void sony_nc_thermal_cleanup(struct platform_device *pd); -static int sony_nc_lid_resume_setup(struct platform_device *pd); +static int sony_nc_lid_resume_setup(struct platform_device *pd, + unsigned int handle); static void sony_nc_lid_resume_cleanup(struct platform_device *pd); static int sony_nc_gfx_switch_setup(struct platform_device *pd, @@ -163,6 +162,21 @@ static int __sony_nc_gfx_switch_status_get(void); static int sony_nc_highspeed_charging_setup(struct platform_device *pd); static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd); +static int sony_nc_lowbatt_setup(struct platform_device *pd); +static void sony_nc_lowbatt_cleanup(struct platform_device *pd); + +static int sony_nc_fanspeed_setup(struct platform_device *pd); +static void sony_nc_fanspeed_cleanup(struct platform_device *pd); + +static int sony_nc_usb_charge_setup(struct platform_device *pd); +static void sony_nc_usb_charge_cleanup(struct platform_device *pd); + +static int sony_nc_panelid_setup(struct platform_device *pd); +static void sony_nc_panelid_cleanup(struct platform_device *pd); + +static int sony_nc_smart_conn_setup(struct platform_device *pd); +static void sony_nc_smart_conn_cleanup(struct platform_device *pd); + static int sony_nc_touchpad_setup(struct platform_device *pd, unsigned int handle); static void sony_nc_touchpad_cleanup(struct platform_device *pd); @@ -1122,6 +1136,8 @@ static struct sony_nc_event sony_100_events[] = { { 0x25, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0xa6, SONYPI_EVENT_HELP_PRESSED }, { 0x26, SONYPI_EVENT_ANYBUTTON_RELEASED }, + { 0xa8, SONYPI_EVENT_FNKEY_1 }, + { 0x28, SONYPI_EVENT_ANYBUTTON_RELEASED }, { 0, 0 }, }; @@ -1339,7 +1355,8 @@ static void sony_nc_function_setup(struct acpi_device *device, result); break; case 0x0119: - result = sony_nc_lid_resume_setup(pf_device); + case 0x015D: + result = sony_nc_lid_resume_setup(pf_device, handle); if (result) pr_err("couldn't set up lid resume function (%d)\n", result); @@ -1381,6 +1398,36 @@ static void sony_nc_function_setup(struct acpi_device *device, pr_err("couldn't set up keyboard backlight function (%d)\n", result); break; + case 0x0121: + result = sony_nc_lowbatt_setup(pf_device); + if (result) + pr_err("couldn't set up low battery function (%d)\n", + result); + break; + case 0x0149: + result = sony_nc_fanspeed_setup(pf_device); + if (result) + pr_err("couldn't set up fan speed function (%d)\n", + result); + break; + case 0x0155: + result = sony_nc_usb_charge_setup(pf_device); + if (result) + pr_err("couldn't set up USB charge support (%d)\n", + result); + break; + case 0x011D: + result = sony_nc_panelid_setup(pf_device); + if (result) + pr_err("couldn't set up panel ID function (%d)\n", + result); + break; + case 0x0168: + result = sony_nc_smart_conn_setup(pf_device); + if (result) + pr_err("couldn't set up smart connect support (%d)\n", + result); + break; default: continue; } @@ -1420,6 +1467,7 @@ static void sony_nc_function_cleanup(struct platform_device *pd) sony_nc_battery_care_cleanup(pd); break; case 0x0119: + case 0x015D: sony_nc_lid_resume_cleanup(pd); break; case 0x0122: @@ -1444,6 +1492,21 @@ static void sony_nc_function_cleanup(struct platform_device *pd) case 0x0163: sony_nc_kbd_backlight_cleanup(pd, handle); break; + case 0x0121: + sony_nc_lowbatt_cleanup(pd); + break; + case 0x0149: + sony_nc_fanspeed_cleanup(pd); + break; + case 0x0155: + sony_nc_usb_charge_cleanup(pd); + break; + case 0x011D: + sony_nc_panelid_cleanup(pd); + break; + case 0x0168: + sony_nc_smart_conn_cleanup(pd); + break; default: continue; } @@ -1719,7 +1782,7 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) { int result; - if (value > 1) + if (value > 2) return -EINVAL; if (sony_call_snc_handle(kbdbl_ctl->handle, @@ -1727,8 +1790,10 @@ static ssize_t __sony_nc_kbd_backlight_mode_set(u8 value) return -EIO; /* Try to turn the light on/off immediately */ - sony_call_snc_handle(kbdbl_ctl->handle, - (value << 0x10) | (kbdbl_ctl->base + 0x100), &result); + if (value != 1) + sony_call_snc_handle(kbdbl_ctl->handle, + (value << 0x0f) | (kbdbl_ctl->base + 0x100), + &result); kbdbl_ctl->mode = value; @@ -2221,9 +2286,14 @@ static void sony_nc_thermal_resume(void) #endif /* resume on LID open */ +#define LID_RESUME_S5 0 +#define LID_RESUME_S4 1 +#define LID_RESUME_S3 2 +#define LID_RESUME_MAX 3 struct snc_lid_resume_control { - struct device_attribute attrs[3]; + struct device_attribute attrs[LID_RESUME_MAX]; unsigned int status; + int handle; }; static struct snc_lid_resume_control *lid_ctl; @@ -2231,8 +2301,9 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, struct device_attribute *attr, const char *buffer, size_t count) { - unsigned int result, pos; + unsigned int result; unsigned long value; + unsigned int pos = LID_RESUME_S5; if (count > 31) return -EINVAL; @@ -2245,21 +2316,21 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, * +--------------+ * 2 1 0 */ - if (strcmp(attr->attr.name, "lid_resume_S3") == 0) - pos = 2; - else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) - pos = 1; - else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) - pos = 0; - else - return -EINVAL; + while (pos < LID_RESUME_MAX) { + if (&lid_ctl->attrs[pos].attr == &attr->attr) + break; + pos++; + } + if (pos == LID_RESUME_MAX) + return -EINVAL; if (value) value = lid_ctl->status | (1 << pos); else value = lid_ctl->status & ~(1 << pos); - if (sony_call_snc_handle(0x0119, value << 0x10 | 0x0100, &result)) + if (sony_call_snc_handle(lid_ctl->handle, value << 0x10 | 0x0100, + &result)) return -EIO; lid_ctl->status = value; @@ -2268,29 +2339,27 @@ static ssize_t sony_nc_lid_resume_store(struct device *dev, } static ssize_t sony_nc_lid_resume_show(struct device *dev, - struct device_attribute *attr, char *buffer) + struct device_attribute *attr, + char *buffer) { - unsigned int pos; + unsigned int pos = LID_RESUME_S5; - if (strcmp(attr->attr.name, "lid_resume_S3") == 0) - pos = 2; - else if (strcmp(attr->attr.name, "lid_resume_S4") == 0) - pos = 1; - else if (strcmp(attr->attr.name, "lid_resume_S5") == 0) - pos = 0; - else - return -EINVAL; - - return snprintf(buffer, PAGE_SIZE, "%d\n", - (lid_ctl->status >> pos) & 0x01); + while (pos < LID_RESUME_MAX) { + if (&lid_ctl->attrs[pos].attr == &attr->attr) + return snprintf(buffer, PAGE_SIZE, "%d\n", + (lid_ctl->status >> pos) & 0x01); + pos++; + } + return -EINVAL; } -static int sony_nc_lid_resume_setup(struct platform_device *pd) +static int sony_nc_lid_resume_setup(struct platform_device *pd, + unsigned int handle) { unsigned int result; int i; - if (sony_call_snc_handle(0x0119, 0x0000, &result)) + if (sony_call_snc_handle(handle, 0x0000, &result)) return -EIO; lid_ctl = kzalloc(sizeof(struct snc_lid_resume_control), GFP_KERNEL); @@ -2298,26 +2367,29 @@ static int sony_nc_lid_resume_setup(struct platform_device *pd) return -ENOMEM; lid_ctl->status = result & 0x7; + lid_ctl->handle = handle; sysfs_attr_init(&lid_ctl->attrs[0].attr); - lid_ctl->attrs[0].attr.name = "lid_resume_S3"; - lid_ctl->attrs[0].attr.mode = S_IRUGO | S_IWUSR; - lid_ctl->attrs[0].show = sony_nc_lid_resume_show; - lid_ctl->attrs[0].store = sony_nc_lid_resume_store; - - sysfs_attr_init(&lid_ctl->attrs[1].attr); - lid_ctl->attrs[1].attr.name = "lid_resume_S4"; - lid_ctl->attrs[1].attr.mode = S_IRUGO | S_IWUSR; - lid_ctl->attrs[1].show = sony_nc_lid_resume_show; - lid_ctl->attrs[1].store = sony_nc_lid_resume_store; - - sysfs_attr_init(&lid_ctl->attrs[2].attr); - lid_ctl->attrs[2].attr.name = "lid_resume_S5"; - lid_ctl->attrs[2].attr.mode = S_IRUGO | S_IWUSR; - lid_ctl->attrs[2].show = sony_nc_lid_resume_show; - lid_ctl->attrs[2].store = sony_nc_lid_resume_store; - - for (i = 0; i < 3; i++) { + lid_ctl->attrs[LID_RESUME_S5].attr.name = "lid_resume_S5"; + lid_ctl->attrs[LID_RESUME_S5].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[LID_RESUME_S5].show = sony_nc_lid_resume_show; + lid_ctl->attrs[LID_RESUME_S5].store = sony_nc_lid_resume_store; + + if (handle == 0x0119) { + sysfs_attr_init(&lid_ctl->attrs[1].attr); + lid_ctl->attrs[LID_RESUME_S4].attr.name = "lid_resume_S4"; + lid_ctl->attrs[LID_RESUME_S4].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[LID_RESUME_S4].show = sony_nc_lid_resume_show; + lid_ctl->attrs[LID_RESUME_S4].store = sony_nc_lid_resume_store; + + sysfs_attr_init(&lid_ctl->attrs[2].attr); + lid_ctl->attrs[LID_RESUME_S3].attr.name = "lid_resume_S3"; + lid_ctl->attrs[LID_RESUME_S3].attr.mode = S_IRUGO | S_IWUSR; + lid_ctl->attrs[LID_RESUME_S3].show = sony_nc_lid_resume_show; + lid_ctl->attrs[LID_RESUME_S3].store = sony_nc_lid_resume_store; + } + for (i = 0; i < LID_RESUME_MAX && + lid_ctl->attrs[LID_RESUME_S3].attr.name; i++) { result = device_create_file(&pd->dev, &lid_ctl->attrs[i]); if (result) goto liderror; @@ -2340,8 +2412,12 @@ static void sony_nc_lid_resume_cleanup(struct platform_device *pd) int i; if (lid_ctl) { - for (i = 0; i < 3; i++) + for (i = 0; i < LID_RESUME_MAX; i++) { + if (!lid_ctl->attrs[i].attr.name) + break; + device_remove_file(&pd->dev, &lid_ctl->attrs[i]); + } kfree(lid_ctl); lid_ctl = NULL; @@ -2524,6 +2600,355 @@ static void sony_nc_highspeed_charging_cleanup(struct platform_device *pd) } } +/* low battery function */ +static struct device_attribute *lowbatt_handle; + +static ssize_t sony_nc_lowbatt_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0121, value << 8, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_lowbatt_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0121, 0x0200, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 1); +} + +static int sony_nc_lowbatt_setup(struct platform_device *pd) +{ + unsigned int result; + + lowbatt_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!lowbatt_handle) + return -ENOMEM; + + sysfs_attr_init(&lowbatt_handle->attr); + lowbatt_handle->attr.name = "lowbatt_hibernate"; + lowbatt_handle->attr.mode = S_IRUGO | S_IWUSR; + lowbatt_handle->show = sony_nc_lowbatt_show; + lowbatt_handle->store = sony_nc_lowbatt_store; + + result = device_create_file(&pd->dev, lowbatt_handle); + if (result) { + kfree(lowbatt_handle); + lowbatt_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_lowbatt_cleanup(struct platform_device *pd) +{ + if (lowbatt_handle) { + device_remove_file(&pd->dev, lowbatt_handle); + kfree(lowbatt_handle); + lowbatt_handle = NULL; + } +} + +/* fan speed function */ +static struct device_attribute *fan_handle, *hsf_handle; + +static ssize_t sony_nc_hsfan_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0149, value << 0x10 | 0x0200, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_hsfan_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0149, 0x0100, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static ssize_t sony_nc_fanspeed_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0149, 0x0300, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0xff); +} + +static int sony_nc_fanspeed_setup(struct platform_device *pd) +{ + unsigned int result; + + fan_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!fan_handle) + return -ENOMEM; + + hsf_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!hsf_handle) { + result = -ENOMEM; + goto out_hsf_handle_alloc; + } + + sysfs_attr_init(&fan_handle->attr); + fan_handle->attr.name = "fanspeed"; + fan_handle->attr.mode = S_IRUGO; + fan_handle->show = sony_nc_fanspeed_show; + fan_handle->store = NULL; + + sysfs_attr_init(&hsf_handle->attr); + hsf_handle->attr.name = "fan_forced"; + hsf_handle->attr.mode = S_IRUGO | S_IWUSR; + hsf_handle->show = sony_nc_hsfan_show; + hsf_handle->store = sony_nc_hsfan_store; + + result = device_create_file(&pd->dev, fan_handle); + if (result) + goto out_fan_handle; + + result = device_create_file(&pd->dev, hsf_handle); + if (result) + goto out_hsf_handle; + + return 0; + +out_hsf_handle: + device_remove_file(&pd->dev, fan_handle); + +out_fan_handle: + kfree(hsf_handle); + hsf_handle = NULL; + +out_hsf_handle_alloc: + kfree(fan_handle); + fan_handle = NULL; + return result; +} + +static void sony_nc_fanspeed_cleanup(struct platform_device *pd) +{ + if (fan_handle) { + device_remove_file(&pd->dev, fan_handle); + kfree(fan_handle); + fan_handle = NULL; + } + if (hsf_handle) { + device_remove_file(&pd->dev, hsf_handle); + kfree(hsf_handle); + hsf_handle = NULL; + } +} + +/* USB charge function */ +static struct device_attribute *uc_handle; + +static ssize_t sony_nc_usb_charge_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0155, value << 0x10 | 0x0100, &result)) + return -EIO; + + return count; +} + +static ssize_t sony_nc_usb_charge_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0155, 0x0000, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result & 0x01); +} + +static int sony_nc_usb_charge_setup(struct platform_device *pd) +{ + unsigned int result; + + if (sony_call_snc_handle(0x0155, 0x0000, &result) || !(result & 0x01)) { + /* some models advertise the handle but have no implementation + * for it + */ + pr_info("No USB Charge capability found\n"); + return 0; + } + + uc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!uc_handle) + return -ENOMEM; + + sysfs_attr_init(&uc_handle->attr); + uc_handle->attr.name = "usb_charge"; + uc_handle->attr.mode = S_IRUGO | S_IWUSR; + uc_handle->show = sony_nc_usb_charge_show; + uc_handle->store = sony_nc_usb_charge_store; + + result = device_create_file(&pd->dev, uc_handle); + if (result) { + kfree(uc_handle); + uc_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_usb_charge_cleanup(struct platform_device *pd) +{ + if (uc_handle) { + device_remove_file(&pd->dev, uc_handle); + kfree(uc_handle); + uc_handle = NULL; + } +} + +/* Panel ID function */ +static struct device_attribute *panel_handle; + +static ssize_t sony_nc_panelid_show(struct device *dev, + struct device_attribute *attr, char *buffer) +{ + unsigned int result; + + if (sony_call_snc_handle(0x011D, 0x0000, &result)) + return -EIO; + + return snprintf(buffer, PAGE_SIZE, "%d\n", result); +} + +static int sony_nc_panelid_setup(struct platform_device *pd) +{ + unsigned int result; + + panel_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!panel_handle) + return -ENOMEM; + + sysfs_attr_init(&panel_handle->attr); + panel_handle->attr.name = "panel_id"; + panel_handle->attr.mode = S_IRUGO; + panel_handle->show = sony_nc_panelid_show; + panel_handle->store = NULL; + + result = device_create_file(&pd->dev, panel_handle); + if (result) { + kfree(panel_handle); + panel_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_panelid_cleanup(struct platform_device *pd) +{ + if (panel_handle) { + device_remove_file(&pd->dev, panel_handle); + kfree(panel_handle); + panel_handle = NULL; + } +} + +/* smart connect function */ +static struct device_attribute *sc_handle; + +static ssize_t sony_nc_smart_conn_store(struct device *dev, + struct device_attribute *attr, + const char *buffer, size_t count) +{ + unsigned int result; + unsigned long value; + + if (count > 31) + return -EINVAL; + + if (kstrtoul(buffer, 10, &value) || value > 1) + return -EINVAL; + + if (sony_call_snc_handle(0x0168, value << 0x10, &result)) + return -EIO; + + return count; +} + +static int sony_nc_smart_conn_setup(struct platform_device *pd) +{ + unsigned int result; + + sc_handle = kzalloc(sizeof(struct device_attribute), GFP_KERNEL); + if (!sc_handle) + return -ENOMEM; + + sysfs_attr_init(&sc_handle->attr); + sc_handle->attr.name = "smart_connect"; + sc_handle->attr.mode = S_IWUSR; + sc_handle->show = NULL; + sc_handle->store = sony_nc_smart_conn_store; + + result = device_create_file(&pd->dev, sc_handle); + if (result) { + kfree(sc_handle); + sc_handle = NULL; + return result; + } + + return 0; +} + +static void sony_nc_smart_conn_cleanup(struct platform_device *pd) +{ + if (sc_handle) { + device_remove_file(&pd->dev, sc_handle); + kfree(sc_handle); + sc_handle = NULL; + } +} + /* Touchpad enable/disable */ struct touchpad_control { struct device_attribute attr; @@ -2726,8 +3151,6 @@ static int sony_nc_add(struct acpi_device *device) int result = 0; struct sony_nc_value *item; - pr_info("%s v%s\n", SONY_NC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); - sony_nc_acpi_device = device; strcpy(acpi_device_class(device), "sony/hotkey"); @@ -2821,6 +3244,7 @@ static int sony_nc_add(struct acpi_device *device) } } + pr_info("SNC setup done.\n"); return 0; out_sysfs: @@ -4259,8 +4683,6 @@ static int sony_pic_add(struct acpi_device *device) struct sony_pic_ioport *io, *tmp_io; struct sony_pic_irq *irq, *tmp_irq; - pr_info("%s v%s\n", SONY_PIC_DRIVER_NAME, SONY_LAPTOP_DRIVER_VERSION); - spic_dev.acpi_dev = device; strcpy(acpi_device_class(device), "sony/hotkey"); sony_pic_detect_device_type(&spic_dev); @@ -4360,6 +4782,7 @@ static int sony_pic_add(struct acpi_device *device) if (result) goto err_remove_pf; + pr_info("SPIC setup done.\n"); return 0; err_remove_pf: diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index f21e1095b1dd..15e61c16736e 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -3441,6 +3441,106 @@ err_exit: return (res < 0)? res : 1; } +/* Thinkpad X1 Carbon support 5 modes including Home mode, Web browser + * mode, Web conference mode, Function mode and Lay-flat mode. + * We support Home mode and Function mode currently. + * + * Will consider support rest of modes in future. + * + */ +enum ADAPTIVE_KEY_MODE { + HOME_MODE, + WEB_BROWSER_MODE, + WEB_CONFERENCE_MODE, + FUNCTION_MODE, + LAYFLAT_MODE +}; + +const int adaptive_keyboard_modes[] = { + HOME_MODE, +/* WEB_BROWSER_MODE = 2, + WEB_CONFERENCE_MODE = 3, */ + FUNCTION_MODE +}; + +#define DFR_CHANGE_ROW 0x101 +#define DFR_SHOW_QUICKVIEW_ROW 0x102 + +/* press Fn key a while second, it will switch to Function Mode. Then + * release Fn key, previous mode be restored. + */ +static bool adaptive_keyboard_mode_is_saved; +static int adaptive_keyboard_prev_mode; + +static int adaptive_keyboard_get_next_mode(int mode) +{ + size_t i; + size_t max_mode = ARRAY_SIZE(adaptive_keyboard_modes) - 1; + + for (i = 0; i <= max_mode; i++) { + if (adaptive_keyboard_modes[i] == mode) + break; + } + + if (i >= max_mode) + i = 0; + else + i++; + + return adaptive_keyboard_modes[i]; +} + +static bool adaptive_keyboard_hotkey_notify_hotkey(unsigned int scancode) +{ + u32 current_mode = 0; + int new_mode = 0; + + switch (scancode) { + case DFR_CHANGE_ROW: + if (adaptive_keyboard_mode_is_saved) { + new_mode = adaptive_keyboard_prev_mode; + adaptive_keyboard_mode_is_saved = false; + } else { + if (!acpi_evalf( + hkey_handle, ¤t_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode\n"); + return false; + } else { + new_mode = adaptive_keyboard_get_next_mode( + current_mode); + } + } + + if (!acpi_evalf(hkey_handle, NULL, "STRW", "vd", new_mode)) { + pr_err("Cannot set adaptive keyboard mode\n"); + return false; + } + + return true; + + case DFR_SHOW_QUICKVIEW_ROW: + if (!acpi_evalf(hkey_handle, + &adaptive_keyboard_prev_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode\n"); + return false; + } else { + adaptive_keyboard_mode_is_saved = true; + + if (!acpi_evalf(hkey_handle, + NULL, "STRW", "vd", FUNCTION_MODE)) { + pr_err("Cannot set adaptive keyboard mode\n"); + return false; + } + } + return true; + + default: + return false; + } +} + static bool hotkey_notify_hotkey(const u32 hkey, bool *send_acpi_ev, bool *ignore_acpi_ev) @@ -3460,6 +3560,8 @@ static bool hotkey_notify_hotkey(const u32 hkey, *ignore_acpi_ev = true; } return true; + } else { + return adaptive_keyboard_hotkey_notify_hotkey(scancode); } return false; } @@ -3732,13 +3834,28 @@ static void hotkey_notify(struct ibm_struct *ibm, u32 event) static void hotkey_suspend(void) { + int hkeyv; + /* Do these on suspend, we get the events on early resume! */ hotkey_wakeup_reason = TP_ACPI_WAKEUP_NONE; hotkey_autosleep_ack = 0; + + /* save previous mode of adaptive keyboard of X1 Carbon */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) == 2) { + if (!acpi_evalf(hkey_handle, + &adaptive_keyboard_prev_mode, + "GTRW", "dd", 0)) { + pr_err("Cannot read adaptive keyboard mode.\n"); + } + } + } } static void hotkey_resume(void) { + int hkeyv; + tpacpi_disable_brightness_delay(); if (hotkey_status_set(true) < 0 || @@ -3751,6 +3868,18 @@ static void hotkey_resume(void) hotkey_wakeup_reason_notify_change(); hotkey_wakeup_hotunplug_complete_notify_change(); hotkey_poll_setup_safe(false); + + /* restore previous mode of adapive keyboard of X1 Carbon */ + if (acpi_evalf(hkey_handle, &hkeyv, "MHKV", "qd")) { + if ((hkeyv >> 8) == 2) { + if (!acpi_evalf(hkey_handle, + NULL, + "STRW", "vd", + adaptive_keyboard_prev_mode)) { + pr_err("Cannot set adaptive keyboard mode.\n"); + } + } + } } /* procfs -------------------------------------------------------------- */ @@ -8451,9 +8580,21 @@ static void mute_led_exit(void) tpacpi_led_set(i, false); } +static void mute_led_resume(void) +{ + int i; + + for (i = 0; i < TPACPI_LED_MAX; i++) { + struct tp_led_table *t = &led_tables[i]; + if (t->state >= 0) + mute_led_on_off(t, t->state); + } +} + static struct ibm_struct mute_led_driver_data = { .name = "mute_led", .exit = mute_led_exit, + .resume = mute_led_resume, }; /**************************************************************************** diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c index 90dd7645a9e5..46473ca7566b 100644 --- a/drivers/platform/x86/toshiba_acpi.c +++ b/drivers/platform/x86/toshiba_acpi.c @@ -5,6 +5,7 @@ * Copyright (C) 2002-2004 John Belmonte * Copyright (C) 2008 Philip Langdale * Copyright (C) 2010 Pierre Ducroquet + * Copyright (C) 2014 Azael Avalos * * 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 @@ -37,7 +38,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -#define TOSHIBA_ACPI_VERSION "0.19" +#define TOSHIBA_ACPI_VERSION "0.20" #define PROC_INTERFACE_VERSION 1 #include <linux/kernel.h> @@ -77,6 +78,9 @@ MODULE_LICENSE("GPL"); * However the ACPI methods seem to be incomplete in some areas (for * example they allow setting, but not reading, the LCD brightness value), * so this is still useful. + * + * SCI stands for "System Configuration Interface" which aim is to + * conceal differences in hardware between different models. */ #define HCI_WORDS 6 @@ -84,12 +88,23 @@ MODULE_LICENSE("GPL"); /* operations */ #define HCI_SET 0xff00 #define HCI_GET 0xfe00 +#define SCI_OPEN 0xf100 +#define SCI_CLOSE 0xf200 +#define SCI_GET 0xf300 +#define SCI_SET 0xf400 /* return codes */ #define HCI_SUCCESS 0x0000 #define HCI_FAILURE 0x1000 #define HCI_NOT_SUPPORTED 0x8000 #define HCI_EMPTY 0x8c00 +#define HCI_DATA_NOT_AVAILABLE 0x8d20 +#define HCI_NOT_INITIALIZED 0x8d50 +#define SCI_OPEN_CLOSE_OK 0x0044 +#define SCI_ALREADY_OPEN 0x8100 +#define SCI_NOT_OPENED 0x8200 +#define SCI_INPUT_DATA_ERROR 0x8300 +#define SCI_NOT_PRESENT 0x8600 /* registers */ #define HCI_FAN 0x0004 @@ -99,13 +114,22 @@ MODULE_LICENSE("GPL"); #define HCI_HOTKEY_EVENT 0x001e #define HCI_LCD_BRIGHTNESS 0x002a #define HCI_WIRELESS 0x0056 +#define HCI_ACCELEROMETER 0x006d +#define HCI_KBD_ILLUMINATION 0x0095 +#define HCI_ECO_MODE 0x0097 +#define HCI_ACCELEROMETER2 0x00a6 +#define SCI_ILLUMINATION 0x014e +#define SCI_KBD_ILLUM_STATUS 0x015c +#define SCI_TOUCHPAD 0x050e /* field definitions */ +#define HCI_ACCEL_MASK 0x7fff #define HCI_HOTKEY_DISABLE 0x0b #define HCI_HOTKEY_ENABLE 0x09 #define HCI_LCD_BRIGHTNESS_BITS 3 #define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) #define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) +#define HCI_MISC_SHIFT 0x10 #define HCI_VIDEO_OUT_LCD 0x1 #define HCI_VIDEO_OUT_CRT 0x2 #define HCI_VIDEO_OUT_TV 0x4 @@ -113,6 +137,8 @@ MODULE_LICENSE("GPL"); #define HCI_WIRELESS_BT_PRESENT 0x0f #define HCI_WIRELESS_BT_ATTACH 0x40 #define HCI_WIRELESS_BT_POWER 0x80 +#define SCI_KBD_MODE_FNZ 0x1 +#define SCI_KBD_MODE_AUTO 0x2 struct toshiba_acpi_dev { struct acpi_device *acpi_dev; @@ -122,10 +148,14 @@ struct toshiba_acpi_dev { struct work_struct hotkey_work; struct backlight_device *backlight_dev; struct led_classdev led_dev; + struct led_classdev kbd_led; + struct led_classdev eco_led; int force_fan; int last_key_event; int key_event_valid; + int kbd_mode; + int kbd_time; unsigned int illumination_supported:1; unsigned int video_supported:1; @@ -134,6 +164,12 @@ struct toshiba_acpi_dev { unsigned int ntfy_supported:1; unsigned int info_supported:1; unsigned int tr_backlight_supported:1; + unsigned int kbd_illum_supported:1; + unsigned int kbd_led_registered:1; + unsigned int touchpad_supported:1; + unsigned int eco_supported:1; + unsigned int accelerometer_supported:1; + unsigned int sysfs_created:1; struct mutex mutex; }; @@ -280,21 +316,94 @@ static acpi_status hci_read2(struct toshiba_acpi_dev *dev, u32 reg, return status; } +/* common sci tasks + */ + +static int sci_open(struct toshiba_acpi_dev *dev) +{ + u32 in[HCI_WORDS] = { SCI_OPEN, 0, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + pr_err("ACPI call to open SCI failed\n"); + return 0; + } + + if (out[0] == SCI_OPEN_CLOSE_OK) { + return 1; + } else if (out[0] == SCI_ALREADY_OPEN) { + pr_info("Toshiba SCI already opened\n"); + return 1; + } else if (out[0] == SCI_NOT_PRESENT) { + pr_info("Toshiba SCI is not present\n"); + } + + return 0; +} + +static void sci_close(struct toshiba_acpi_dev *dev) +{ + u32 in[HCI_WORDS] = { SCI_CLOSE, 0, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + pr_err("ACPI call to close SCI failed\n"); + return; + } + + if (out[0] == SCI_OPEN_CLOSE_OK) + return; + else if (out[0] == SCI_NOT_OPENED) + pr_info("Toshiba SCI not opened\n"); + else if (out[0] == SCI_NOT_PRESENT) + pr_info("Toshiba SCI is not present\n"); +} + +static acpi_status sci_read(struct toshiba_acpi_dev *dev, u32 reg, + u32 *out1, u32 *result) +{ + u32 in[HCI_WORDS] = { SCI_GET, reg, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status = hci_raw(dev, in, out); + *out1 = out[2]; + *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; + return status; +} + +static acpi_status sci_write(struct toshiba_acpi_dev *dev, u32 reg, + u32 in1, u32 *result) +{ + u32 in[HCI_WORDS] = { SCI_SET, reg, in1, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status = hci_raw(dev, in, out); + *result = (ACPI_SUCCESS(status)) ? out[0] : HCI_FAILURE; + return status; +} + /* Illumination support */ static int toshiba_illumination_available(struct toshiba_acpi_dev *dev) { - u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; + u32 in[HCI_WORDS] = { SCI_GET, SCI_ILLUMINATION, 0, 0, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status; - in[0] = 0xf100; + if (!sci_open(dev)) + return 0; + status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { + sci_close(dev); + if (ACPI_FAILURE(status) || out[0] == HCI_FAILURE) { + pr_err("ACPI call to query Illumination support failed\n"); + return 0; + } else if (out[0] == HCI_NOT_SUPPORTED || out[1] != 1) { pr_info("Illumination device not available\n"); return 0; } - in[0] = 0xf400; - status = hci_raw(dev, in, out); + return 1; } @@ -303,82 +412,270 @@ static void toshiba_illumination_set(struct led_classdev *cdev, { struct toshiba_acpi_dev *dev = container_of(cdev, struct toshiba_acpi_dev, led_dev); - u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; - u32 out[HCI_WORDS]; + u32 state, result; acpi_status status; /* First request : initialize communication. */ - in[0] = 0xf100; - status = hci_raw(dev, in, out); + if (!sci_open(dev)) + return; + + /* Switch the illumination on/off */ + state = brightness ? 1 : 0; + status = sci_write(dev, SCI_ILLUMINATION, state, &result); + sci_close(dev); if (ACPI_FAILURE(status)) { - pr_info("Illumination device not available\n"); + pr_err("ACPI call for illumination failed\n"); + return; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Illumination not supported\n"); return; } +} - if (brightness) { - /* Switch the illumination on */ - in[0] = 0xf400; - in[1] = 0x14e; - in[2] = 1; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("ACPI call for illumination failed\n"); - return; - } - } else { - /* Switch the illumination off */ - in[0] = 0xf400; - in[1] = 0x14e; - in[2] = 0; - status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("ACPI call for illumination failed.\n"); - return; - } +static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, led_dev); + u32 state, result; + acpi_status status; + + /* First request : initialize communication. */ + if (!sci_open(dev)) + return LED_OFF; + + /* Check the illumination */ + status = sci_read(dev, SCI_ILLUMINATION, &state, &result); + sci_close(dev); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call for illumination failed\n"); + return LED_OFF; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Illumination not supported\n"); + return LED_OFF; } - /* Last request : close communication. */ - in[0] = 0xf200; - in[1] = 0; - in[2] = 0; - hci_raw(dev, in, out); + return state ? LED_FULL : LED_OFF; } -static enum led_brightness toshiba_illumination_get(struct led_classdev *cdev) +/* KBD Illumination */ +static int toshiba_kbd_illum_status_set(struct toshiba_acpi_dev *dev, u32 time) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_write(dev, SCI_KBD_ILLUM_STATUS, time, &result); + sci_close(dev); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to set KBD backlight status failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight status not supported\n"); + return -ENODEV; + } + + return 0; +} + +static int toshiba_kbd_illum_status_get(struct toshiba_acpi_dev *dev, u32 *time) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_read(dev, SCI_KBD_ILLUM_STATUS, time, &result); + sci_close(dev); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to get KBD backlight status failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight status not supported\n"); + return -ENODEV; + } + + return 0; +} + +static enum led_brightness toshiba_kbd_backlight_get(struct led_classdev *cdev) { struct toshiba_acpi_dev *dev = container_of(cdev, - struct toshiba_acpi_dev, led_dev); - u32 in[HCI_WORDS] = { 0, 0, 0, 0, 0, 0 }; + struct toshiba_acpi_dev, kbd_led); + u32 state, result; + acpi_status status; + + /* Check the keyboard backlight state */ + status = hci_read1(dev, HCI_KBD_ILLUMINATION, &state, &result); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to get the keyboard backlight failed\n"); + return LED_OFF; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight not supported\n"); + return LED_OFF; + } + + return state ? LED_FULL : LED_OFF; +} + +static void toshiba_kbd_backlight_set(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, kbd_led); + u32 state, result; + acpi_status status; + + /* Set the keyboard backlight state */ + state = brightness ? 1 : 0; + status = hci_write1(dev, HCI_KBD_ILLUMINATION, state, &result); + if (ACPI_FAILURE(status) || result == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to set KBD Illumination mode failed\n"); + return; + } else if (result == HCI_NOT_SUPPORTED) { + pr_info("Keyboard backlight not supported\n"); + return; + } +} + +/* TouchPad support */ +static int toshiba_touchpad_set(struct toshiba_acpi_dev *dev, u32 state) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_write(dev, SCI_TOUCHPAD, state, &result); + sci_close(dev); + if (ACPI_FAILURE(status)) { + pr_err("ACPI call to set the touchpad failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + return -ENODEV; + } + + return 0; +} + +static int toshiba_touchpad_get(struct toshiba_acpi_dev *dev, u32 *state) +{ + u32 result; + acpi_status status; + + if (!sci_open(dev)) + return -EIO; + + status = sci_read(dev, SCI_TOUCHPAD, state, &result); + sci_close(dev); + if (ACPI_FAILURE(status)) { + pr_err("ACPI call to query the touchpad failed\n"); + return -EIO; + } else if (result == HCI_NOT_SUPPORTED) { + return -ENODEV; + } + + return 0; +} + +/* Eco Mode support */ +static int toshiba_eco_mode_available(struct toshiba_acpi_dev *dev) +{ + acpi_status status; + u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[HCI_WORDS]; + + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_info("ACPI call to get ECO led failed\n"); + return 0; + } + + return 1; +} + +static enum led_brightness toshiba_eco_mode_get_status(struct led_classdev *cdev) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, eco_led); + u32 in[HCI_WORDS] = { HCI_GET, HCI_ECO_MODE, 0, 1, 0, 0 }; u32 out[HCI_WORDS]; acpi_status status; - enum led_brightness result; - /* First request : initialize communication. */ - in[0] = 0xf100; status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("Illumination device not available\n"); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to get ECO led failed\n"); return LED_OFF; } - /* Check the illumination */ - in[0] = 0xf300; - in[1] = 0x14e; + return out[2] ? LED_FULL : LED_OFF; +} + +static void toshiba_eco_mode_set_status(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct toshiba_acpi_dev *dev = container_of(cdev, + struct toshiba_acpi_dev, eco_led); + u32 in[HCI_WORDS] = { HCI_SET, HCI_ECO_MODE, 0, 1, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + /* Switch the Eco Mode led on/off */ + in[2] = (brightness) ? 1 : 0; status = hci_raw(dev, in, out); - if (ACPI_FAILURE(status)) { - pr_info("ACPI call for illumination failed.\n"); - return LED_OFF; + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to set ECO led failed\n"); + return; } +} - result = out[2] ? LED_FULL : LED_OFF; +/* Accelerometer support */ +static int toshiba_accelerometer_supported(struct toshiba_acpi_dev *dev) +{ + u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER2, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + /* Check if the accelerometer call exists, + * this call also serves as initialization + */ + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to query the accelerometer failed\n"); + return -EIO; + } else if (out[0] == HCI_DATA_NOT_AVAILABLE || + out[0] == HCI_NOT_INITIALIZED) { + pr_err("Accelerometer not initialized\n"); + return -EIO; + } else if (out[0] == HCI_NOT_SUPPORTED) { + pr_info("Accelerometer not supported\n"); + return -ENODEV; + } + + return 0; +} + +static int toshiba_accelerometer_get(struct toshiba_acpi_dev *dev, + u32 *xy, u32 *z) +{ + u32 in[HCI_WORDS] = { HCI_GET, HCI_ACCELEROMETER, 0, 1, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status; + + /* Check the Accelerometer status */ + status = hci_raw(dev, in, out); + if (ACPI_FAILURE(status) || out[0] == SCI_INPUT_DATA_ERROR) { + pr_err("ACPI call to query the accelerometer failed\n"); + return -EIO; + } - /* Last request : close communication. */ - in[0] = 0xf200; - in[1] = 0; - in[2] = 0; - hci_raw(dev, in, out); + *xy = out[2]; + *z = out[4]; - return result; + return 0; } /* Bluetooth rfkill handlers */ @@ -904,6 +1201,177 @@ static const struct backlight_ops toshiba_backlight_data = { .update_status = set_lcd_status, }; +/* + * Sysfs files + */ + +static ssize_t toshiba_kbd_bl_mode_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + int mode = -1; + int time = -1; + + if (sscanf(buf, "%i", &mode) != 1 && (mode != 2 || mode != 1)) + return -EINVAL; + + /* Set the Keyboard Backlight Mode where: + * Mode - Auto (2) | FN-Z (1) + * Auto - KBD backlight turns off automatically in given time + * FN-Z - KBD backlight "toggles" when hotkey pressed + */ + if (mode != -1 && toshiba->kbd_mode != mode) { + time = toshiba->kbd_time << HCI_MISC_SHIFT; + time = time + toshiba->kbd_mode; + if (toshiba_kbd_illum_status_set(toshiba, time) < 0) + return -EIO; + toshiba->kbd_mode = mode; + } + + return count; +} + +static ssize_t toshiba_kbd_bl_mode_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 time; + + if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) + return -EIO; + + return sprintf(buf, "%i\n", time & 0x07); +} + +static ssize_t toshiba_kbd_bl_timeout_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + int time = -1; + + if (sscanf(buf, "%i", &time) != 1 && (time < 0 || time > 60)) + return -EINVAL; + + /* Set the Keyboard Backlight Timeout: 0-60 seconds */ + if (time != -1 && toshiba->kbd_time != time) { + time = time << HCI_MISC_SHIFT; + time = (toshiba->kbd_mode == SCI_KBD_MODE_AUTO) ? + time + 1 : time + 2; + if (toshiba_kbd_illum_status_set(toshiba, time) < 0) + return -EIO; + toshiba->kbd_time = time >> HCI_MISC_SHIFT; + } + + return count; +} + +static ssize_t toshiba_kbd_bl_timeout_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 time; + + if (toshiba_kbd_illum_status_get(toshiba, &time) < 0) + return -EIO; + + return sprintf(buf, "%i\n", time >> HCI_MISC_SHIFT); +} + +static ssize_t toshiba_touchpad_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + int state; + + /* Set the TouchPad on/off, 0 - Disable | 1 - Enable */ + if (sscanf(buf, "%i", &state) == 1 && (state == 0 || state == 1)) { + if (toshiba_touchpad_set(toshiba, state) < 0) + return -EIO; + } + + return count; +} + +static ssize_t toshiba_touchpad_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 state; + int ret; + + ret = toshiba_touchpad_get(toshiba, &state); + if (ret < 0) + return ret; + + return sprintf(buf, "%i\n", state); +} + +static ssize_t toshiba_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct toshiba_acpi_dev *toshiba = dev_get_drvdata(dev); + u32 xyval, zval, tmp; + u16 x, y, z; + int ret; + + xyval = zval = 0; + ret = toshiba_accelerometer_get(toshiba, &xyval, &zval); + if (ret < 0) + return ret; + + x = xyval & HCI_ACCEL_MASK; + tmp = xyval >> HCI_MISC_SHIFT; + y = tmp & HCI_ACCEL_MASK; + z = zval & HCI_ACCEL_MASK; + + return sprintf(buf, "%d %d %d\n", x, y, z); +} + +static DEVICE_ATTR(kbd_backlight_mode, S_IRUGO | S_IWUSR, + toshiba_kbd_bl_mode_show, toshiba_kbd_bl_mode_store); +static DEVICE_ATTR(kbd_backlight_timeout, S_IRUGO | S_IWUSR, + toshiba_kbd_bl_timeout_show, toshiba_kbd_bl_timeout_store); +static DEVICE_ATTR(touchpad, S_IRUGO | S_IWUSR, + toshiba_touchpad_show, toshiba_touchpad_store); +static DEVICE_ATTR(position, S_IRUGO, toshiba_position_show, NULL); + +static struct attribute *toshiba_attributes[] = { + &dev_attr_kbd_backlight_mode.attr, + &dev_attr_kbd_backlight_timeout.attr, + &dev_attr_touchpad.attr, + &dev_attr_position.attr, + NULL, +}; + +static umode_t toshiba_sysfs_is_visible(struct kobject *kobj, + struct attribute *attr, int idx) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct toshiba_acpi_dev *drv = dev_get_drvdata(dev); + bool exists = true; + + if (attr == &dev_attr_kbd_backlight_mode.attr) + exists = (drv->kbd_illum_supported) ? true : false; + else if (attr == &dev_attr_kbd_backlight_timeout.attr) + exists = (drv->kbd_mode == SCI_KBD_MODE_AUTO) ? true : false; + else if (attr == &dev_attr_touchpad.attr) + exists = (drv->touchpad_supported) ? true : false; + else if (attr == &dev_attr_position.attr) + exists = (drv->accelerometer_supported) ? true : false; + + return exists ? attr->mode : 0; +} + +static struct attribute_group toshiba_attr_group = { + .is_visible = toshiba_sysfs_is_visible, + .attrs = toshiba_attributes, +}; + static bool toshiba_acpi_i8042_filter(unsigned char data, unsigned char str, struct serio *port) { @@ -1106,6 +1574,10 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) remove_toshiba_proc_entries(dev); + if (dev->sysfs_created) + sysfs_remove_group(&dev->acpi_dev->dev.kobj, + &toshiba_attr_group); + if (dev->ntfy_supported) { i8042_remove_filter(toshiba_acpi_i8042_filter); cancel_work_sync(&dev->hotkey_work); @@ -1127,6 +1599,12 @@ static int toshiba_acpi_remove(struct acpi_device *acpi_dev) if (dev->illumination_supported) led_classdev_unregister(&dev->led_dev); + if (dev->kbd_led_registered) + led_classdev_unregister(&dev->kbd_led); + + if (dev->eco_supported) + led_classdev_unregister(&dev->eco_led); + if (toshiba_acpi) toshiba_acpi = NULL; @@ -1172,6 +1650,7 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->acpi_dev = acpi_dev; dev->method_hci = hci_method; acpi_dev->driver_data = dev; + dev_set_drvdata(&acpi_dev->dev, dev); if (toshiba_acpi_setup_keyboard(dev)) pr_info("Unable to activate hotkeys\n"); @@ -1212,6 +1691,40 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) dev->illumination_supported = 1; } + if (toshiba_eco_mode_available(dev)) { + dev->eco_led.name = "toshiba::eco_mode"; + dev->eco_led.max_brightness = 1; + dev->eco_led.brightness_set = toshiba_eco_mode_set_status; + dev->eco_led.brightness_get = toshiba_eco_mode_get_status; + if (!led_classdev_register(&dev->acpi_dev->dev, &dev->eco_led)) + dev->eco_supported = 1; + } + + ret = toshiba_kbd_illum_status_get(dev, &dummy); + if (!ret) { + dev->kbd_time = dummy >> HCI_MISC_SHIFT; + dev->kbd_mode = dummy & 0x07; + } + dev->kbd_illum_supported = !ret; + /* + * Only register the LED if KBD illumination is supported + * and the keyboard backlight operation mode is set to FN-Z + */ + if (dev->kbd_illum_supported && dev->kbd_mode == SCI_KBD_MODE_FNZ) { + dev->kbd_led.name = "toshiba::kbd_backlight"; + dev->kbd_led.max_brightness = 1; + dev->kbd_led.brightness_set = toshiba_kbd_backlight_set; + dev->kbd_led.brightness_get = toshiba_kbd_backlight_get; + if (!led_classdev_register(&dev->acpi_dev->dev, &dev->kbd_led)) + dev->kbd_led_registered = 1; + } + + ret = toshiba_touchpad_get(dev, &dummy); + dev->touchpad_supported = !ret; + + ret = toshiba_accelerometer_supported(dev); + dev->accelerometer_supported = !ret; + /* Determine whether or not BIOS supports fan and video interfaces */ ret = get_video_status(dev, &dummy); @@ -1220,6 +1733,14 @@ static int toshiba_acpi_add(struct acpi_device *acpi_dev) ret = get_fan_status(dev, &dummy); dev->fan_supported = !ret; + ret = sysfs_create_group(&dev->acpi_dev->dev.kobj, + &toshiba_attr_group); + if (ret) { + dev->sysfs_created = 0; + goto error; + } + dev->sysfs_created = !ret; + create_toshiba_proc_entries(dev); toshiba_acpi = dev; diff --git a/drivers/regulator/bcm590xx-regulator.c b/drivers/regulator/bcm590xx-regulator.c index ab08ca7cfb08..c3750c5b382b 100644 --- a/drivers/regulator/bcm590xx-regulator.c +++ b/drivers/regulator/bcm590xx-regulator.c @@ -123,6 +123,7 @@ struct bcm590xx_info { #define BCM590XX_REG_RANGES(_name, _ranges) \ { \ .name = #_name, \ + .n_voltages = 64, \ .n_linear_ranges = ARRAY_SIZE(_ranges), \ .linear_ranges = _ranges, \ } diff --git a/drivers/regulator/s2mpa01.c b/drivers/regulator/s2mpa01.c index 808b3aa7a42c..f19a30f0fb42 100644 --- a/drivers/regulator/s2mpa01.c +++ b/drivers/regulator/s2mpa01.c @@ -192,13 +192,11 @@ static int s2mpa01_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) if (!ramp_enable) goto ramp_disable; - if (enable_shift) { - ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, - 1 << enable_shift, 1 << enable_shift); - if (ret) { - dev_err(&rdev->dev, "failed to enable ramp rate\n"); - return ret; - } + ret = regmap_update_bits(rdev->regmap, S2MPA01_REG_RAMP1, + 1 << enable_shift, 1 << enable_shift); + if (ret) { + dev_err(&rdev->dev, "failed to enable ramp rate\n"); + return ret; } ramp_val = get_ramp_delay(ramp_delay); diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 68fd54702edb..e713c162fbd4 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -202,13 +202,11 @@ static int s2mps11_set_ramp_delay(struct regulator_dev *rdev, int ramp_delay) if (!ramp_enable) goto ramp_disable; - if (enable_shift) { - ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, - 1 << enable_shift, 1 << enable_shift); - if (ret) { - dev_err(&rdev->dev, "failed to enable ramp rate\n"); - return ret; - } + ret = regmap_update_bits(rdev->regmap, S2MPS11_REG_RAMP, + 1 << enable_shift, 1 << enable_shift); + if (ret) { + dev_err(&rdev->dev, "failed to enable ramp rate\n"); + return ret; } ramp_val = get_ramp_delay(ramp_delay); diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index f05badabd69e..92f19a005dc3 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -964,6 +964,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) config.driver_data = s5m8767; config.regmap = iodev->regmap_pmic; config.of_node = pdata->regulators[i].reg_node; + config.ena_gpio = config.ena_gpio_flags = 0; if (pdata->regulators[i].ext_control_gpio) s5m8767_regulator_config_ext_control(s5m8767, &pdata->regulators[i], &config); diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index c8bd092fc945..02832d64d918 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -263,6 +263,9 @@ config SCSI_SCAN_ASYNC You can override this choice by specifying "scsi_mod.scan=sync" or async on the kernel's command line. + Note that this setting also affects whether resuming from + system suspend will be performed asynchronously. + menu "SCSI Transports" depends on SCSI diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 788c4fe2b0c9..68fb66fdb757 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -684,6 +684,20 @@ static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) qlt_xmit_tm_rsp(mcmd); } +static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) +{ + struct qla_tgt_cmd *cmd = container_of(se_cmd, + struct qla_tgt_cmd, se_cmd); + struct scsi_qla_host *vha = cmd->vha; + struct qla_hw_data *ha = vha->hw; + + if (!cmd->sg_mapped) + return; + + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); + cmd->sg_mapped = 0; +} + /* Local pointer to allocated TCM configfs fabric module */ struct target_fabric_configfs *tcm_qla2xxx_fabric_configfs; struct target_fabric_configfs *tcm_qla2xxx_npiv_fabric_configfs; @@ -1468,7 +1482,7 @@ static int tcm_qla2xxx_check_initiator_node_acl( } se_tpg = &tpg->se_tpg; - se_sess = transport_init_session(); + se_sess = transport_init_session(TARGET_PROT_NORMAL); if (IS_ERR(se_sess)) { pr_err("Unable to initialize struct se_session\n"); return PTR_ERR(se_sess); @@ -1877,6 +1891,7 @@ static struct target_core_fabric_ops tcm_qla2xxx_ops = { .queue_data_in = tcm_qla2xxx_queue_data_in, .queue_status = tcm_qla2xxx_queue_status, .queue_tm_rsp = tcm_qla2xxx_queue_tm_rsp, + .aborted_task = tcm_qla2xxx_aborted_task, /* * Setup function pointers for generic logic in * target_core_fabric_configfs.c @@ -1926,6 +1941,7 @@ static struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { .queue_data_in = tcm_qla2xxx_queue_data_in, .queue_status = tcm_qla2xxx_queue_status, .queue_tm_rsp = tcm_qla2xxx_queue_tm_rsp, + .aborted_task = tcm_qla2xxx_aborted_task, /* * Setup function pointers for generic logic in * target_core_fabric_configfs.c diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c4d632c27a3e..88d46fe6bf98 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -91,6 +91,15 @@ EXPORT_SYMBOL(scsi_logging_level); ASYNC_DOMAIN(scsi_sd_probe_domain); EXPORT_SYMBOL(scsi_sd_probe_domain); +/* + * Separate domain (from scsi_sd_probe_domain) to maximize the benefit of + * asynchronous system resume operations. It is marked 'exclusive' to avoid + * being included in the async_synchronize_full() that is invoked by + * dpm_resume() + */ +ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain); +EXPORT_SYMBOL(scsi_sd_pm_domain); + /* NB: These are exposed through /proc/scsi/scsi and form part of the ABI. * You may not alter any existing entry (although adding new ones is * encouraged once assigned by ANSI/INCITS T10 diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 5681c05ac506..65a123d9c676 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -184,7 +184,7 @@ void scsi_queue_insert(struct scsi_cmnd *cmd, int reason) */ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd, int data_direction, void *buffer, unsigned bufflen, - unsigned char *sense, int timeout, int retries, int flags, + unsigned char *sense, int timeout, int retries, u64 flags, int *resid) { struct request *req; @@ -235,7 +235,7 @@ EXPORT_SYMBOL(scsi_execute); int scsi_execute_req_flags(struct scsi_device *sdev, const unsigned char *cmd, int data_direction, void *buffer, unsigned bufflen, struct scsi_sense_hdr *sshdr, int timeout, int retries, - int *resid, int flags) + int *resid, u64 flags) { char *sense = NULL; int result; diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index 001e9ceda4c3..7454498c4091 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -18,35 +18,77 @@ #ifdef CONFIG_PM_SLEEP -static int scsi_dev_type_suspend(struct device *dev, int (*cb)(struct device *)) +static int do_scsi_suspend(struct device *dev, const struct dev_pm_ops *pm) { + return pm && pm->suspend ? pm->suspend(dev) : 0; +} + +static int do_scsi_freeze(struct device *dev, const struct dev_pm_ops *pm) +{ + return pm && pm->freeze ? pm->freeze(dev) : 0; +} + +static int do_scsi_poweroff(struct device *dev, const struct dev_pm_ops *pm) +{ + return pm && pm->poweroff ? pm->poweroff(dev) : 0; +} + +static int do_scsi_resume(struct device *dev, const struct dev_pm_ops *pm) +{ + return pm && pm->resume ? pm->resume(dev) : 0; +} + +static int do_scsi_thaw(struct device *dev, const struct dev_pm_ops *pm) +{ + return pm && pm->thaw ? pm->thaw(dev) : 0; +} + +static int do_scsi_restore(struct device *dev, const struct dev_pm_ops *pm) +{ + return pm && pm->restore ? pm->restore(dev) : 0; +} + +static int scsi_dev_type_suspend(struct device *dev, + int (*cb)(struct device *, const struct dev_pm_ops *)) +{ + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int err; + /* flush pending in-flight resume operations, suspend is synchronous */ + async_synchronize_full_domain(&scsi_sd_pm_domain); + err = scsi_device_quiesce(to_scsi_device(dev)); if (err == 0) { - if (cb) { - err = cb(dev); - if (err) - scsi_device_resume(to_scsi_device(dev)); - } + err = cb(dev, pm); + if (err) + scsi_device_resume(to_scsi_device(dev)); } dev_dbg(dev, "scsi suspend: %d\n", err); return err; } -static int scsi_dev_type_resume(struct device *dev, int (*cb)(struct device *)) +static int scsi_dev_type_resume(struct device *dev, + int (*cb)(struct device *, const struct dev_pm_ops *)) { + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int err = 0; - if (cb) - err = cb(dev); + err = cb(dev, pm); scsi_device_resume(to_scsi_device(dev)); dev_dbg(dev, "scsi resume: %d\n", err); + + if (err == 0) { + pm_runtime_disable(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + } + return err; } static int -scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *)) +scsi_bus_suspend_common(struct device *dev, + int (*cb)(struct device *, const struct dev_pm_ops *)) { int err = 0; @@ -66,20 +108,54 @@ scsi_bus_suspend_common(struct device *dev, int (*cb)(struct device *)) return err; } -static int -scsi_bus_resume_common(struct device *dev, int (*cb)(struct device *)) +static void async_sdev_resume(void *dev, async_cookie_t cookie) { - int err = 0; + scsi_dev_type_resume(dev, do_scsi_resume); +} - if (scsi_is_sdev_device(dev)) - err = scsi_dev_type_resume(dev, cb); +static void async_sdev_thaw(void *dev, async_cookie_t cookie) +{ + scsi_dev_type_resume(dev, do_scsi_thaw); +} - if (err == 0) { +static void async_sdev_restore(void *dev, async_cookie_t cookie) +{ + scsi_dev_type_resume(dev, do_scsi_restore); +} + +static int scsi_bus_resume_common(struct device *dev, + int (*cb)(struct device *, const struct dev_pm_ops *)) +{ + async_func_t fn; + + if (!scsi_is_sdev_device(dev)) + fn = NULL; + else if (cb == do_scsi_resume) + fn = async_sdev_resume; + else if (cb == do_scsi_thaw) + fn = async_sdev_thaw; + else if (cb == do_scsi_restore) + fn = async_sdev_restore; + else + fn = NULL; + + if (fn) { + async_schedule_domain(fn, dev, &scsi_sd_pm_domain); + + /* + * If a user has disabled async probing a likely reason + * is due to a storage enclosure that does not inject + * staggered spin-ups. For safety, make resume + * synchronous as well in that case. + */ + if (strncmp(scsi_scan_type, "async", 5) != 0) + async_synchronize_full_domain(&scsi_sd_pm_domain); + } else { pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); } - return err; + return 0; } static int scsi_bus_prepare(struct device *dev) @@ -97,38 +173,32 @@ static int scsi_bus_prepare(struct device *dev) static int scsi_bus_suspend(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - return scsi_bus_suspend_common(dev, pm ? pm->suspend : NULL); + return scsi_bus_suspend_common(dev, do_scsi_suspend); } static int scsi_bus_resume(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - return scsi_bus_resume_common(dev, pm ? pm->resume : NULL); + return scsi_bus_resume_common(dev, do_scsi_resume); } static int scsi_bus_freeze(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - return scsi_bus_suspend_common(dev, pm ? pm->freeze : NULL); + return scsi_bus_suspend_common(dev, do_scsi_freeze); } static int scsi_bus_thaw(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - return scsi_bus_resume_common(dev, pm ? pm->thaw : NULL); + return scsi_bus_resume_common(dev, do_scsi_thaw); } static int scsi_bus_poweroff(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - return scsi_bus_suspend_common(dev, pm ? pm->poweroff : NULL); + return scsi_bus_suspend_common(dev, do_scsi_poweroff); } static int scsi_bus_restore(struct device *dev) { - const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; - return scsi_bus_resume_common(dev, pm ? pm->restore : NULL); + return scsi_bus_resume_common(dev, do_scsi_restore); } #else /* CONFIG_PM_SLEEP */ diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index f079a598bed4..48e5b657e79f 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -112,6 +112,7 @@ extern void scsi_exit_procfs(void); #endif /* CONFIG_PROC_FS */ /* scsi_scan.c */ +extern char scsi_scan_type[]; extern int scsi_complete_async_scans(void); extern int scsi_scan_host_selected(struct Scsi_Host *, unsigned int, unsigned int, unsigned int, int); @@ -166,6 +167,7 @@ static inline int scsi_autopm_get_host(struct Scsi_Host *h) { return 0; } static inline void scsi_autopm_put_host(struct Scsi_Host *h) {} #endif /* CONFIG_PM_RUNTIME */ +extern struct async_domain scsi_sd_pm_domain; extern struct async_domain scsi_sd_probe_domain; /* diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index 27f96d5b7680..e02b3aab56ce 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -97,7 +97,7 @@ MODULE_PARM_DESC(max_luns, #define SCSI_SCAN_TYPE_DEFAULT "sync" #endif -static char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT; +char scsi_scan_type[6] = SCSI_SCAN_TYPE_DEFAULT; module_param_string(scan, scsi_scan_type, sizeof(scsi_scan_type), S_IRUGO); MODULE_PARM_DESC(scan, "sync, async or none"); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 89e6c04ac595..efcbcd182863 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -3026,6 +3026,7 @@ static int sd_remove(struct device *dev) devt = disk_devt(sdkp->disk); scsi_autopm_get_device(sdkp->device); + async_synchronize_full_domain(&scsi_sd_pm_domain); async_synchronize_full_domain(&scsi_sd_probe_domain); blk_queue_prep_rq(sdkp->device->request_queue, scsi_prep_fn); blk_queue_unprep_rq(sdkp->device->request_queue, NULL); diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index efe1960af2b3..60f2b41c7310 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -383,7 +383,7 @@ config SPI_RSPI config SPI_QUP tristate "Qualcomm SPI controller with QUP interface" - depends on ARCH_MSM_DT || (ARM && COMPILE_TEST) + depends on ARCH_QCOM || (ARM && COMPILE_TEST) help Qualcomm Universal Peripheral (QUP) core is an AHB slave that provides a common data path (an output FIFO and an input FIFO) diff --git a/drivers/spi/spi-fsl-espi.c b/drivers/spi/spi-fsl-espi.c index 6fb2b75df821..e767f5831b9c 100644 --- a/drivers/spi/spi-fsl-espi.c +++ b/drivers/spi/spi-fsl-espi.c @@ -441,7 +441,8 @@ static void fsl_espi_do_one_msg(struct spi_message *m) m->actual_length = espi_trans.actual_length; m->status = espi_trans.status; - m->complete(m->context); + if (m->complete) + m->complete(m->context); } static int fsl_espi_setup(struct spi_device *spi) diff --git a/drivers/spi/spi-fsl-spi.c b/drivers/spi/spi-fsl-spi.c index f35488ed62a9..b3e7775034db 100644 --- a/drivers/spi/spi-fsl-spi.c +++ b/drivers/spi/spi-fsl-spi.c @@ -408,7 +408,8 @@ static void fsl_spi_do_one_msg(struct spi_message *m) } m->status = status; - m->complete(m->context); + if (m->complete) + m->complete(m->context); if (status || !cs_change) { ndelay(nsecs); diff --git a/drivers/spi/spi-mpc512x-psc.c b/drivers/spi/spi-mpc512x-psc.c index 3822eef2ef9d..577d23a12763 100644 --- a/drivers/spi/spi-mpc512x-psc.c +++ b/drivers/spi/spi-mpc512x-psc.c @@ -300,7 +300,8 @@ static int mpc512x_psc_spi_msg_xfer(struct spi_master *master, } m->status = status; - m->complete(m->context); + if (m->complete) + m->complete(m->context); if (status || !cs_change) mpc512x_psc_spi_deactivate_cs(spi); diff --git a/drivers/spi/spi-mpc52xx-psc.c b/drivers/spi/spi-mpc52xx-psc.c index 3d18d9351185..de532aa11d34 100644 --- a/drivers/spi/spi-mpc52xx-psc.c +++ b/drivers/spi/spi-mpc52xx-psc.c @@ -247,7 +247,8 @@ static void mpc52xx_psc_spi_work(struct work_struct *work) } m->status = status; - m->complete(m->context); + if (m->complete) + m->complete(m->context); if (status || !cs_change) mpc52xx_psc_spi_deactivate_cs(spi); diff --git a/drivers/spi/spi-mpc52xx.c b/drivers/spi/spi-mpc52xx.c index aac2a5ddd964..b07db4b62d80 100644 --- a/drivers/spi/spi-mpc52xx.c +++ b/drivers/spi/spi-mpc52xx.c @@ -234,7 +234,8 @@ static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms, dev_err(&ms->master->dev, "mode fault\n"); mpc52xx_spi_chipsel(ms, 0); ms->message->status = -EIO; - ms->message->complete(ms->message->context); + if (ms->message->complete) + ms->message->complete(ms->message->context); ms->state = mpc52xx_spi_fsmstate_idle; return FSM_CONTINUE; } @@ -288,7 +289,8 @@ mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, u8 status, u8 data) ms->msg_count++; mpc52xx_spi_chipsel(ms, 0); ms->message->status = 0; - ms->message->complete(ms->message->context); + if (ms->message->complete) + ms->message->complete(ms->message->context); ms->state = mpc52xx_spi_fsmstate_idle; return FSM_CONTINUE; } diff --git a/drivers/spi/spi-omap2-mcspi.c b/drivers/spi/spi-omap2-mcspi.c index 2941c5b96ebc..4dc77df38864 100644 --- a/drivers/spi/spi-omap2-mcspi.c +++ b/drivers/spi/spi-omap2-mcspi.c @@ -1379,12 +1379,13 @@ static int omap2_mcspi_probe(struct platform_device *pdev) INIT_LIST_HEAD(&mcspi->ctx.cs); - mcspi->dma_channels = kcalloc(master->num_chipselect, - sizeof(struct omap2_mcspi_dma), - GFP_KERNEL); - - if (mcspi->dma_channels == NULL) + mcspi->dma_channels = devm_kcalloc(&pdev->dev, master->num_chipselect, + sizeof(struct omap2_mcspi_dma), + GFP_KERNEL); + if (mcspi->dma_channels == NULL) { + status = -ENOMEM; goto free_master; + } for (i = 0; i < master->num_chipselect; i++) { char *dma_rx_ch_name = mcspi->dma_channels[i].dma_rx_ch_name; @@ -1426,7 +1427,7 @@ static int omap2_mcspi_probe(struct platform_device *pdev) } if (status < 0) - goto dma_chnl_free; + goto free_master; pm_runtime_use_autosuspend(&pdev->dev); pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT); @@ -1444,8 +1445,6 @@ static int omap2_mcspi_probe(struct platform_device *pdev) disable_pm: pm_runtime_disable(&pdev->dev); -dma_chnl_free: - kfree(mcspi->dma_channels); free_master: spi_master_put(master); return status; @@ -1453,19 +1452,12 @@ free_master: static int omap2_mcspi_remove(struct platform_device *pdev) { - struct spi_master *master; - struct omap2_mcspi *mcspi; - struct omap2_mcspi_dma *dma_channels; - - master = platform_get_drvdata(pdev); - mcspi = spi_master_get_devdata(master); - dma_channels = mcspi->dma_channels; + struct spi_master *master = platform_get_drvdata(pdev); + struct omap2_mcspi *mcspi = spi_master_get_devdata(master); pm_runtime_put_sync(mcspi->dev); pm_runtime_disable(&pdev->dev); - kfree(dma_channels); - return 0; } diff --git a/drivers/spi/spi-sh.c b/drivers/spi/spi-sh.c index f6f2c7010177..03edf5ed0e9f 100644 --- a/drivers/spi/spi-sh.c +++ b/drivers/spi/spi-sh.c @@ -322,7 +322,8 @@ static void spi_sh_work(struct work_struct *work) spin_lock_irqsave(&ss->lock, flags); mesg->status = 0; - mesg->complete(mesg->context); + if (mesg->complete) + mesg->complete(mesg->context); } clear_fifo(ss); @@ -340,7 +341,8 @@ static void spi_sh_work(struct work_struct *work) error: mesg->status = ret; - mesg->complete(mesg->context); + if (mesg->complete) + mesg->complete(mesg->context); spi_sh_clear_bit(ss, SPI_SH_SSA | SPI_SH_SSDB | SPI_SH_SSD, SPI_SH_CR1); diff --git a/drivers/spi/spi-txx9.c b/drivers/spi/spi-txx9.c index 820b499816f8..5f183baa91a9 100644 --- a/drivers/spi/spi-txx9.c +++ b/drivers/spi/spi-txx9.c @@ -262,7 +262,8 @@ static void txx9spi_work_one(struct txx9spi *c, struct spi_message *m) exit: m->status = status; - m->complete(m->context); + if (m->complete) + m->complete(m->context); /* normally deactivate chipselect ... unless no error and * cs_change has hinted that the next message will probably diff --git a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c index a54b506ba7ca..b87b246111c0 100644 --- a/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c +++ b/drivers/staging/lustre/lnet/klnds/socklnd/socklnd_lib-linux.c @@ -99,16 +99,7 @@ ksocknal_lib_send_iov (ksock_conn_t *conn, ksock_tx_t *tx) struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov; unsigned int niov = tx->tx_niov; #endif - struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = scratchiov, - .msg_iovlen = niov, - .msg_control = NULL, - .msg_controllen = 0, - .msg_flags = MSG_DONTWAIT - }; - mm_segment_t oldmm = get_fs(); + struct msghdr msg = {.msg_flags = MSG_DONTWAIT}; int i; for (nob = i = 0; i < niov; i++) { @@ -120,9 +111,7 @@ ksocknal_lib_send_iov (ksock_conn_t *conn, ksock_tx_t *tx) nob < tx->tx_resid) msg.msg_flags |= MSG_MORE; - set_fs (KERNEL_DS); - rc = sock_sendmsg(sock, &msg, nob); - set_fs (oldmm); + rc = kernel_sendmsg(sock, &msg, (struct kvec *)scratchiov, niov, nob); } return rc; } @@ -174,16 +163,7 @@ ksocknal_lib_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx) struct iovec *scratchiov = conn->ksnc_scheduler->kss_scratch_iov; unsigned int niov = tx->tx_nkiov; #endif - struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = scratchiov, - .msg_iovlen = niov, - .msg_control = NULL, - .msg_controllen = 0, - .msg_flags = MSG_DONTWAIT - }; - mm_segment_t oldmm = get_fs(); + struct msghdr msg = {.msg_flags = MSG_DONTWAIT}; int i; for (nob = i = 0; i < niov; i++) { @@ -196,9 +176,7 @@ ksocknal_lib_send_kiov (ksock_conn_t *conn, ksock_tx_t *tx) nob < tx->tx_resid) msg.msg_flags |= MSG_MORE; - set_fs (KERNEL_DS); - rc = sock_sendmsg(sock, &msg, nob); - set_fs (oldmm); + rc = kernel_sendmsg(sock, &msg, (struct kvec *)scratchiov, niov, nob); for (i = 0; i < niov; i++) kunmap(kiov[i].kiov_page); @@ -237,15 +215,8 @@ ksocknal_lib_recv_iov (ksock_conn_t *conn) #endif struct iovec *iov = conn->ksnc_rx_iov; struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = scratchiov, - .msg_iovlen = niov, - .msg_control = NULL, - .msg_controllen = 0, .msg_flags = 0 }; - mm_segment_t oldmm = get_fs(); int nob; int i; int rc; @@ -263,10 +234,8 @@ ksocknal_lib_recv_iov (ksock_conn_t *conn) } LASSERT (nob <= conn->ksnc_rx_nob_wanted); - set_fs (KERNEL_DS); - rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT); - /* NB this is just a boolean..........................^ */ - set_fs (oldmm); + rc = kernel_recvmsg(conn->ksnc_sock, &msg, + (struct kvec *)scratchiov, niov, nob, MSG_DONTWAIT); saved_csum = 0; if (conn->ksnc_proto == &ksocknal_protocol_v2x) { @@ -355,14 +324,8 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn) #endif lnet_kiov_t *kiov = conn->ksnc_rx_kiov; struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = scratchiov, - .msg_control = NULL, - .msg_controllen = 0, .msg_flags = 0 }; - mm_segment_t oldmm = get_fs(); int nob; int i; int rc; @@ -370,13 +333,14 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn) void *addr; int sum; int fragnob; + int n; /* NB we can't trust socket ops to either consume our iovs * or leave them alone. */ addr = ksocknal_lib_kiov_vmap(kiov, niov, scratchiov, pages); if (addr != NULL) { nob = scratchiov[0].iov_len; - msg.msg_iovlen = 1; + n = 1; } else { for (nob = i = 0; i < niov; i++) { @@ -384,15 +348,13 @@ ksocknal_lib_recv_kiov (ksock_conn_t *conn) scratchiov[i].iov_base = kmap(kiov[i].kiov_page) + kiov[i].kiov_offset; } - msg.msg_iovlen = niov; + n = niov; } LASSERT (nob <= conn->ksnc_rx_nob_wanted); - set_fs (KERNEL_DS); - rc = sock_recvmsg (conn->ksnc_sock, &msg, nob, MSG_DONTWAIT); - /* NB this is just a boolean.......................^ */ - set_fs (oldmm); + rc = kernel_recvmsg(conn->ksnc_sock, &msg, + (struct kvec *)scratchiov, n, nob, MSG_DONTWAIT); if (conn->ksnc_msg.ksm_csum != 0) { for (i = 0, sum = rc; sum > 0; i++, sum -= fragnob) { diff --git a/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c b/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c index e6069d78af6b..7539fe16d76f 100644 --- a/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c +++ b/drivers/staging/lustre/lustre/libcfs/linux/linux-tcpip.c @@ -265,17 +265,11 @@ libcfs_sock_write (struct socket *sock, void *buffer, int nob, int timeout) * empty enough to take the whole message immediately */ for (;;) { - struct iovec iov = { + struct kvec iov = { .iov_base = buffer, .iov_len = nob }; struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = NULL, - .msg_controllen = 0, .msg_flags = (timeout == 0) ? MSG_DONTWAIT : 0 }; @@ -297,11 +291,9 @@ libcfs_sock_write (struct socket *sock, void *buffer, int nob, int timeout) } } - set_fs (KERNEL_DS); then = jiffies; - rc = sock_sendmsg (sock, &msg, iov.iov_len); + rc = kernel_sendmsg(sock, &msg, &iov, 1, nob); ticks -= jiffies - then; - set_fs (oldmm); if (rc == nob) return 0; @@ -338,17 +330,11 @@ libcfs_sock_read (struct socket *sock, void *buffer, int nob, int timeout) LASSERT (ticks > 0); for (;;) { - struct iovec iov = { + struct kvec iov = { .iov_base = buffer, .iov_len = nob }; struct msghdr msg = { - .msg_name = NULL, - .msg_namelen = 0, - .msg_iov = &iov, - .msg_iovlen = 1, - .msg_control = NULL, - .msg_controllen = 0, .msg_flags = 0 }; @@ -367,11 +353,9 @@ libcfs_sock_read (struct socket *sock, void *buffer, int nob, int timeout) return rc; } - set_fs(KERNEL_DS); then = jiffies; - rc = sock_recvmsg(sock, &msg, iov.iov_len, 0); + rc = kernel_recvmsg(sock, &msg, &iov, 1, nob, 0); ticks -= jiffies - then; - set_fs(oldmm); if (rc < 0) return rc; diff --git a/drivers/staging/lustre/lustre/llite/symlink.c b/drivers/staging/lustre/lustre/llite/symlink.c index ab06891f7fc7..80d48b5ae247 100644 --- a/drivers/staging/lustre/lustre/llite/symlink.c +++ b/drivers/staging/lustre/lustre/llite/symlink.c @@ -115,27 +115,6 @@ failed: return rc; } -static int ll_readlink(struct dentry *dentry, char *buffer, int buflen) -{ - struct inode *inode = dentry->d_inode; - struct ptlrpc_request *request; - char *symname; - int rc; - - CDEBUG(D_VFSTRACE, "VFS Op\n"); - - ll_inode_size_lock(inode); - rc = ll_readlink_internal(inode, &request, &symname); - if (rc) - GOTO(out, rc); - - rc = vfs_readlink(dentry, buffer, buflen, symname); - out: - ptlrpc_req_finished(request); - ll_inode_size_unlock(inode); - return rc; -} - static void *ll_follow_link(struct dentry *dentry, struct nameidata *nd) { struct inode *inode = dentry->d_inode; @@ -175,7 +154,7 @@ static void ll_put_link(struct dentry *dentry, struct nameidata *nd, void *cooki } struct inode_operations ll_fast_symlink_inode_operations = { - .readlink = ll_readlink, + .readlink = generic_readlink, .setattr = ll_setattr, .follow_link = ll_follow_link, .put_link = ll_put_link, diff --git a/drivers/staging/media/msi3101/msi001.c b/drivers/staging/media/msi3101/msi001.c index ac43bae10102..bd0b93cb6c53 100644 --- a/drivers/staging/media/msi3101/msi001.c +++ b/drivers/staging/media/msi3101/msi001.c @@ -201,7 +201,7 @@ static int msi001_set_tuner(struct msi001 *s) dev_dbg(&s->spi->dev, "%s: bandwidth selected=%d\n", __func__, bandwidth_lut[i].freq); - f_vco = (f_rf + f_if + f_if1) * lo_div; + f_vco = (u64) (f_rf + f_if + f_if1) * lo_div; tmp64 = f_vco; m = do_div(tmp64, F_REF * R_REF); n = (unsigned int) tmp64; diff --git a/drivers/staging/media/msi3101/sdr-msi3101.c b/drivers/staging/media/msi3101/sdr-msi3101.c index 260d1b736721..65d351f99da2 100644 --- a/drivers/staging/media/msi3101/sdr-msi3101.c +++ b/drivers/staging/media/msi3101/sdr-msi3101.c @@ -913,7 +913,6 @@ static int msi3101_set_usb_adc(struct msi3101_state *s) /* set tuner, subdev, filters according to sampling rate */ bandwidth_auto = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH_AUTO); - bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); if (v4l2_ctrl_g_ctrl(bandwidth_auto)) { bandwidth = v4l2_ctrl_find(&s->hdl, V4L2_CID_RF_TUNER_BANDWIDTH); v4l2_ctrl_s_ctrl(bandwidth, s->f_adc); @@ -1078,6 +1077,7 @@ static int msi3101_start_streaming(struct vb2_queue *vq, unsigned int count) static int msi3101_stop_streaming(struct vb2_queue *vq) { struct msi3101_state *s = vb2_get_drv_priv(vq); + int ret; dev_dbg(&s->udev->dev, "%s:\n", __func__); if (mutex_lock_interruptible(&s->v4l2_lock)) @@ -1090,17 +1090,22 @@ static int msi3101_stop_streaming(struct vb2_queue *vq) /* according to tests, at least 700us delay is required */ msleep(20); - msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0); + ret = msi3101_ctrl_msg(s, CMD_STOP_STREAMING, 0); + if (ret) + goto err_sleep_tuner; /* sleep USB IF / ADC */ - msi3101_ctrl_msg(s, CMD_WREG, 0x01000003); + ret = msi3101_ctrl_msg(s, CMD_WREG, 0x01000003); + if (ret) + goto err_sleep_tuner; +err_sleep_tuner: /* sleep tuner */ - v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0); + ret = v4l2_subdev_call(s->v4l2_subdev, core, s_power, 0); mutex_unlock(&s->v4l2_lock); - return 0; + return ret; } static struct vb2_ops msi3101_vb2_ops = { diff --git a/drivers/staging/usbip/stub_dev.c b/drivers/staging/usbip/stub_dev.c index 773d8ca07a00..de692d7011a5 100644 --- a/drivers/staging/usbip/stub_dev.c +++ b/drivers/staging/usbip/stub_dev.c @@ -86,7 +86,6 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, struct stub_device *sdev = dev_get_drvdata(dev); int sockfd = 0; struct socket *socket; - ssize_t err = -EINVAL; int rv; if (!sdev) { @@ -99,6 +98,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, return -EINVAL; if (sockfd != -1) { + int err; dev_info(dev, "stub up\n"); spin_lock_irq(&sdev->ud.lock); @@ -108,7 +108,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, goto err; } - socket = sockfd_to_socket(sockfd); + socket = sockfd_lookup(sockfd, &err); if (!socket) goto err; @@ -141,7 +141,7 @@ static ssize_t store_sockfd(struct device *dev, struct device_attribute *attr, err: spin_unlock_irq(&sdev->ud.lock); - return err; + return -EINVAL; } static DEVICE_ATTR(usbip_sockfd, S_IWUSR, NULL, store_sockfd); @@ -211,7 +211,7 @@ static void stub_shutdown_connection(struct usbip_device *ud) * not touch NULL socket. */ if (ud->tcp_socket) { - fput(ud->tcp_socket->file); + sockfd_put(ud->tcp_socket); ud->tcp_socket = NULL; } diff --git a/drivers/staging/usbip/usbip_common.c b/drivers/staging/usbip/usbip_common.c index 184fa70365db..facaaf003f19 100644 --- a/drivers/staging/usbip/usbip_common.c +++ b/drivers/staging/usbip/usbip_common.c @@ -382,31 +382,6 @@ err: } EXPORT_SYMBOL_GPL(usbip_recv); -struct socket *sockfd_to_socket(unsigned int sockfd) -{ - struct socket *socket; - struct file *file; - struct inode *inode; - - file = fget(sockfd); - if (!file) { - pr_err("invalid sockfd\n"); - return NULL; - } - - inode = file_inode(file); - - if (!inode || !S_ISSOCK(inode->i_mode)) { - fput(file); - return NULL; - } - - socket = SOCKET_I(inode); - - return socket; -} -EXPORT_SYMBOL_GPL(sockfd_to_socket); - /* there may be more cases to tweak the flags. */ static unsigned int tweak_transfer_flags(unsigned int flags) { diff --git a/drivers/staging/usbip/usbip_common.h b/drivers/staging/usbip/usbip_common.h index 732fb636a1e5..f555d834f134 100644 --- a/drivers/staging/usbip/usbip_common.h +++ b/drivers/staging/usbip/usbip_common.h @@ -299,7 +299,6 @@ void usbip_dump_urb(struct urb *purb); void usbip_dump_header(struct usbip_header *pdu); int usbip_recv(struct socket *sock, void *buf, int size); -struct socket *sockfd_to_socket(unsigned int sockfd); void usbip_pack_pdu(struct usbip_header *pdu, struct urb *urb, int cmd, int pack); diff --git a/drivers/staging/usbip/vhci_hcd.c b/drivers/staging/usbip/vhci_hcd.c index 1e84577230ef..70e17551943d 100644 --- a/drivers/staging/usbip/vhci_hcd.c +++ b/drivers/staging/usbip/vhci_hcd.c @@ -788,7 +788,7 @@ static void vhci_shutdown_connection(struct usbip_device *ud) /* active connection is closed */ if (vdev->ud.tcp_socket) { - fput(vdev->ud.tcp_socket->file); + sockfd_put(vdev->ud.tcp_socket); vdev->ud.tcp_socket = NULL; } pr_info("release socket\n"); @@ -835,7 +835,7 @@ static void vhci_device_reset(struct usbip_device *ud) vdev->udev = NULL; if (ud->tcp_socket) { - fput(ud->tcp_socket->file); + sockfd_put(ud->tcp_socket); ud->tcp_socket = NULL; } ud->status = VDEV_ST_NULL; diff --git a/drivers/staging/usbip/vhci_sysfs.c b/drivers/staging/usbip/vhci_sysfs.c index e0980324fb03..47bddcdde0a6 100644 --- a/drivers/staging/usbip/vhci_sysfs.c +++ b/drivers/staging/usbip/vhci_sysfs.c @@ -176,6 +176,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, struct socket *socket; int sockfd = 0; __u32 rhport = 0, devid = 0, speed = 0; + int err; /* * @rhport: port number of vhci_hcd @@ -194,8 +195,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, return -EINVAL; /* Extract socket from fd. */ - /* The correct way to clean this up is to fput(socket->file). */ - socket = sockfd_to_socket(sockfd); + socket = sockfd_lookup(sockfd, &err); if (!socket) return -EINVAL; @@ -211,7 +211,7 @@ static ssize_t store_attach(struct device *dev, struct device_attribute *attr, spin_unlock(&vdev->ud.lock); spin_unlock(&the_controller->lock); - fput(socket->file); + sockfd_put(socket); dev_err(dev, "port %d already used\n", rhport); return -EINVAL; diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index b83ec378d04f..78cab13bbb1b 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -499,6 +499,23 @@ static int iscsit_queue_rsp(struct iscsi_conn *conn, struct iscsi_cmd *cmd) return 0; } +static void iscsit_aborted_task(struct iscsi_conn *conn, struct iscsi_cmd *cmd) +{ + bool scsi_cmd = (cmd->iscsi_opcode == ISCSI_OP_SCSI_CMD); + + spin_lock_bh(&conn->cmd_lock); + if (!list_empty(&cmd->i_conn_node)) + list_del_init(&cmd->i_conn_node); + spin_unlock_bh(&conn->cmd_lock); + + __iscsit_free_cmd(cmd, scsi_cmd, true); +} + +static enum target_prot_op iscsit_get_sup_prot_ops(struct iscsi_conn *conn) +{ + return TARGET_PROT_NORMAL; +} + static struct iscsit_transport iscsi_target_transport = { .name = "iSCSI/TCP", .transport_type = ISCSI_TCP, @@ -513,6 +530,8 @@ static struct iscsit_transport iscsi_target_transport = { .iscsit_response_queue = iscsit_response_queue, .iscsit_queue_data_in = iscsit_queue_rsp, .iscsit_queue_status = iscsit_queue_rsp, + .iscsit_aborted_task = iscsit_aborted_task, + .iscsit_get_sup_prot_ops = iscsit_get_sup_prot_ops, }; static int __init iscsi_target_init_module(void) @@ -1503,6 +1522,16 @@ int iscsit_setup_nop_out(struct iscsi_conn *conn, struct iscsi_cmd *cmd, { u32 payload_length = ntoh24(hdr->dlength); + if (!(hdr->flags & ISCSI_FLAG_CMD_FINAL)) { + pr_err("NopOUT Flag's, Left Most Bit not set, protocol error.\n"); + if (!cmd) + return iscsit_add_reject(conn, ISCSI_REASON_PROTOCOL_ERROR, + (unsigned char *)hdr); + + return iscsit_reject_cmd(cmd, ISCSI_REASON_PROTOCOL_ERROR, + (unsigned char *)hdr); + } + if (hdr->itt == RESERVED_ITT && !(hdr->opcode & ISCSI_OP_IMMEDIATE)) { pr_err("NOPOUT ITT is reserved, but Immediate Bit is" " not set, protocol error.\n"); @@ -2468,6 +2497,7 @@ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn) { struct iscsi_cmd *cmd; struct iscsi_conn *conn_p; + bool found = false; /* * Only send a Asynchronous Message on connections whos network @@ -2476,11 +2506,12 @@ static void iscsit_build_conn_drop_async_message(struct iscsi_conn *conn) list_for_each_entry(conn_p, &conn->sess->sess_conn_list, conn_list) { if (conn_p->conn_state == TARG_CONN_STATE_LOGGED_IN) { iscsit_inc_conn_usage_count(conn_p); + found = true; break; } } - if (!conn_p) + if (!found) return; cmd = iscsit_allocate_cmd(conn_p, TASK_RUNNING); diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index 1c0088fe9e99..ae03f3e5de1e 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -1052,6 +1052,11 @@ TPG_ATTR(demo_mode_discovery, S_IRUGO | S_IWUSR); */ DEF_TPG_ATTRIB(default_erl); TPG_ATTR(default_erl, S_IRUGO | S_IWUSR); +/* + * Define iscsi_tpg_attrib_s_t10_pi + */ +DEF_TPG_ATTRIB(t10_pi); +TPG_ATTR(t10_pi, S_IRUGO | S_IWUSR); static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_authentication.attr, @@ -1064,6 +1069,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { &iscsi_tpg_attrib_prod_mode_write_protect.attr, &iscsi_tpg_attrib_demo_mode_discovery.attr, &iscsi_tpg_attrib_default_erl.attr, + &iscsi_tpg_attrib_t10_pi.attr, NULL, }; @@ -1815,6 +1821,13 @@ static void lio_queue_tm_rsp(struct se_cmd *se_cmd) iscsit_add_cmd_to_response_queue(cmd, cmd->conn, cmd->i_state); } +static void lio_aborted_task(struct se_cmd *se_cmd) +{ + struct iscsi_cmd *cmd = container_of(se_cmd, struct iscsi_cmd, se_cmd); + + cmd->conn->conn_transport->iscsit_aborted_task(cmd->conn, cmd); +} + static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg) { struct iscsi_portal_group *tpg = se_tpg->se_tpg_fabric_ptr; @@ -1999,6 +2012,7 @@ int iscsi_target_register_configfs(void) fabric->tf_ops.queue_data_in = &lio_queue_data_in; fabric->tf_ops.queue_status = &lio_queue_status; fabric->tf_ops.queue_tm_rsp = &lio_queue_tm_rsp; + fabric->tf_ops.aborted_task = &lio_aborted_task; /* * Setup function pointers for generic logic in target_core_fabric_configfs.c */ diff --git a/drivers/target/iscsi/iscsi_target_core.h b/drivers/target/iscsi/iscsi_target_core.h index 48f7b3bf4e8c..886d74d6f3d4 100644 --- a/drivers/target/iscsi/iscsi_target_core.h +++ b/drivers/target/iscsi/iscsi_target_core.h @@ -58,7 +58,8 @@ #define TA_DEMO_MODE_DISCOVERY 1 #define TA_DEFAULT_ERL 0 #define TA_CACHE_CORE_NPS 0 - +/* T10 protection information disabled by default */ +#define TA_DEFAULT_T10_PI 0 #define ISCSI_IOV_DATA_BUFFER 5 @@ -765,6 +766,7 @@ struct iscsi_tpg_attrib { u32 prod_mode_write_protect; u32 demo_mode_discovery; u32 default_erl; + u8 t10_pi; struct iscsi_portal_group *tpg; }; @@ -787,6 +789,7 @@ struct iscsi_np { void *np_context; struct iscsit_transport *np_transport; struct list_head np_list; + struct iscsi_tpg_np *tpg_np; } ____cacheline_aligned; struct iscsi_tpg_np { diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index e29279e6b577..8739b98f6f93 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -259,6 +259,7 @@ static int iscsi_login_zero_tsih_s1( { struct iscsi_session *sess = NULL; struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf; + enum target_prot_op sup_pro_ops; int ret; sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL); @@ -320,8 +321,9 @@ static int iscsi_login_zero_tsih_s1( kfree(sess); return -ENOMEM; } + sup_pro_ops = conn->conn_transport->iscsit_get_sup_prot_ops(conn); - sess->se_sess = transport_init_session(); + sess->se_sess = transport_init_session(sup_pro_ops); if (IS_ERR(sess->se_sess)) { iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, ISCSI_LOGIN_STATUS_NO_RESOURCES); diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 44a5471de00f..eb96b20dc09e 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -225,6 +225,7 @@ static void iscsit_set_default_tpg_attribs(struct iscsi_portal_group *tpg) a->prod_mode_write_protect = TA_PROD_MODE_WRITE_PROTECT; a->demo_mode_discovery = TA_DEMO_MODE_DISCOVERY; a->default_erl = TA_DEFAULT_ERL; + a->t10_pi = TA_DEFAULT_T10_PI; } int iscsit_tpg_add_portal_group(struct iscsi_tiqn *tiqn, struct iscsi_portal_group *tpg) @@ -500,6 +501,7 @@ struct iscsi_tpg_np *iscsit_tpg_add_network_portal( init_completion(&tpg_np->tpg_np_comp); kref_init(&tpg_np->tpg_np_kref); tpg_np->tpg_np = np; + np->tpg_np = tpg_np; tpg_np->tpg = tpg; spin_lock(&tpg->tpg_np_lock); @@ -858,3 +860,22 @@ int iscsit_ta_default_erl( return 0; } + +int iscsit_ta_t10_pi( + struct iscsi_portal_group *tpg, + u32 flag) +{ + struct iscsi_tpg_attrib *a = &tpg->tpg_attrib; + + if ((flag != 0) && (flag != 1)) { + pr_err("Illegal value %d\n", flag); + return -EINVAL; + } + + a->t10_pi = flag; + pr_debug("iSCSI_TPG[%hu] - T10 Protection information bit:" + " %s\n", tpg->tpgt, (a->t10_pi) ? + "ON" : "OFF"); + + return 0; +} diff --git a/drivers/target/iscsi/iscsi_target_tpg.h b/drivers/target/iscsi/iscsi_target_tpg.h index 213c0fc7fdc9..0a182f2aa8a2 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.h +++ b/drivers/target/iscsi/iscsi_target_tpg.h @@ -39,5 +39,6 @@ extern int iscsit_ta_demo_mode_write_protect(struct iscsi_portal_group *, u32); extern int iscsit_ta_prod_mode_write_protect(struct iscsi_portal_group *, u32); extern int iscsit_ta_demo_mode_discovery(struct iscsi_portal_group *, u32); extern int iscsit_ta_default_erl(struct iscsi_portal_group *, u32); +extern int iscsit_ta_t10_pi(struct iscsi_portal_group *, u32); #endif /* ISCSI_TARGET_TPG_H */ diff --git a/drivers/target/iscsi/iscsi_target_util.c b/drivers/target/iscsi/iscsi_target_util.c index e655b042ed18..53e157cb8c54 100644 --- a/drivers/target/iscsi/iscsi_target_util.c +++ b/drivers/target/iscsi/iscsi_target_util.c @@ -705,8 +705,8 @@ void iscsit_release_cmd(struct iscsi_cmd *cmd) } EXPORT_SYMBOL(iscsit_release_cmd); -static void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool scsi_cmd, - bool check_queues) +void __iscsit_free_cmd(struct iscsi_cmd *cmd, bool scsi_cmd, + bool check_queues) { struct iscsi_conn *conn = cmd->conn; diff --git a/drivers/target/iscsi/iscsi_target_util.h b/drivers/target/iscsi/iscsi_target_util.h index 561a424d1980..a68508c4fec8 100644 --- a/drivers/target/iscsi/iscsi_target_util.h +++ b/drivers/target/iscsi/iscsi_target_util.h @@ -30,6 +30,7 @@ extern void iscsit_remove_cmd_from_tx_queues(struct iscsi_cmd *, struct iscsi_co extern bool iscsit_conn_all_queues_empty(struct iscsi_conn *); extern void iscsit_free_queue_reqs_for_conn(struct iscsi_conn *); extern void iscsit_release_cmd(struct iscsi_cmd *); +extern void __iscsit_free_cmd(struct iscsi_cmd *, bool, bool); extern void iscsit_free_cmd(struct iscsi_cmd *, bool); extern int iscsit_check_session_usage_count(struct iscsi_session *); extern void iscsit_dec_session_usage_count(struct iscsi_session *); diff --git a/drivers/target/loopback/tcm_loop.c b/drivers/target/loopback/tcm_loop.c index fadad7c5f635..c886ad1c39fb 100644 --- a/drivers/target/loopback/tcm_loop.c +++ b/drivers/target/loopback/tcm_loop.c @@ -212,6 +212,10 @@ static void tcm_loop_submission_work(struct work_struct *work) se_cmd->se_cmd_flags |= SCF_BIDI; } + + if (!scsi_prot_sg_count(sc) && scsi_get_prot_op(sc) != SCSI_PROT_NORMAL) + se_cmd->prot_pto = true; + rc = target_submit_cmd_map_sgls(se_cmd, tl_nexus->se_sess, sc->cmnd, &tl_cmd->tl_sense_buf[0], tl_cmd->sc->device->lun, scsi_bufflen(sc), tcm_loop_sam_attr(sc), @@ -915,6 +919,11 @@ static void tcm_loop_queue_tm_rsp(struct se_cmd *se_cmd) wake_up(&tl_tmr->tl_tmr_wait); } +static void tcm_loop_aborted_task(struct se_cmd *se_cmd) +{ + return; +} + static char *tcm_loop_dump_proto_id(struct tcm_loop_hba *tl_hba) { switch (tl_hba->tl_proto_id) { @@ -1009,7 +1018,7 @@ static int tcm_loop_make_nexus( /* * Initialize the struct se_session pointer */ - tl_nexus->se_sess = transport_init_session(); + tl_nexus->se_sess = transport_init_session(TARGET_PROT_ALL); if (IS_ERR(tl_nexus->se_sess)) { ret = PTR_ERR(tl_nexus->se_sess); goto out; @@ -1483,6 +1492,7 @@ static int tcm_loop_register_configfs(void) fabric->tf_ops.queue_data_in = &tcm_loop_queue_data_in; fabric->tf_ops.queue_status = &tcm_loop_queue_status; fabric->tf_ops.queue_tm_rsp = &tcm_loop_queue_tm_rsp; + fabric->tf_ops.aborted_task = &tcm_loop_aborted_task; /* * Setup function pointers for generic logic in target_core_fabric_configfs.c diff --git a/drivers/target/sbp/sbp_target.c b/drivers/target/sbp/sbp_target.c index 24884cac19ce..e7e93727553c 100644 --- a/drivers/target/sbp/sbp_target.c +++ b/drivers/target/sbp/sbp_target.c @@ -210,7 +210,7 @@ static struct sbp_session *sbp_session_create( return ERR_PTR(-ENOMEM); } - sess->se_sess = transport_init_session(); + sess->se_sess = transport_init_session(TARGET_PROT_NORMAL); if (IS_ERR(sess->se_sess)) { pr_err("failed to init se_session\n"); @@ -1846,6 +1846,11 @@ static void sbp_queue_tm_rsp(struct se_cmd *se_cmd) { } +static void sbp_aborted_task(struct se_cmd *se_cmd) +{ + return; +} + static int sbp_check_stop_free(struct se_cmd *se_cmd) { struct sbp_target_request *req = container_of(se_cmd, @@ -2526,6 +2531,7 @@ static struct target_core_fabric_ops sbp_ops = { .queue_data_in = sbp_queue_data_in, .queue_status = sbp_queue_status, .queue_tm_rsp = sbp_queue_tm_rsp, + .aborted_task = sbp_aborted_task, .check_stop_free = sbp_check_stop_free, .fabric_make_wwn = sbp_make_tport, diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index c3d9df6aaf5f..fcbe6125b73e 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -455,11 +455,26 @@ out: return rc; } -static inline int core_alua_state_nonoptimized( +static inline void set_ascq(struct se_cmd *cmd, u8 alua_ascq) +{ + /* + * Set SCSI additional sense code (ASC) to 'LUN Not Accessible'; + * The ALUA additional sense code qualifier (ASCQ) is determined + * by the ALUA primary or secondary access state.. + */ + pr_debug("[%s]: ALUA TG Port not available, " + "SenseKey: NOT_READY, ASC/ASCQ: " + "0x04/0x%02x\n", + cmd->se_tfo->get_fabric_name(), alua_ascq); + + cmd->scsi_asc = 0x04; + cmd->scsi_ascq = alua_ascq; +} + +static inline void core_alua_state_nonoptimized( struct se_cmd *cmd, unsigned char *cdb, - int nonop_delay_msecs, - u8 *alua_ascq) + int nonop_delay_msecs) { /* * Set SCF_ALUA_NON_OPTIMIZED here, this value will be checked @@ -468,13 +483,11 @@ static inline int core_alua_state_nonoptimized( */ cmd->se_cmd_flags |= SCF_ALUA_NON_OPTIMIZED; cmd->alua_nonop_delay = nonop_delay_msecs; - return 0; } static inline int core_alua_state_lba_dependent( struct se_cmd *cmd, - struct t10_alua_tg_pt_gp *tg_pt_gp, - u8 *alua_ascq) + struct t10_alua_tg_pt_gp *tg_pt_gp) { struct se_device *dev = cmd->se_dev; u64 segment_size, segment_mult, sectors, lba; @@ -520,7 +533,7 @@ static inline int core_alua_state_lba_dependent( } if (!cur_map) { spin_unlock(&dev->t10_alua.lba_map_lock); - *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE); return 1; } list_for_each_entry(map_mem, &cur_map->lba_map_mem_list, @@ -531,11 +544,11 @@ static inline int core_alua_state_lba_dependent( switch(map_mem->lba_map_mem_alua_state) { case ALUA_ACCESS_STATE_STANDBY: spin_unlock(&dev->t10_alua.lba_map_lock); - *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY); return 1; case ALUA_ACCESS_STATE_UNAVAILABLE: spin_unlock(&dev->t10_alua.lba_map_lock); - *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE); return 1; default: break; @@ -548,8 +561,7 @@ static inline int core_alua_state_lba_dependent( static inline int core_alua_state_standby( struct se_cmd *cmd, - unsigned char *cdb, - u8 *alua_ascq) + unsigned char *cdb) { /* * Allowed CDBs for ALUA_ACCESS_STATE_STANDBY as defined by @@ -570,7 +582,7 @@ static inline int core_alua_state_standby( case MI_REPORT_TARGET_PGS: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY); return 1; } case MAINTENANCE_OUT: @@ -578,7 +590,7 @@ static inline int core_alua_state_standby( case MO_SET_TARGET_PGS: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY); return 1; } case REQUEST_SENSE: @@ -588,7 +600,7 @@ static inline int core_alua_state_standby( case WRITE_BUFFER: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_TG_PT_STANDBY; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_STANDBY); return 1; } @@ -597,8 +609,7 @@ static inline int core_alua_state_standby( static inline int core_alua_state_unavailable( struct se_cmd *cmd, - unsigned char *cdb, - u8 *alua_ascq) + unsigned char *cdb) { /* * Allowed CDBs for ALUA_ACCESS_STATE_UNAVAILABLE as defined by @@ -613,7 +624,7 @@ static inline int core_alua_state_unavailable( case MI_REPORT_TARGET_PGS: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE); return 1; } case MAINTENANCE_OUT: @@ -621,7 +632,7 @@ static inline int core_alua_state_unavailable( case MO_SET_TARGET_PGS: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE); return 1; } case REQUEST_SENSE: @@ -629,7 +640,7 @@ static inline int core_alua_state_unavailable( case WRITE_BUFFER: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_TG_PT_UNAVAILABLE; + set_ascq(cmd, ASCQ_04H_ALUA_TG_PT_UNAVAILABLE); return 1; } @@ -638,8 +649,7 @@ static inline int core_alua_state_unavailable( static inline int core_alua_state_transition( struct se_cmd *cmd, - unsigned char *cdb, - u8 *alua_ascq) + unsigned char *cdb) { /* * Allowed CDBs for ALUA_ACCESS_STATE_TRANSITION as defined by @@ -654,7 +664,7 @@ static inline int core_alua_state_transition( case MI_REPORT_TARGET_PGS: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION; + set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION); return 1; } case REQUEST_SENSE: @@ -662,7 +672,7 @@ static inline int core_alua_state_transition( case WRITE_BUFFER: return 0; default: - *alua_ascq = ASCQ_04H_ALUA_STATE_TRANSITION; + set_ascq(cmd, ASCQ_04H_ALUA_STATE_TRANSITION); return 1; } @@ -684,8 +694,6 @@ target_alua_state_check(struct se_cmd *cmd) struct t10_alua_tg_pt_gp *tg_pt_gp; struct t10_alua_tg_pt_gp_member *tg_pt_gp_mem; int out_alua_state, nonop_delay_msecs; - u8 alua_ascq; - int ret; if (dev->se_hba->hba_flags & HBA_FLAGS_INTERNAL_USE) return 0; @@ -701,9 +709,8 @@ target_alua_state_check(struct se_cmd *cmd) if (atomic_read(&port->sep_tg_pt_secondary_offline)) { pr_debug("ALUA: Got secondary offline status for local" " target port\n"); - alua_ascq = ASCQ_04H_ALUA_OFFLINE; - ret = 1; - goto out; + set_ascq(cmd, ASCQ_04H_ALUA_OFFLINE); + return TCM_CHECK_CONDITION_NOT_READY; } /* * Second, obtain the struct t10_alua_tg_pt_gp_member pointer to the @@ -731,20 +738,23 @@ target_alua_state_check(struct se_cmd *cmd) switch (out_alua_state) { case ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED: - ret = core_alua_state_nonoptimized(cmd, cdb, - nonop_delay_msecs, &alua_ascq); + core_alua_state_nonoptimized(cmd, cdb, nonop_delay_msecs); break; case ALUA_ACCESS_STATE_STANDBY: - ret = core_alua_state_standby(cmd, cdb, &alua_ascq); + if (core_alua_state_standby(cmd, cdb)) + return TCM_CHECK_CONDITION_NOT_READY; break; case ALUA_ACCESS_STATE_UNAVAILABLE: - ret = core_alua_state_unavailable(cmd, cdb, &alua_ascq); + if (core_alua_state_unavailable(cmd, cdb)) + return TCM_CHECK_CONDITION_NOT_READY; break; case ALUA_ACCESS_STATE_TRANSITION: - ret = core_alua_state_transition(cmd, cdb, &alua_ascq); + if (core_alua_state_transition(cmd, cdb)) + return TCM_CHECK_CONDITION_NOT_READY; break; case ALUA_ACCESS_STATE_LBA_DEPENDENT: - ret = core_alua_state_lba_dependent(cmd, tg_pt_gp, &alua_ascq); + if (core_alua_state_lba_dependent(cmd, tg_pt_gp)) + return TCM_CHECK_CONDITION_NOT_READY; break; /* * OFFLINE is a secondary ALUA target port group access state, that is @@ -757,23 +767,6 @@ target_alua_state_check(struct se_cmd *cmd) return TCM_INVALID_CDB_FIELD; } -out: - if (ret > 0) { - /* - * Set SCSI additional sense code (ASC) to 'LUN Not Accessible'; - * The ALUA additional sense code qualifier (ASCQ) is determined - * by the ALUA primary or secondary access state.. - */ - pr_debug("[%s]: ALUA TG Port not available, " - "SenseKey: NOT_READY, ASC/ASCQ: " - "0x04/0x%02x\n", - cmd->se_tfo->get_fabric_name(), alua_ascq); - - cmd->scsi_asc = 0x04; - cmd->scsi_ascq = alua_ascq; - return TCM_CHECK_CONDITION_NOT_READY; - } - return 0; } diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index f0e85b119692..60a9ae6df763 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -457,6 +457,10 @@ static int target_fabric_tf_ops_check( pr_err("Missing tfo->queue_tm_rsp()\n"); return -EINVAL; } + if (!tfo->aborted_task) { + pr_err("Missing tfo->aborted_task()\n"); + return -EINVAL; + } /* * We at least require tfo->fabric_make_wwn(), tfo->fabric_drop_wwn() * tfo->fabric_make_tpg() and tfo->fabric_drop_tpg() in diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index cf991a91a8a9..7d6cddaec525 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -854,25 +854,6 @@ static int fd_init_prot(struct se_device *dev) return 0; } -static void fd_init_format_buf(struct se_device *dev, unsigned char *buf, - u32 unit_size, u32 *ref_tag, u16 app_tag, - bool inc_reftag) -{ - unsigned char *p = buf; - int i; - - for (i = 0; i < unit_size; i += dev->prot_length) { - *((u16 *)&p[0]) = 0xffff; - *((__be16 *)&p[2]) = cpu_to_be16(app_tag); - *((__be32 *)&p[4]) = cpu_to_be32(*ref_tag); - - if (inc_reftag) - (*ref_tag)++; - - p += dev->prot_length; - } -} - static int fd_format_prot(struct se_device *dev) { struct fd_dev *fd_dev = FD_DEV(dev); @@ -880,10 +861,8 @@ static int fd_format_prot(struct se_device *dev) sector_t prot_length, prot; unsigned char *buf; loff_t pos = 0; - u32 ref_tag = 0; int unit_size = FDBD_FORMAT_UNIT_SIZE * dev->dev_attrib.block_size; int rc, ret = 0, size, len; - bool inc_reftag = false; if (!dev->dev_attrib.pi_prot_type) { pr_err("Unable to format_prot while pi_prot_type == 0\n"); @@ -894,37 +873,20 @@ static int fd_format_prot(struct se_device *dev) return -ENODEV; } - switch (dev->dev_attrib.pi_prot_type) { - case TARGET_DIF_TYPE3_PROT: - ref_tag = 0xffffffff; - break; - case TARGET_DIF_TYPE2_PROT: - case TARGET_DIF_TYPE1_PROT: - inc_reftag = true; - break; - default: - break; - } - buf = vzalloc(unit_size); if (!buf) { pr_err("Unable to allocate FILEIO prot buf\n"); return -ENOMEM; } - prot_length = (dev->transport->get_blocks(dev) + 1) * dev->prot_length; size = prot_length; pr_debug("Using FILEIO prot_length: %llu\n", (unsigned long long)prot_length); + memset(buf, 0xff, unit_size); for (prot = 0; prot < prot_length; prot += unit_size) { - - fd_init_format_buf(dev, buf, unit_size, &ref_tag, 0xffff, - inc_reftag); - len = min(unit_size, size); - rc = kernel_write(prot_fd, buf, len, pos); if (rc != len) { pr_err("vfs_write to prot file failed: %d\n", rc); diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 554d4f75a75a..9e0232cca92e 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -203,10 +203,9 @@ static void iblock_free_device(struct se_device *dev) if (ib_dev->ibd_bd != NULL) blkdev_put(ib_dev->ibd_bd, FMODE_WRITE|FMODE_READ|FMODE_EXCL); - if (ib_dev->ibd_bio_set != NULL) { - bioset_integrity_free(ib_dev->ibd_bio_set); + if (ib_dev->ibd_bio_set != NULL) bioset_free(ib_dev->ibd_bio_set); - } + kfree(ib_dev); } diff --git a/drivers/target/target_core_rd.c b/drivers/target/target_core_rd.c index 66a5aba5a0d9..b920db3388cd 100644 --- a/drivers/target/target_core_rd.c +++ b/drivers/target/target_core_rd.c @@ -242,7 +242,7 @@ static void rd_release_prot_space(struct rd_dev *rd_dev) rd_dev->sg_prot_count = 0; } -static int rd_build_prot_space(struct rd_dev *rd_dev, int prot_length) +static int rd_build_prot_space(struct rd_dev *rd_dev, int prot_length, int block_size) { struct rd_dev_sg_table *sg_table; u32 total_sg_needed, sg_tables; @@ -252,8 +252,13 @@ static int rd_build_prot_space(struct rd_dev *rd_dev, int prot_length) if (rd_dev->rd_flags & RDF_NULLIO) return 0; - - total_sg_needed = rd_dev->rd_page_count / prot_length; + /* + * prot_length=8byte dif data + * tot sg needed = rd_page_count * (PGSZ/block_size) * + * (prot_length/block_size) + pad + * PGSZ canceled each other. + */ + total_sg_needed = (rd_dev->rd_page_count * prot_length / block_size) + 1; sg_tables = (total_sg_needed / max_sg_per_table) + 1; @@ -606,7 +611,8 @@ static int rd_init_prot(struct se_device *dev) if (!dev->dev_attrib.pi_prot_type) return 0; - return rd_build_prot_space(rd_dev, dev->prot_length); + return rd_build_prot_space(rd_dev, dev->prot_length, + dev->dev_attrib.block_size); } static void rd_free_prot(struct se_device *dev) diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index 77e6531fb0a1..e0229592ec55 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -89,6 +89,7 @@ static sense_reason_t sbc_emulate_readcapacity_16(struct se_cmd *cmd) { struct se_device *dev = cmd->se_dev; + struct se_session *sess = cmd->se_sess; unsigned char *rbuf; unsigned char buf[32]; unsigned long long blocks = dev->transport->get_blocks(dev); @@ -109,8 +110,10 @@ sbc_emulate_readcapacity_16(struct se_cmd *cmd) /* * Set P_TYPE and PROT_EN bits for DIF support */ - if (dev->dev_attrib.pi_prot_type) - buf[12] = (dev->dev_attrib.pi_prot_type - 1) << 1 | 0x1; + if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { + if (dev->dev_attrib.pi_prot_type) + buf[12] = (dev->dev_attrib.pi_prot_type - 1) << 1 | 0x1; + } if (dev->transport->get_lbppbe) buf[13] = dev->transport->get_lbppbe(dev) & 0x0f; @@ -425,13 +428,14 @@ static sense_reason_t compare_and_write_callback(struct se_cmd *cmd) goto out; } - write_sg = kzalloc(sizeof(struct scatterlist) * cmd->t_data_nents, + write_sg = kmalloc(sizeof(struct scatterlist) * cmd->t_data_nents, GFP_KERNEL); if (!write_sg) { pr_err("Unable to allocate compare_and_write sg\n"); ret = TCM_OUT_OF_RESOURCES; goto out; } + sg_init_table(write_sg, cmd->t_data_nents); /* * Setup verify and write data payloads from total NumberLBAs. */ @@ -569,30 +573,85 @@ sbc_compare_and_write(struct se_cmd *cmd) return TCM_NO_SENSE; } +static int +sbc_set_prot_op_checks(u8 protect, enum target_prot_type prot_type, + bool is_write, struct se_cmd *cmd) +{ + if (is_write) { + cmd->prot_op = protect ? TARGET_PROT_DOUT_PASS : + TARGET_PROT_DOUT_INSERT; + switch (protect) { + case 0x0: + case 0x3: + cmd->prot_checks = 0; + break; + case 0x1: + case 0x5: + cmd->prot_checks = TARGET_DIF_CHECK_GUARD; + if (prot_type == TARGET_DIF_TYPE1_PROT) + cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG; + break; + case 0x2: + if (prot_type == TARGET_DIF_TYPE1_PROT) + cmd->prot_checks = TARGET_DIF_CHECK_REFTAG; + break; + case 0x4: + cmd->prot_checks = TARGET_DIF_CHECK_GUARD; + break; + default: + pr_err("Unsupported protect field %d\n", protect); + return -EINVAL; + } + } else { + cmd->prot_op = protect ? TARGET_PROT_DIN_PASS : + TARGET_PROT_DIN_STRIP; + switch (protect) { + case 0x0: + case 0x1: + case 0x5: + cmd->prot_checks = TARGET_DIF_CHECK_GUARD; + if (prot_type == TARGET_DIF_TYPE1_PROT) + cmd->prot_checks |= TARGET_DIF_CHECK_REFTAG; + break; + case 0x2: + if (prot_type == TARGET_DIF_TYPE1_PROT) + cmd->prot_checks = TARGET_DIF_CHECK_REFTAG; + break; + case 0x3: + cmd->prot_checks = 0; + break; + case 0x4: + cmd->prot_checks = TARGET_DIF_CHECK_GUARD; + break; + default: + pr_err("Unsupported protect field %d\n", protect); + return -EINVAL; + } + } + + return 0; +} + static bool sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb, - u32 sectors) + u32 sectors, bool is_write) { - if (!cmd->t_prot_sg || !cmd->t_prot_nents) + u8 protect = cdb[1] >> 5; + + if ((!cmd->t_prot_sg || !cmd->t_prot_nents) && cmd->prot_pto) return true; switch (dev->dev_attrib.pi_prot_type) { case TARGET_DIF_TYPE3_PROT: - if (!(cdb[1] & 0xe0)) - return true; - cmd->reftag_seed = 0xffffffff; break; case TARGET_DIF_TYPE2_PROT: - if (cdb[1] & 0xe0) + if (protect) return false; cmd->reftag_seed = cmd->t_task_lba; break; case TARGET_DIF_TYPE1_PROT: - if (!(cdb[1] & 0xe0)) - return true; - cmd->reftag_seed = cmd->t_task_lba; break; case TARGET_DIF_TYPE0_PROT: @@ -600,9 +659,15 @@ sbc_check_prot(struct se_device *dev, struct se_cmd *cmd, unsigned char *cdb, return true; } + if (sbc_set_prot_op_checks(protect, dev->dev_attrib.pi_prot_type, + is_write, cmd)) + return false; + cmd->prot_type = dev->dev_attrib.pi_prot_type; cmd->prot_length = dev->prot_length * sectors; - cmd->prot_handover = PROT_SEPERATED; + pr_debug("%s: prot_type=%d, prot_length=%d prot_op=%d prot_checks=%d\n", + __func__, cmd->prot_type, cmd->prot_length, + cmd->prot_op, cmd->prot_checks); return true; } @@ -628,7 +693,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) sectors = transport_get_sectors_10(cdb); cmd->t_task_lba = transport_lba_32(cdb); - if (!sbc_check_prot(dev, cmd, cdb, sectors)) + if (!sbc_check_prot(dev, cmd, cdb, sectors, false)) return TCM_UNSUPPORTED_SCSI_OPCODE; cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; @@ -639,7 +704,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) sectors = transport_get_sectors_12(cdb); cmd->t_task_lba = transport_lba_32(cdb); - if (!sbc_check_prot(dev, cmd, cdb, sectors)) + if (!sbc_check_prot(dev, cmd, cdb, sectors, false)) return TCM_UNSUPPORTED_SCSI_OPCODE; cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; @@ -650,7 +715,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) sectors = transport_get_sectors_16(cdb); cmd->t_task_lba = transport_lba_64(cdb); - if (!sbc_check_prot(dev, cmd, cdb, sectors)) + if (!sbc_check_prot(dev, cmd, cdb, sectors, false)) return TCM_UNSUPPORTED_SCSI_OPCODE; cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; @@ -669,7 +734,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) sectors = transport_get_sectors_10(cdb); cmd->t_task_lba = transport_lba_32(cdb); - if (!sbc_check_prot(dev, cmd, cdb, sectors)) + if (!sbc_check_prot(dev, cmd, cdb, sectors, true)) return TCM_UNSUPPORTED_SCSI_OPCODE; if (cdb[1] & 0x8) @@ -682,7 +747,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) sectors = transport_get_sectors_12(cdb); cmd->t_task_lba = transport_lba_32(cdb); - if (!sbc_check_prot(dev, cmd, cdb, sectors)) + if (!sbc_check_prot(dev, cmd, cdb, sectors, true)) return TCM_UNSUPPORTED_SCSI_OPCODE; if (cdb[1] & 0x8) @@ -695,7 +760,7 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) sectors = transport_get_sectors_16(cdb); cmd->t_task_lba = transport_lba_64(cdb); - if (!sbc_check_prot(dev, cmd, cdb, sectors)) + if (!sbc_check_prot(dev, cmd, cdb, sectors, true)) return TCM_UNSUPPORTED_SCSI_OPCODE; if (cdb[1] & 0x8) @@ -1031,6 +1096,50 @@ err: } EXPORT_SYMBOL(sbc_execute_unmap); +void +sbc_dif_generate(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + struct se_dif_v1_tuple *sdt; + struct scatterlist *dsg, *psg = cmd->t_prot_sg; + sector_t sector = cmd->t_task_lba; + void *daddr, *paddr; + int i, j, offset = 0; + + for_each_sg(cmd->t_data_sg, dsg, cmd->t_data_nents, i) { + daddr = kmap_atomic(sg_page(dsg)) + dsg->offset; + paddr = kmap_atomic(sg_page(psg)) + psg->offset; + + for (j = 0; j < dsg->length; j += dev->dev_attrib.block_size) { + + if (offset >= psg->length) { + kunmap_atomic(paddr); + psg = sg_next(psg); + paddr = kmap_atomic(sg_page(psg)) + psg->offset; + offset = 0; + } + + sdt = paddr + offset; + sdt->guard_tag = cpu_to_be16(crc_t10dif(daddr + j, + dev->dev_attrib.block_size)); + if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT) + sdt->ref_tag = cpu_to_be32(sector & 0xffffffff); + sdt->app_tag = 0; + + pr_debug("DIF WRITE INSERT sector: %llu guard_tag: 0x%04x" + " app_tag: 0x%04x ref_tag: %u\n", + (unsigned long long)sector, sdt->guard_tag, + sdt->app_tag, be32_to_cpu(sdt->ref_tag)); + + sector++; + offset += sizeof(struct se_dif_v1_tuple); + } + + kunmap_atomic(paddr); + kunmap_atomic(daddr); + } +} + static sense_reason_t sbc_dif_v1_verify(struct se_device *dev, struct se_dif_v1_tuple *sdt, const void *p, sector_t sector, unsigned int ei_lba) @@ -1162,9 +1271,9 @@ sbc_dif_verify_write(struct se_cmd *cmd, sector_t start, unsigned int sectors, } EXPORT_SYMBOL(sbc_dif_verify_write); -sense_reason_t -sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors, - unsigned int ei_lba, struct scatterlist *sg, int sg_off) +static sense_reason_t +__sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors, + unsigned int ei_lba, struct scatterlist *sg, int sg_off) { struct se_device *dev = cmd->se_dev; struct se_dif_v1_tuple *sdt; @@ -1217,8 +1326,31 @@ sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors, kunmap_atomic(paddr); kunmap_atomic(daddr); } - sbc_dif_copy_prot(cmd, sectors, true, sg, sg_off); return 0; } + +sense_reason_t +sbc_dif_read_strip(struct se_cmd *cmd) +{ + struct se_device *dev = cmd->se_dev; + u32 sectors = cmd->prot_length / dev->prot_length; + + return __sbc_dif_verify_read(cmd, cmd->t_task_lba, sectors, 0, + cmd->t_prot_sg, 0); +} + +sense_reason_t +sbc_dif_verify_read(struct se_cmd *cmd, sector_t start, unsigned int sectors, + unsigned int ei_lba, struct scatterlist *sg, int sg_off) +{ + sense_reason_t rc; + + rc = __sbc_dif_verify_read(cmd, start, sectors, ei_lba, sg, sg_off); + if (rc) + return rc; + + sbc_dif_copy_prot(cmd, sectors, true, sg, sg_off); + return 0; +} EXPORT_SYMBOL(sbc_dif_verify_read); diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 3bebc71ea033..8653666612a8 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -71,6 +71,7 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf) { struct se_lun *lun = cmd->se_lun; struct se_device *dev = cmd->se_dev; + struct se_session *sess = cmd->se_sess; /* Set RMB (removable media) for tape devices */ if (dev->transport->get_device_type(dev) == TYPE_TAPE) @@ -101,10 +102,13 @@ spc_emulate_inquiry_std(struct se_cmd *cmd, unsigned char *buf) if (dev->dev_attrib.emulate_3pc) buf[5] |= 0x8; /* - * Set Protection (PROTECT) bit when DIF has been enabled. + * Set Protection (PROTECT) bit when DIF has been enabled on the + * device, and the transport supports VERIFY + PASS. */ - if (dev->dev_attrib.pi_prot_type) - buf[5] |= 0x1; + if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { + if (dev->dev_attrib.pi_prot_type) + buf[5] |= 0x1; + } buf[7] = 0x2; /* CmdQue=1 */ @@ -473,16 +477,19 @@ static sense_reason_t spc_emulate_evpd_86(struct se_cmd *cmd, unsigned char *buf) { struct se_device *dev = cmd->se_dev; + struct se_session *sess = cmd->se_sess; buf[3] = 0x3c; /* * Set GRD_CHK + REF_CHK for TYPE1 protection, or GRD_CHK * only for TYPE3 protection. */ - if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT) - buf[4] = 0x5; - else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT) - buf[4] = 0x4; + if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { + if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE1_PROT) + buf[4] = 0x5; + else if (dev->dev_attrib.pi_prot_type == TARGET_DIF_TYPE3_PROT) + buf[4] = 0x4; + } /* Set HEADSUP, ORDSUP, SIMPSUP */ buf[5] = 0x07; @@ -762,7 +769,7 @@ out: return ret; } -static int spc_modesense_rwrecovery(struct se_device *dev, u8 pc, u8 *p) +static int spc_modesense_rwrecovery(struct se_cmd *cmd, u8 pc, u8 *p) { p[0] = 0x01; p[1] = 0x0a; @@ -775,8 +782,11 @@ out: return 12; } -static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p) +static int spc_modesense_control(struct se_cmd *cmd, u8 pc, u8 *p) { + struct se_device *dev = cmd->se_dev; + struct se_session *sess = cmd->se_sess; + p[0] = 0x0a; p[1] = 0x0a; @@ -868,8 +878,10 @@ static int spc_modesense_control(struct se_device *dev, u8 pc, u8 *p) * type, shall not modify the contents of the LOGICAL BLOCK REFERENCE * TAG field. */ - if (dev->dev_attrib.pi_prot_type) - p[5] |= 0x80; + if (sess->sup_prot_ops & (TARGET_PROT_DIN_PASS | TARGET_PROT_DOUT_PASS)) { + if (dev->dev_attrib.pi_prot_type) + p[5] |= 0x80; + } p[8] = 0xff; p[9] = 0xff; @@ -879,8 +891,10 @@ out: return 12; } -static int spc_modesense_caching(struct se_device *dev, u8 pc, u8 *p) +static int spc_modesense_caching(struct se_cmd *cmd, u8 pc, u8 *p) { + struct se_device *dev = cmd->se_dev; + p[0] = 0x08; p[1] = 0x12; @@ -896,7 +910,7 @@ out: return 20; } -static int spc_modesense_informational_exceptions(struct se_device *dev, u8 pc, unsigned char *p) +static int spc_modesense_informational_exceptions(struct se_cmd *cmd, u8 pc, unsigned char *p) { p[0] = 0x1c; p[1] = 0x0a; @@ -912,7 +926,7 @@ out: static struct { uint8_t page; uint8_t subpage; - int (*emulate)(struct se_device *, u8, unsigned char *); + int (*emulate)(struct se_cmd *, u8, unsigned char *); } modesense_handlers[] = { { .page = 0x01, .subpage = 0x00, .emulate = spc_modesense_rwrecovery }, { .page = 0x08, .subpage = 0x00, .emulate = spc_modesense_caching }, @@ -1050,7 +1064,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) * the only two possibilities). */ if ((modesense_handlers[i].subpage & ~subpage) == 0) { - ret = modesense_handlers[i].emulate(dev, pc, &buf[length]); + ret = modesense_handlers[i].emulate(cmd, pc, &buf[length]); if (!ten && length + ret >= 255) break; length += ret; @@ -1063,7 +1077,7 @@ static sense_reason_t spc_emulate_modesense(struct se_cmd *cmd) for (i = 0; i < ARRAY_SIZE(modesense_handlers); ++i) if (modesense_handlers[i].page == page && modesense_handlers[i].subpage == subpage) { - length += modesense_handlers[i].emulate(dev, pc, &buf[length]); + length += modesense_handlers[i].emulate(cmd, pc, &buf[length]); goto set_length; } @@ -1095,7 +1109,6 @@ set_length: static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) { - struct se_device *dev = cmd->se_dev; char *cdb = cmd->t_task_cdb; bool ten = cdb[0] == MODE_SELECT_10; int off = ten ? 8 : 4; @@ -1131,7 +1144,7 @@ static sense_reason_t spc_emulate_modeselect(struct se_cmd *cmd) if (modesense_handlers[i].page == page && modesense_handlers[i].subpage == subpage) { memset(tbuf, 0, SE_MODE_PAGE_BUF); - length = modesense_handlers[i].emulate(dev, 0, tbuf); + length = modesense_handlers[i].emulate(cmd, 0, tbuf); goto check_contents; } diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c index 70c638f730af..f7cd95e8111a 100644 --- a/drivers/target/target_core_tmr.c +++ b/drivers/target/target_core_tmr.c @@ -87,14 +87,17 @@ static void core_tmr_handle_tas_abort( struct se_cmd *cmd, int tas) { + bool remove = true; /* * TASK ABORTED status (TAS) bit support */ if ((tmr_nacl && - (tmr_nacl == cmd->se_sess->se_node_acl)) || tas) + (tmr_nacl != cmd->se_sess->se_node_acl)) && tas) { + remove = false; transport_send_task_abort(cmd); + } - transport_cmd_finish_abort(cmd, 0); + transport_cmd_finish_abort(cmd, remove); } static int target_check_cdb_and_preempt(struct list_head *list, @@ -127,6 +130,11 @@ void core_tmr_abort_task( if (dev != se_cmd->se_dev) continue; + + /* skip se_cmd associated with tmr */ + if (tmr->task_cmd == se_cmd) + continue; + ref_tag = se_cmd->se_tfo->get_task_tag(se_cmd); if (tmr->ref_task_tag != ref_tag) continue; @@ -150,18 +158,9 @@ void core_tmr_abort_task( cancel_work_sync(&se_cmd->work); transport_wait_for_tasks(se_cmd); - /* - * Now send SAM_STAT_TASK_ABORTED status for the referenced - * se_cmd descriptor.. - */ - transport_send_task_abort(se_cmd); - /* - * Also deal with possible extra acknowledge reference.. - */ - if (se_cmd->se_cmd_flags & SCF_ACK_KREF) - target_put_sess_cmd(se_sess, se_cmd); target_put_sess_cmd(se_sess, se_cmd); + transport_cmd_finish_abort(se_cmd, true); printk("ABORT_TASK: Sending TMR_FUNCTION_COMPLETE for" " ref_tag: %d\n", ref_tag); diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 2956250b7225..d4b98690a736 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -235,7 +235,7 @@ void transport_subsystem_check_init(void) sub_api_initialized = 1; } -struct se_session *transport_init_session(void) +struct se_session *transport_init_session(enum target_prot_op sup_prot_ops) { struct se_session *se_sess; @@ -251,6 +251,7 @@ struct se_session *transport_init_session(void) INIT_LIST_HEAD(&se_sess->sess_wait_list); spin_lock_init(&se_sess->sess_cmd_lock); kref_init(&se_sess->sess_kref); + se_sess->sup_prot_ops = sup_prot_ops; return se_sess; } @@ -288,12 +289,13 @@ int transport_alloc_session_tags(struct se_session *se_sess, EXPORT_SYMBOL(transport_alloc_session_tags); struct se_session *transport_init_session_tags(unsigned int tag_num, - unsigned int tag_size) + unsigned int tag_size, + enum target_prot_op sup_prot_ops) { struct se_session *se_sess; int rc; - se_sess = transport_init_session(); + se_sess = transport_init_session(sup_prot_ops); if (IS_ERR(se_sess)) return se_sess; @@ -603,6 +605,15 @@ static void transport_lun_remove_cmd(struct se_cmd *cmd) void transport_cmd_finish_abort(struct se_cmd *cmd, int remove) { + if (cmd->se_cmd_flags & SCF_SE_LUN_CMD) + transport_lun_remove_cmd(cmd); + /* + * Allow the fabric driver to unmap any resources before + * releasing the descriptor via TFO->release_cmd() + */ + if (remove) + cmd->se_tfo->aborted_task(cmd); + if (transport_cmd_check_stop_to_fabric(cmd)) return; if (remove) @@ -1365,6 +1376,13 @@ int target_submit_cmd_map_sgls(struct se_cmd *se_cmd, struct se_session *se_sess target_put_sess_cmd(se_sess, se_cmd); return 0; } + + rc = target_setup_cmd_from_cdb(se_cmd, cdb); + if (rc != 0) { + transport_generic_request_failure(se_cmd, rc); + return 0; + } + /* * Save pointers for SGLs containing protection information, * if present. @@ -1374,11 +1392,6 @@ int target_submit_cmd_map_sgls(struct se_cmd *se_cmd, struct se_session *se_sess se_cmd->t_prot_nents = sgl_prot_count; } - rc = target_setup_cmd_from_cdb(se_cmd, cdb); - if (rc != 0) { - transport_generic_request_failure(se_cmd, rc); - return 0; - } /* * When a non zero sgl_count has been passed perform SGL passthrough * mapping for pre-allocated fabric memory instead of having target @@ -1754,6 +1767,15 @@ void target_execute_cmd(struct se_cmd *cmd) cmd->t_state = TRANSPORT_PROCESSING; cmd->transport_state |= CMD_T_ACTIVE|CMD_T_BUSY|CMD_T_SENT; spin_unlock_irq(&cmd->t_state_lock); + /* + * Perform WRITE_INSERT of PI using software emulation when backend + * device has PI enabled, if the transport has not already generated + * PI using hardware WRITE_INSERT offload. + */ + if (cmd->prot_op == TARGET_PROT_DOUT_INSERT) { + if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DOUT_INSERT)) + sbc_dif_generate(cmd); + } if (target_handle_task_attr(cmd)) { spin_lock_irq(&cmd->t_state_lock); @@ -1883,6 +1905,21 @@ static void transport_handle_queue_full( schedule_work(&cmd->se_dev->qf_work_queue); } +static bool target_check_read_strip(struct se_cmd *cmd) +{ + sense_reason_t rc; + + if (!(cmd->se_sess->sup_prot_ops & TARGET_PROT_DIN_STRIP)) { + rc = sbc_dif_read_strip(cmd); + if (rc) { + cmd->pi_err = rc; + return true; + } + } + + return false; +} + static void target_complete_ok_work(struct work_struct *work) { struct se_cmd *cmd = container_of(work, struct se_cmd, work); @@ -1947,6 +1984,22 @@ static void target_complete_ok_work(struct work_struct *work) cmd->data_length; } spin_unlock(&cmd->se_lun->lun_sep_lock); + /* + * Perform READ_STRIP of PI using software emulation when + * backend had PI enabled, if the transport will not be + * performing hardware READ_STRIP offload. + */ + if (cmd->prot_op == TARGET_PROT_DIN_STRIP && + target_check_read_strip(cmd)) { + ret = transport_send_check_condition_and_sense(cmd, + cmd->pi_err, 0); + if (ret == -EAGAIN || ret == -ENOMEM) + goto queue_full; + + transport_lun_remove_cmd(cmd); + transport_cmd_check_stop_to_fabric(cmd); + return; + } trace_target_cmd_complete(cmd); ret = cmd->se_tfo->queue_data_in(cmd); @@ -2039,6 +2092,10 @@ static inline void transport_free_pages(struct se_cmd *cmd) transport_free_sgl(cmd->t_bidi_data_sg, cmd->t_bidi_data_nents); cmd->t_bidi_data_sg = NULL; cmd->t_bidi_data_nents = 0; + + transport_free_sgl(cmd->t_prot_sg, cmd->t_prot_nents); + cmd->t_prot_sg = NULL; + cmd->t_prot_nents = 0; } /** @@ -2202,6 +2259,14 @@ transport_generic_new_cmd(struct se_cmd *cmd) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; } + if (cmd->prot_op != TARGET_PROT_NORMAL) { + ret = target_alloc_sgl(&cmd->t_prot_sg, + &cmd->t_prot_nents, + cmd->prot_length, true); + if (ret < 0) + return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; + } + ret = target_alloc_sgl(&cmd->t_data_sg, &cmd->t_data_nents, cmd->data_length, zero_flag); if (ret < 0) @@ -2770,13 +2835,17 @@ int transport_check_aborted_status(struct se_cmd *cmd, int send_status) if (!(cmd->transport_state & CMD_T_ABORTED)) return 0; - if (!send_status || (cmd->se_cmd_flags & SCF_SENT_DELAYED_TAS)) + /* + * If cmd has been aborted but either no status is to be sent or it has + * already been sent, just return + */ + if (!send_status || !(cmd->se_cmd_flags & SCF_SEND_DELAYED_TAS)) return 1; pr_debug("Sending delayed SAM_STAT_TASK_ABORTED status for CDB: 0x%02x ITT: 0x%08x\n", cmd->t_task_cdb[0], cmd->se_tfo->get_task_tag(cmd)); - cmd->se_cmd_flags |= SCF_SENT_DELAYED_TAS; + cmd->se_cmd_flags &= ~SCF_SEND_DELAYED_TAS; cmd->scsi_status = SAM_STAT_TASK_ABORTED; trace_target_cmd_complete(cmd); cmd->se_tfo->queue_status(cmd); @@ -2790,7 +2859,7 @@ void transport_send_task_abort(struct se_cmd *cmd) unsigned long flags; spin_lock_irqsave(&cmd->t_state_lock, flags); - if (cmd->se_cmd_flags & (SCF_SENT_CHECK_CONDITION | SCF_SENT_DELAYED_TAS)) { + if (cmd->se_cmd_flags & (SCF_SENT_CHECK_CONDITION)) { spin_unlock_irqrestore(&cmd->t_state_lock, flags); return; } @@ -2805,6 +2874,7 @@ void transport_send_task_abort(struct se_cmd *cmd) if (cmd->data_direction == DMA_TO_DEVICE) { if (cmd->se_tfo->write_pending_status(cmd) != 0) { cmd->transport_state |= CMD_T_ABORTED; + cmd->se_cmd_flags |= SCF_SEND_DELAYED_TAS; smp_mb__after_atomic_inc(); return; } diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index 752863acecb8..a0bcfd3e7e7d 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -94,20 +94,19 @@ struct ft_lun { */ struct ft_tpg { u32 index; - struct ft_lport_acl *lport_acl; + struct ft_lport_wwn *lport_wwn; struct ft_tport *tport; /* active tport or NULL */ - struct list_head list; /* linkage in ft_lport_acl tpg_list */ struct list_head lun_list; /* head of LUNs */ struct se_portal_group se_tpg; struct workqueue_struct *workqueue; }; -struct ft_lport_acl { +struct ft_lport_wwn { u64 wwpn; char name[FT_NAMELEN]; - struct list_head list; - struct list_head tpg_list; - struct se_wwn fc_lport_wwn; + struct list_head ft_wwn_node; + struct ft_tpg *tpg; + struct se_wwn se_wwn; }; /* @@ -128,7 +127,6 @@ struct ft_cmd { u32 sg_cnt; /* No. of item in scatterlist */ }; -extern struct list_head ft_lport_list; extern struct mutex ft_lport_lock; extern struct fc4_prov ft_prov; extern struct target_fabric_configfs *ft_configfs; @@ -163,6 +161,7 @@ int ft_write_pending_status(struct se_cmd *); u32 ft_get_task_tag(struct se_cmd *); int ft_get_cmd_state(struct se_cmd *); void ft_queue_tm_resp(struct se_cmd *); +void ft_aborted_task(struct se_cmd *); /* * other internal functions. diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 8b2c1aaf81de..01cf37f212c3 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -426,6 +426,11 @@ void ft_queue_tm_resp(struct se_cmd *se_cmd) ft_send_resp_code(cmd, code); } +void ft_aborted_task(struct se_cmd *se_cmd) +{ + return; +} + static void ft_send_work(struct work_struct *work); /* diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index e879da81ad93..efdcb9663a1a 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -50,7 +50,7 @@ struct target_fabric_configfs *ft_configfs; -LIST_HEAD(ft_lport_list); +static LIST_HEAD(ft_wwn_list); DEFINE_MUTEX(ft_lport_lock); unsigned int ft_debug_logging; @@ -298,7 +298,7 @@ static struct se_portal_group *ft_add_tpg( struct config_group *group, const char *name) { - struct ft_lport_acl *lacl; + struct ft_lport_wwn *ft_wwn; struct ft_tpg *tpg; struct workqueue_struct *wq; unsigned long index; @@ -318,12 +318,17 @@ static struct se_portal_group *ft_add_tpg( if (index > UINT_MAX) return NULL; - lacl = container_of(wwn, struct ft_lport_acl, fc_lport_wwn); + if ((index != 1)) { + pr_err("Error, a single TPG=1 is used for HW port mappings\n"); + return ERR_PTR(-ENOSYS); + } + + ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn); tpg = kzalloc(sizeof(*tpg), GFP_KERNEL); if (!tpg) return NULL; tpg->index = index; - tpg->lport_acl = lacl; + tpg->lport_wwn = ft_wwn; INIT_LIST_HEAD(&tpg->lun_list); wq = alloc_workqueue("tcm_fc", 0, 1); @@ -342,7 +347,7 @@ static struct se_portal_group *ft_add_tpg( tpg->workqueue = wq; mutex_lock(&ft_lport_lock); - list_add_tail(&tpg->list, &lacl->tpg_list); + ft_wwn->tpg = tpg; mutex_unlock(&ft_lport_lock); return &tpg->se_tpg; @@ -351,6 +356,7 @@ static struct se_portal_group *ft_add_tpg( static void ft_del_tpg(struct se_portal_group *se_tpg) { struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg); + struct ft_lport_wwn *ft_wwn = tpg->lport_wwn; pr_debug("del tpg %s\n", config_item_name(&tpg->se_tpg.tpg_group.cg_item)); @@ -361,7 +367,7 @@ static void ft_del_tpg(struct se_portal_group *se_tpg) synchronize_rcu(); mutex_lock(&ft_lport_lock); - list_del(&tpg->list); + ft_wwn->tpg = NULL; if (tpg->tport) { tpg->tport->tpg = NULL; tpg->tport = NULL; @@ -380,15 +386,11 @@ static void ft_del_tpg(struct se_portal_group *se_tpg) */ struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport) { - struct ft_lport_acl *lacl; - struct ft_tpg *tpg; + struct ft_lport_wwn *ft_wwn; - list_for_each_entry(lacl, &ft_lport_list, list) { - if (lacl->wwpn == lport->wwpn) { - list_for_each_entry(tpg, &lacl->tpg_list, list) - return tpg; /* XXX for now return first entry */ - return NULL; - } + list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) { + if (ft_wwn->wwpn == lport->wwpn) + return ft_wwn->tpg; } return NULL; } @@ -401,50 +403,49 @@ struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport) * Add lport to allowed config. * The name is the WWPN in lower-case ASCII, colon-separated bytes. */ -static struct se_wwn *ft_add_lport( +static struct se_wwn *ft_add_wwn( struct target_fabric_configfs *tf, struct config_group *group, const char *name) { - struct ft_lport_acl *lacl; - struct ft_lport_acl *old_lacl; + struct ft_lport_wwn *ft_wwn; + struct ft_lport_wwn *old_ft_wwn; u64 wwpn; - pr_debug("add lport %s\n", name); + pr_debug("add wwn %s\n", name); if (ft_parse_wwn(name, &wwpn, 1) < 0) return NULL; - lacl = kzalloc(sizeof(*lacl), GFP_KERNEL); - if (!lacl) + ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL); + if (!ft_wwn) return NULL; - lacl->wwpn = wwpn; - INIT_LIST_HEAD(&lacl->tpg_list); + ft_wwn->wwpn = wwpn; mutex_lock(&ft_lport_lock); - list_for_each_entry(old_lacl, &ft_lport_list, list) { - if (old_lacl->wwpn == wwpn) { + list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) { + if (old_ft_wwn->wwpn == wwpn) { mutex_unlock(&ft_lport_lock); - kfree(lacl); + kfree(ft_wwn); return NULL; } } - list_add_tail(&lacl->list, &ft_lport_list); - ft_format_wwn(lacl->name, sizeof(lacl->name), wwpn); + list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list); + ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn); mutex_unlock(&ft_lport_lock); - return &lacl->fc_lport_wwn; + return &ft_wwn->se_wwn; } -static void ft_del_lport(struct se_wwn *wwn) +static void ft_del_wwn(struct se_wwn *wwn) { - struct ft_lport_acl *lacl = container_of(wwn, - struct ft_lport_acl, fc_lport_wwn); + struct ft_lport_wwn *ft_wwn = container_of(wwn, + struct ft_lport_wwn, se_wwn); - pr_debug("del lport %s\n", lacl->name); + pr_debug("del wwn %s\n", ft_wwn->name); mutex_lock(&ft_lport_lock); - list_del(&lacl->list); + list_del(&ft_wwn->ft_wwn_node); mutex_unlock(&ft_lport_lock); - kfree(lacl); + kfree(ft_wwn); } static ssize_t ft_wwn_show_attr_version( @@ -471,7 +472,7 @@ static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg) { struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; - return tpg->lport_acl->name; + return tpg->lport_wwn->name; } static u16 ft_get_tag(struct se_portal_group *se_tpg) @@ -536,12 +537,13 @@ static struct target_core_fabric_ops ft_fabric_ops = { .queue_data_in = ft_queue_data_in, .queue_status = ft_queue_status, .queue_tm_rsp = ft_queue_tm_resp, + .aborted_task = ft_aborted_task, /* * Setup function pointers for generic logic in * target_core_fabric_configfs.c */ - .fabric_make_wwn = &ft_add_lport, - .fabric_drop_wwn = &ft_del_lport, + .fabric_make_wwn = &ft_add_wwn, + .fabric_drop_wwn = &ft_del_wwn, .fabric_make_tpg = &ft_add_tpg, .fabric_drop_tpg = &ft_del_tpg, .fabric_post_link = NULL, diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index ae52c08dad09..21ce50880c79 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -51,7 +51,7 @@ static void ft_sess_delete_all(struct ft_tport *); * Lookup or allocate target local port. * Caller holds ft_lport_lock. */ -static struct ft_tport *ft_tport_create(struct fc_lport *lport) +static struct ft_tport *ft_tport_get(struct fc_lport *lport) { struct ft_tpg *tpg; struct ft_tport *tport; @@ -68,6 +68,7 @@ static struct ft_tport *ft_tport_create(struct fc_lport *lport) if (tport) { tport->tpg = tpg; + tpg->tport = tport; return tport; } @@ -114,7 +115,7 @@ static void ft_tport_delete(struct ft_tport *tport) void ft_lport_add(struct fc_lport *lport, void *arg) { mutex_lock(&ft_lport_lock); - ft_tport_create(lport); + ft_tport_get(lport); mutex_unlock(&ft_lport_lock); } @@ -211,7 +212,8 @@ static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id, return NULL; sess->se_sess = transport_init_session_tags(TCM_FC_DEFAULT_TAGS, - sizeof(struct ft_cmd)); + sizeof(struct ft_cmd), + TARGET_PROT_NORMAL); if (IS_ERR(sess->se_sess)) { kfree(sess); return NULL; @@ -350,7 +352,7 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len, struct ft_node_acl *acl; u32 fcp_parm; - tport = ft_tport_create(rdata->local_port); + tport = ft_tport_get(rdata->local_port); if (!tport) goto not_target; /* not a target for this local port */ diff --git a/drivers/thermal/imx_thermal.c b/drivers/thermal/imx_thermal.c index 45af765a3198..a99c63152b8d 100644 --- a/drivers/thermal/imx_thermal.c +++ b/drivers/thermal/imx_thermal.c @@ -62,12 +62,16 @@ enum imx_thermal_trip { #define IMX_POLLING_DELAY 2000 /* millisecond */ #define IMX_PASSIVE_DELAY 1000 +#define FACTOR0 10000000 +#define FACTOR1 15976 +#define FACTOR2 4297157 + struct imx_thermal_data { struct thermal_zone_device *tz; struct thermal_cooling_device *cdev; enum thermal_device_mode mode; struct regmap *tempmon; - int c1, c2; /* See formula in imx_get_sensor_data() */ + u32 c1, c2; /* See formula in imx_get_sensor_data() */ unsigned long temp_passive; unsigned long temp_critical; unsigned long alarm_temp; @@ -84,7 +88,7 @@ static void imx_set_alarm_temp(struct imx_thermal_data *data, int alarm_value; data->alarm_temp = alarm_temp; - alarm_value = (alarm_temp - data->c2) / data->c1; + alarm_value = (data->c2 - alarm_temp) / data->c1; regmap_write(map, TEMPSENSE0 + REG_CLR, TEMPSENSE0_ALARM_VALUE_MASK); regmap_write(map, TEMPSENSE0 + REG_SET, alarm_value << TEMPSENSE0_ALARM_VALUE_SHIFT); @@ -136,7 +140,7 @@ static int imx_get_temp(struct thermal_zone_device *tz, unsigned long *temp) n_meas = (val & TEMPSENSE0_TEMP_CNT_MASK) >> TEMPSENSE0_TEMP_CNT_SHIFT; /* See imx_get_sensor_data() for formula derivation */ - *temp = data->c2 + data->c1 * n_meas; + *temp = data->c2 - n_meas * data->c1; /* Update alarm value to next higher trip point */ if (data->alarm_temp == data->temp_passive && *temp >= data->temp_passive) @@ -305,6 +309,7 @@ static int imx_get_sensor_data(struct platform_device *pdev) int t1, t2, n1, n2; int ret; u32 val; + u64 temp64; map = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "fsl,tempmon-data"); @@ -330,6 +335,8 @@ static int imx_get_sensor_data(struct platform_device *pdev) * [31:20] - sensor value @ 25C * [19:8] - sensor value of hot * [7:0] - hot temperature value + * Use universal formula now and only need sensor value @ 25C + * slope = 0.4297157 - (0.0015976 * 25C fuse) */ n1 = val >> 20; n2 = (val & 0xfff00) >> 8; @@ -337,20 +344,26 @@ static int imx_get_sensor_data(struct platform_device *pdev) t1 = 25; /* t1 always 25C */ /* - * Derived from linear interpolation, - * Tmeas = T2 + (Nmeas - N2) * (T1 - T2) / (N1 - N2) + * Derived from linear interpolation: + * slope = 0.4297157 - (0.0015976 * 25C fuse) + * slope = (FACTOR2 - FACTOR1 * n1) / FACTOR0 + * (Nmeas - n1) / (Tmeas - t1) = slope * We want to reduce this down to the minimum computation necessary * for each temperature read. Also, we want Tmeas in millicelsius * and we don't want to lose precision from integer division. So... - * milli_Tmeas = 1000 * T2 + 1000 * (Nmeas - N2) * (T1 - T2) / (N1 - N2) - * Let constant c1 = 1000 * (T1 - T2) / (N1 - N2) - * milli_Tmeas = (1000 * T2) + c1 * (Nmeas - N2) - * milli_Tmeas = (1000 * T2) + (c1 * Nmeas) - (c1 * N2) - * Let constant c2 = (1000 * T2) - (c1 * N2) - * milli_Tmeas = c2 + (c1 * Nmeas) + * Tmeas = (Nmeas - n1) / slope + t1 + * milli_Tmeas = 1000 * (Nmeas - n1) / slope + 1000 * t1 + * milli_Tmeas = -1000 * (n1 - Nmeas) / slope + 1000 * t1 + * Let constant c1 = (-1000 / slope) + * milli_Tmeas = (n1 - Nmeas) * c1 + 1000 * t1 + * Let constant c2 = n1 *c1 + 1000 * t1 + * milli_Tmeas = c2 - Nmeas * c1 */ - data->c1 = 1000 * (t1 - t2) / (n1 - n2); - data->c2 = 1000 * t2 - data->c1 * n2; + temp64 = FACTOR0; + temp64 *= 1000; + do_div(temp64, FACTOR1 * n1 - FACTOR2); + data->c1 = temp64; + data->c2 = n1 * data->c1 + 1000 * t1; /* * Set the default passive cooling trip point to 20 °C below the diff --git a/drivers/thermal/rcar_thermal.c b/drivers/thermal/rcar_thermal.c index 79a09d02bbca..5a37940b02c9 100644 --- a/drivers/thermal/rcar_thermal.c +++ b/drivers/thermal/rcar_thermal.c @@ -299,12 +299,17 @@ static void _rcar_thermal_irq_ctrl(struct rcar_thermal_priv *priv, int enable) static void rcar_thermal_work(struct work_struct *work) { struct rcar_thermal_priv *priv; + unsigned long cctemp, nctemp; priv = container_of(work, struct rcar_thermal_priv, work.work); + rcar_thermal_get_temp(priv->zone, &cctemp); rcar_thermal_update_temp(priv); rcar_thermal_irq_enable(priv); - thermal_zone_device_update(priv->zone); + + rcar_thermal_get_temp(priv->zone, &nctemp); + if (nctemp != cctemp) + thermal_zone_device_update(priv->zone); } static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) @@ -313,7 +318,7 @@ static u32 rcar_thermal_had_changed(struct rcar_thermal_priv *priv, u32 status) status = (status >> rcar_id_to_shift(priv)) & 0x3; - if (status & 0x3) { + if (status) { dev_dbg(dev, "thermal%d %s%s\n", priv->id, (status & 0x2) ? "Rising " : "", diff --git a/drivers/thermal/ti-soc-thermal/ti-bandgap.c b/drivers/thermal/ti-soc-thermal/ti-bandgap.c index 74c0e3474d6e..3ab12ee359b7 100644 --- a/drivers/thermal/ti-soc-thermal/ti-bandgap.c +++ b/drivers/thermal/ti-soc-thermal/ti-bandgap.c @@ -1500,10 +1500,8 @@ static int ti_bandgap_resume(struct device *dev) return ti_bandgap_restore_ctxt(bgp); } -static const struct dev_pm_ops ti_bandgap_dev_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ti_bandgap_suspend, - ti_bandgap_resume) -}; +static SIMPLE_DEV_PM_OPS(ti_bandgap_dev_pm_ops, ti_bandgap_suspend, + ti_bandgap_resume); #define DEV_PM_OPS (&ti_bandgap_dev_pm_ops) #else diff --git a/drivers/tty/tty_audit.c b/drivers/tty/tty_audit.c index b0e540137e39..90ca082935f6 100644 --- a/drivers/tty/tty_audit.c +++ b/drivers/tty/tty_audit.c @@ -65,6 +65,7 @@ static void tty_audit_log(const char *description, int major, int minor, { struct audit_buffer *ab; struct task_struct *tsk = current; + pid_t pid = task_pid_nr(tsk); uid_t uid = from_kuid(&init_user_ns, task_uid(tsk)); uid_t loginuid = from_kuid(&init_user_ns, audit_get_loginuid(tsk)); unsigned int sessionid = audit_get_sessionid(tsk); @@ -74,7 +75,7 @@ static void tty_audit_log(const char *description, int major, int minor, char name[sizeof(tsk->comm)]; audit_log_format(ab, "%s pid=%u uid=%u auid=%u ses=%u major=%d" - " minor=%d comm=", description, tsk->pid, uid, + " minor=%d comm=", description, pid, uid, loginuid, sessionid, major, minor); get_task_comm(name, tsk); audit_log_untrustedstring(ab, name); diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/tcm_usb_gadget.c index 460c266b8e24..f058c0368d61 100644 --- a/drivers/usb/gadget/tcm_usb_gadget.c +++ b/drivers/usb/gadget/tcm_usb_gadget.c @@ -1471,6 +1471,11 @@ static void usbg_queue_tm_rsp(struct se_cmd *se_cmd) { } +static void usbg_aborted_task(struct se_cmd *se_cmd) +{ + return; +} + static const char *usbg_check_wwn(const char *name) { const char *n; @@ -1726,7 +1731,7 @@ static int tcm_usbg_make_nexus(struct usbg_tpg *tpg, char *name) pr_err("Unable to allocate struct tcm_vhost_nexus\n"); goto err_unlock; } - tv_nexus->tvn_se_sess = transport_init_session(); + tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL); if (IS_ERR(tv_nexus->tvn_se_sess)) goto err_free; @@ -1897,6 +1902,7 @@ static struct target_core_fabric_ops usbg_ops = { .queue_data_in = usbg_send_read_response, .queue_status = usbg_send_status_response, .queue_tm_rsp = usbg_queue_tm_rsp, + .aborted_task = usbg_aborted_task, .check_stop_free = usbg_check_stop_free, .fabric_make_wwn = usbg_make_tport, diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index e1e22e0f01e8..be414d2b2b22 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -818,9 +818,9 @@ static int vhost_net_release(struct inode *inode, struct file *f) vhost_dev_cleanup(&n->dev, false); vhost_net_vq_reset(n); if (tx_sock) - fput(tx_sock->file); + sockfd_put(tx_sock); if (rx_sock) - fput(rx_sock->file); + sockfd_put(rx_sock); /* Make sure no callbacks are outstanding */ synchronize_rcu_bh(); /* We do an extra flush before freeing memory, @@ -860,7 +860,7 @@ static struct socket *get_raw_socket(int fd) } return sock; err: - fput(sock->file); + sockfd_put(sock); return ERR_PTR(r); } @@ -966,7 +966,7 @@ static long vhost_net_set_backend(struct vhost_net *n, unsigned index, int fd) if (oldsock) { vhost_net_flush_vq(n, index); - fput(oldsock->file); + sockfd_put(oldsock); } mutex_unlock(&n->dev.mutex); @@ -978,7 +978,7 @@ err_used: if (ubufs) vhost_net_ubuf_put_wait_and_free(ubufs); err_ubufs: - fput(sock->file); + sockfd_put(sock); err_vq: mutex_unlock(&vq->mutex); err: @@ -1009,9 +1009,9 @@ static long vhost_net_reset_owner(struct vhost_net *n) done: mutex_unlock(&n->dev.mutex); if (tx_sock) - fput(tx_sock->file); + sockfd_put(tx_sock); if (rx_sock) - fput(rx_sock->file); + sockfd_put(rx_sock); return err; } diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index e48d4a672580..cf50ce93975b 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -539,6 +539,11 @@ static void tcm_vhost_queue_tm_rsp(struct se_cmd *se_cmd) return; } +static void tcm_vhost_aborted_task(struct se_cmd *se_cmd) +{ + return; +} + static void tcm_vhost_free_evt(struct vhost_scsi *vs, struct tcm_vhost_evt *evt) { vs->vs_events_nr--; @@ -1740,7 +1745,8 @@ static int tcm_vhost_make_nexus(struct tcm_vhost_tpg *tpg, */ tv_nexus->tvn_se_sess = transport_init_session_tags( TCM_VHOST_DEFAULT_TAGS, - sizeof(struct tcm_vhost_cmd)); + sizeof(struct tcm_vhost_cmd), + TARGET_PROT_NORMAL); if (IS_ERR(tv_nexus->tvn_se_sess)) { mutex_unlock(&tpg->tv_tpg_mutex); kfree(tv_nexus); @@ -2131,6 +2137,7 @@ static struct target_core_fabric_ops tcm_vhost_ops = { .queue_data_in = tcm_vhost_queue_data_in, .queue_status = tcm_vhost_queue_status, .queue_tm_rsp = tcm_vhost_queue_tm_rsp, + .aborted_task = tcm_vhost_aborted_task, /* * Setup callers for generic logic in target_core_fabric_configfs.c */ diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index 27d3cf255e78..bd2172c2d650 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -347,7 +347,7 @@ struct backlight_device *backlight_device_register(const char *name, rc = device_register(&new_bd->dev); if (rc) { - kfree(new_bd); + put_device(&new_bd->dev); return ERR_PTR(rc); } diff --git a/drivers/video/backlight/gpio_backlight.c b/drivers/video/backlight/gpio_backlight.c index 81fb12770c2a..a2eba12e1cb7 100644 --- a/drivers/video/backlight/gpio_backlight.c +++ b/drivers/video/backlight/gpio_backlight.c @@ -13,6 +13,8 @@ #include <linux/init.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> #include <linux/platform_data/gpio_backlight.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -23,6 +25,7 @@ struct gpio_backlight { int gpio; int active; + int def_value; }; static int gpio_backlight_update_status(struct backlight_device *bl) @@ -60,6 +63,29 @@ static const struct backlight_ops gpio_backlight_ops = { .check_fb = gpio_backlight_check_fb, }; +static int gpio_backlight_probe_dt(struct platform_device *pdev, + struct gpio_backlight *gbl) +{ + struct device_node *np = pdev->dev.of_node; + enum of_gpio_flags gpio_flags; + + gbl->gpio = of_get_gpio_flags(np, 0, &gpio_flags); + + if (!gpio_is_valid(gbl->gpio)) { + if (gbl->gpio != -EPROBE_DEFER) { + dev_err(&pdev->dev, + "Error: The gpios parameter is missing or invalid.\n"); + } + return gbl->gpio; + } + + gbl->active = (gpio_flags & OF_GPIO_ACTIVE_LOW) ? 0 : 1; + + gbl->def_value = of_property_read_bool(np, "default-on"); + + return 0; +} + static int gpio_backlight_probe(struct platform_device *pdev) { struct gpio_backlight_platform_data *pdata = @@ -67,10 +93,12 @@ static int gpio_backlight_probe(struct platform_device *pdev) struct backlight_properties props; struct backlight_device *bl; struct gpio_backlight *gbl; + struct device_node *np = pdev->dev.of_node; int ret; - if (!pdata) { - dev_err(&pdev->dev, "failed to find platform data\n"); + if (!pdata && !np) { + dev_err(&pdev->dev, + "failed to find platform data or device tree node.\n"); return -ENODEV; } @@ -79,14 +107,22 @@ static int gpio_backlight_probe(struct platform_device *pdev) return -ENOMEM; gbl->dev = &pdev->dev; - gbl->fbdev = pdata->fbdev; - gbl->gpio = pdata->gpio; - gbl->active = pdata->active_low ? 0 : 1; + + if (np) { + ret = gpio_backlight_probe_dt(pdev, gbl); + if (ret) + return ret; + } else { + gbl->fbdev = pdata->fbdev; + gbl->gpio = pdata->gpio; + gbl->active = pdata->active_low ? 0 : 1; + gbl->def_value = pdata->def_value; + } ret = devm_gpio_request_one(gbl->dev, gbl->gpio, GPIOF_DIR_OUT | (gbl->active ? GPIOF_INIT_LOW : GPIOF_INIT_HIGH), - pdata->name); + pdata ? pdata->name : "backlight"); if (ret < 0) { dev_err(&pdev->dev, "unable to request GPIO\n"); return ret; @@ -103,17 +139,25 @@ static int gpio_backlight_probe(struct platform_device *pdev) return PTR_ERR(bl); } - bl->props.brightness = pdata->def_value; + bl->props.brightness = gbl->def_value; backlight_update_status(bl); platform_set_drvdata(pdev, bl); return 0; } +#ifdef CONFIG_OF +static struct of_device_id gpio_backlight_of_match[] = { + { .compatible = "gpio-backlight" }, + { /* sentinel */ } +}; +#endif + static struct platform_driver gpio_backlight_driver = { .driver = { .name = "gpio-backlight", .owner = THIS_MODULE, + .of_match_table = of_match_ptr(gpio_backlight_of_match), }, .probe = gpio_backlight_probe, }; diff --git a/drivers/video/backlight/lm3639_bl.c b/drivers/video/backlight/lm3639_bl.c index 6fd60adf922e..5f36808d214f 100644 --- a/drivers/video/backlight/lm3639_bl.c +++ b/drivers/video/backlight/lm3639_bl.c @@ -349,8 +349,9 @@ static int lm3639_probe(struct i2c_client *client, props.brightness = pdata->init_brt_led; props.max_brightness = pdata->max_brt_led; pchip->bled = - backlight_device_register("lm3639_bled", pchip->dev, pchip, - &lm3639_bled_ops, &props); + devm_backlight_device_register(pchip->dev, "lm3639_bled", + pchip->dev, pchip, &lm3639_bled_ops, + &props); if (IS_ERR(pchip->bled)) { dev_err(&client->dev, "fail : backlight register\n"); ret = PTR_ERR(pchip->bled); @@ -360,7 +361,7 @@ static int lm3639_probe(struct i2c_client *client, ret = device_create_file(&(pchip->bled->dev), &dev_attr_bled_mode); if (ret < 0) { dev_err(&client->dev, "failed : add sysfs entries\n"); - goto err_bled_mode; + goto err_out; } /* flash */ @@ -391,8 +392,6 @@ err_torch: led_classdev_unregister(&pchip->cdev_flash); err_flash: device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode); -err_bled_mode: - backlight_device_unregister(pchip->bled); err_out: return ret; } @@ -407,10 +406,8 @@ static int lm3639_remove(struct i2c_client *client) led_classdev_unregister(&pchip->cdev_torch); if (&pchip->cdev_flash) led_classdev_unregister(&pchip->cdev_flash); - if (pchip->bled) { + if (pchip->bled) device_remove_file(&(pchip->bled->dev), &dev_attr_bled_mode); - backlight_device_unregister(pchip->bled); - } return 0; } @@ -432,6 +429,6 @@ static struct i2c_driver lm3639_i2c_driver = { module_i2c_driver(lm3639_i2c_driver); MODULE_DESCRIPTION("Texas Instruments Backlight+Flash LED driver for LM3639"); -MODULE_AUTHOR("Daniel Jeong <daniel.jeong@ti.com>"); -MODULE_AUTHOR("G.Shark Jeong <gshark.jeong@gmail.com>"); +MODULE_AUTHOR("Daniel Jeong <gshark.jeong@gmail.com>"); +MODULE_AUTHOR("Ldd Mlp <ldd-mlp@list.ti.com>"); MODULE_LICENSE("GPL v2"); |