diff options
27 files changed, 1275 insertions, 703 deletions
diff --git a/Documentation/fpga/fpga-mgr.txt b/Documentation/fpga/fpga-mgr.txt index 78f197fadfd1..cc6413ed6fc9 100644 --- a/Documentation/fpga/fpga-mgr.txt +++ b/Documentation/fpga/fpga-mgr.txt @@ -11,61 +11,65 @@ hidden away in a low level driver which registers a set of ops with the core. The FPGA image data itself is very manufacturer specific, but for our purposes it's just binary data. The FPGA manager core won't parse it. +The FPGA image to be programmed can be in a scatter gather list, a single +contiguous buffer, or a firmware file. Because allocating contiguous kernel +memory for the buffer should be avoided, users are encouraged to use a scatter +gather list instead if possible. + +The particulars for programming the image are presented in a structure (struct +fpga_image_info). This struct contains parameters such as pointers to the +FPGA image as well as image-specific particulars such as whether the image was +built for full or partial reconfiguration. API Functions: ============== -To program the FPGA from a file or from a buffer: -------------------------------------------------- - - int fpga_mgr_buf_load(struct fpga_manager *mgr, - struct fpga_image_info *info, - const char *buf, size_t count); - -Load the FPGA from an image which exists as a contiguous buffer in -memory. Allocating contiguous kernel memory for the buffer should be avoided, -users are encouraged to use the _sg interface instead of this. - - int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, - struct fpga_image_info *info, - struct sg_table *sgt); +To program the FPGA: +-------------------- -Load the FPGA from an image from non-contiguous in memory. Callers can -construct a sg_table using alloc_page backed memory. + int fpga_mgr_load(struct fpga_manager *mgr, + struct fpga_image_info *info); - int fpga_mgr_firmware_load(struct fpga_manager *mgr, - struct fpga_image_info *info, - const char *image_name); - -Load the FPGA from an image which exists as a file. The image file must be on -the firmware search path (see the firmware class documentation). If successful, +Load the FPGA from an image which is indicated in the info. If successful, the FPGA ends up in operating mode. Return 0 on success or a negative error code. -A FPGA design contained in a FPGA image file will likely have particulars that -affect how the image is programmed to the FPGA. These are contained in struct -fpga_image_info. Currently the only such particular is a single flag bit -indicating whether the image is for full or partial reconfiguration. +To allocate or free a struct fpga_image_info: +--------------------------------------------- + + struct fpga_image_info *fpga_image_info_alloc(struct device *dev); + + void fpga_image_info_free(struct fpga_image_info *info); To get/put a reference to a FPGA manager: ----------------------------------------- struct fpga_manager *of_fpga_mgr_get(struct device_node *node); struct fpga_manager *fpga_mgr_get(struct device *dev); + void fpga_mgr_put(struct fpga_manager *mgr); -Given a DT node or device, get an exclusive reference to a FPGA manager. +Given a DT node or device, get a reference to a FPGA manager. This pointer +can be saved until you are ready to program the FPGA. fpga_mgr_put releases +the reference. - void fpga_mgr_put(struct fpga_manager *mgr); -Release the reference. +To get exclusive control of a FPGA manager: +------------------------------------------- + + int fpga_mgr_lock(struct fpga_manager *mgr); + void fpga_mgr_unlock(struct fpga_manager *mgr); + +The user should call fpga_mgr_lock and verify that it returns 0 before +attempting to program the FPGA. Likewise, the user should call +fpga_mgr_unlock when done programming the FPGA. To register or unregister the low level FPGA-specific driver: ------------------------------------------------------------- int fpga_mgr_register(struct device *dev, const char *name, - const struct fpga_manager_ops *mops, - void *priv); + const struct fpga_manager_ops *mops, + void *priv); void fpga_mgr_unregister(struct device *dev); @@ -75,62 +79,58 @@ device." How to write an image buffer to a supported FPGA ================================================ -/* Include to get the API */ #include <linux/fpga/fpga-mgr.h> -/* device node that specifies the FPGA manager to use */ -struct device_node *mgr_node = ... +struct fpga_manager *mgr; +struct fpga_image_info *info; +int ret; -/* FPGA image is in this buffer. count is size of the buffer. */ -char *buf = ... -int count = ... +/* + * Get a reference to FPGA manager. The manager is not locked, so you can + * hold onto this reference without it preventing programming. + * + * This example uses the device node of the manager. Alternatively, use + * fpga_mgr_get(dev) instead if you have the device. + */ +mgr = of_fpga_mgr_get(mgr_node); /* struct with information about the FPGA image to program. */ -struct fpga_image_info info; +info = fpga_image_info_alloc(dev); /* flags indicates whether to do full or partial reconfiguration */ -info.flags = 0; +info->flags = FPGA_MGR_PARTIAL_RECONFIG; -int ret; +/* + * At this point, indicate where the image is. This is pseudo-code; you're + * going to use one of these three. + */ +if (image is in a scatter gather table) { -/* Get exclusive control of FPGA manager */ -struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node); + info->sgt = [your scatter gather table] -/* Load the buffer to the FPGA */ -ret = fpga_mgr_buf_load(mgr, &info, buf, count); - -/* Release the FPGA manager */ -fpga_mgr_put(mgr); - - -How to write an image file to a supported FPGA -============================================== -/* Include to get the API */ -#include <linux/fpga/fpga-mgr.h> +} else if (image is in a buffer) { -/* device node that specifies the FPGA manager to use */ -struct device_node *mgr_node = ... + info->buf = [your image buffer] + info->count = [image buffer size] -/* FPGA image is in this file which is in the firmware search path */ -const char *path = "fpga-image-9.rbf" +} else if (image is in a firmware file) { -/* struct with information about the FPGA image to program. */ -struct fpga_image_info info; - -/* flags indicates whether to do full or partial reconfiguration */ -info.flags = 0; + info->firmware_name = devm_kstrdup(dev, firmware_name, GFP_KERNEL); -int ret; +} /* Get exclusive control of FPGA manager */ -struct fpga_manager *mgr = of_fpga_mgr_get(mgr_node); +ret = fpga_mgr_lock(mgr); -/* Get the firmware image (path) and load it to the FPGA */ -ret = fpga_mgr_firmware_load(mgr, &info, path); +/* Load the buffer to the FPGA */ +ret = fpga_mgr_buf_load(mgr, &info, buf, count); /* Release the FPGA manager */ +fpga_mgr_unlock(mgr); fpga_mgr_put(mgr); +/* Deallocate the image info if you're done with it */ +fpga_image_info_free(info); How to support a new FPGA device ================================ diff --git a/Documentation/fpga/fpga-region.txt b/Documentation/fpga/fpga-region.txt new file mode 100644 index 000000000000..139a02ba1ff6 --- /dev/null +++ b/Documentation/fpga/fpga-region.txt @@ -0,0 +1,95 @@ +FPGA Regions + +Alan Tull 2017 + +CONTENTS + - Introduction + - The FPGA region API + - Usage example + +Introduction +============ + +This document is meant to be an brief overview of the FPGA region API usage. A +more conceptual look at regions can be found in [1]. + +For the purposes of this API document, let's just say that a region associates +an FPGA Manager and a bridge (or bridges) with a reprogrammable region of an +FPGA or the whole FPGA. The API provides a way to register a region and to +program a region. + +Currently the only layer above fpga-region.c in the kernel is the Device Tree +support (of-fpga-region.c) described in [1]. The DT support layer uses regions +to program the FPGA and then DT to handle enumeration. The common region code +is intended to be used by other schemes that have other ways of accomplishing +enumeration after programming. + +An fpga-region can be set up to know the following things: +* which FPGA manager to use to do the programming +* which bridges to disable before programming and enable afterwards. + +Additional info needed to program the FPGA image is passed in the struct +fpga_image_info [2] including: +* pointers to the image as either a scatter-gather buffer, a contiguous + buffer, or the name of firmware file +* flags indicating specifics such as whether the image if for partial + reconfiguration. + +=================== +The FPGA region API +=================== + +To register or unregister a region: +----------------------------------- + + int fpga_region_register(struct device *dev, + struct fpga_region *region); + int fpga_region_unregister(struct fpga_region *region); + +An example of usage can be seen in the probe function of [3] + +To program an FPGA: +------------------- + int fpga_region_program_fpga(struct fpga_region *region); + +This function operates on info passed in the fpga_image_info +(region->info). + +This function will attempt to: + * lock the region's mutex + * lock the region's FPGA manager + * build a list of FPGA bridges if a method has been specified to do so + * disable the bridges + * program the FPGA + * re-enable the bridges + * release the locks + +============= +Usage example +============= + +First, allocate the info struct: + + info = fpga_image_info_alloc(dev); + if (!info) + return -ENOMEM; + +Set flags as needed, i.e. + + info->flags |= FPGA_MGR_PARTIAL_RECONFIG; + +Point to your FPGA image, such as: + + info->sgt = &sgt; + +Add info to region and do the programming: + + region->info = info; + ret = fpga_region_program_fpga(region); + +Then enumerate whatever hardware has appeared in the FPGA. + +-- +[1] ../devicetree/bindings/fpga/fpga-region.txt +[2] ./fpga-mgr.txt +[3] ../../drivers/fpga/of-fpga-region.c diff --git a/Documentation/fpga/overview.txt b/Documentation/fpga/overview.txt new file mode 100644 index 000000000000..0f1236e7e675 --- /dev/null +++ b/Documentation/fpga/overview.txt @@ -0,0 +1,23 @@ +Linux kernel FPGA support + +Alan Tull 2017 + +The main point of this project has been to separate the out the upper layers +that know when to reprogram a FPGA from the lower layers that know how to +reprogram a specific FPGA device. The intention is to make this manufacturer +agnostic, understanding that of course the FPGA images are very device specific +themselves. + +The framework in the kernel includes: +* low level FPGA manager drivers that know how to program a specific device +* the fpga-mgr framework they are registered with +* low level FPGA bridge drivers for hard/soft bridges which are intended to + be disable during FPGA programming +* the fpga-bridge framework they are registered with +* the fpga-region framework which associates and controls managers and bridges + as reconfigurable regions +* the of-fpga-region support for reprogramming FPGAs when device tree overlays + are applied. + +I would encourage you the user to add code that creates FPGA regions rather +that trying to control managers and bridges separately. diff --git a/MAINTAINERS b/MAINTAINERS index 82ad0eabce4f..8d83ed21a62b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3400,8 +3400,8 @@ M: Arnd Bergmann <arnd@arndb.de> M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git S: Supported -F: drivers/char/* -F: drivers/misc/* +F: drivers/char/ +F: drivers/misc/ F: include/linux/miscdevice.h CHECKPATCH diff --git a/drivers/android/binder.c b/drivers/android/binder.c index bccec9de0533..85b0bb497759 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -577,6 +577,8 @@ enum { * (protected by @proc->inner_lock) * @todo: list of work to do for this thread * (protected by @proc->inner_lock) + * @process_todo: whether work in @todo should be processed + * (protected by @proc->inner_lock) * @return_error: transaction errors reported by this thread * (only accessed by this thread) * @reply_error: transaction errors reported by target thread @@ -602,6 +604,7 @@ struct binder_thread { bool looper_need_return; /* can be written by other thread */ struct binder_transaction *transaction_stack; struct list_head todo; + bool process_todo; struct binder_error return_error; struct binder_error reply_error; wait_queue_head_t wait; @@ -787,6 +790,16 @@ static bool binder_worklist_empty(struct binder_proc *proc, return ret; } +/** + * binder_enqueue_work_ilocked() - Add an item to the work list + * @work: struct binder_work to add to list + * @target_list: list to add work to + * + * Adds the work to the specified list. Asserts that work + * is not already on a list. + * + * Requires the proc->inner_lock to be held. + */ static void binder_enqueue_work_ilocked(struct binder_work *work, struct list_head *target_list) @@ -797,22 +810,56 @@ binder_enqueue_work_ilocked(struct binder_work *work, } /** - * binder_enqueue_work() - Add an item to the work list - * @proc: binder_proc associated with list + * binder_enqueue_deferred_thread_work_ilocked() - Add deferred thread work + * @thread: thread to queue work to * @work: struct binder_work to add to list - * @target_list: list to add work to * - * Adds the work to the specified list. Asserts that work - * is not already on a list. + * Adds the work to the todo list of the thread. Doesn't set the process_todo + * flag, which means that (if it wasn't already set) the thread will go to + * sleep without handling this work when it calls read. + * + * Requires the proc->inner_lock to be held. */ static void -binder_enqueue_work(struct binder_proc *proc, - struct binder_work *work, - struct list_head *target_list) +binder_enqueue_deferred_thread_work_ilocked(struct binder_thread *thread, + struct binder_work *work) { - binder_inner_proc_lock(proc); - binder_enqueue_work_ilocked(work, target_list); - binder_inner_proc_unlock(proc); + binder_enqueue_work_ilocked(work, &thread->todo); +} + +/** + * binder_enqueue_thread_work_ilocked() - Add an item to the thread work list + * @thread: thread to queue work to + * @work: struct binder_work to add to list + * + * Adds the work to the todo list of the thread, and enables processing + * of the todo queue. + * + * Requires the proc->inner_lock to be held. + */ +static void +binder_enqueue_thread_work_ilocked(struct binder_thread *thread, + struct binder_work *work) +{ + binder_enqueue_work_ilocked(work, &thread->todo); + thread->process_todo = true; +} + +/** + * binder_enqueue_thread_work() - Add an item to the thread work list + * @thread: thread to queue work to + * @work: struct binder_work to add to list + * + * Adds the work to the todo list of the thread, and enables processing + * of the todo queue. + */ +static void +binder_enqueue_thread_work(struct binder_thread *thread, + struct binder_work *work) +{ + binder_inner_proc_lock(thread->proc); + binder_enqueue_thread_work_ilocked(thread, work); + binder_inner_proc_unlock(thread->proc); } static void @@ -927,7 +974,7 @@ static long task_close_fd(struct binder_proc *proc, unsigned int fd) static bool binder_has_work_ilocked(struct binder_thread *thread, bool do_proc_work) { - return !binder_worklist_empty_ilocked(&thread->todo) || + return thread->process_todo || thread->looper_need_return || (do_proc_work && !binder_worklist_empty_ilocked(&thread->proc->todo)); @@ -1215,6 +1262,17 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong, node->local_strong_refs++; if (!node->has_strong_ref && target_list) { binder_dequeue_work_ilocked(&node->work); + /* + * Note: this function is the only place where we queue + * directly to a thread->todo without using the + * corresponding binder_enqueue_thread_work() helper + * functions; in this case it's ok to not set the + * process_todo flag, since we know this node work will + * always be followed by other work that starts queue + * processing: in case of synchronous transactions, a + * BR_REPLY or BR_ERROR; in case of oneway + * transactions, a BR_TRANSACTION_COMPLETE. + */ binder_enqueue_work_ilocked(&node->work, target_list); } } else { @@ -1226,6 +1284,9 @@ static int binder_inc_node_nilocked(struct binder_node *node, int strong, node->debug_id); return -EINVAL; } + /* + * See comment above + */ binder_enqueue_work_ilocked(&node->work, target_list); } } @@ -1915,9 +1976,9 @@ static void binder_send_failed_reply(struct binder_transaction *t, binder_pop_transaction_ilocked(target_thread, t); if (target_thread->reply_error.cmd == BR_OK) { target_thread->reply_error.cmd = error_code; - binder_enqueue_work_ilocked( - &target_thread->reply_error.work, - &target_thread->todo); + binder_enqueue_thread_work_ilocked( + target_thread, + &target_thread->reply_error.work); wake_up_interruptible(&target_thread->wait); } else { WARN(1, "Unexpected reply error: %u\n", @@ -2556,18 +2617,16 @@ static bool binder_proc_transaction(struct binder_transaction *t, struct binder_proc *proc, struct binder_thread *thread) { - struct list_head *target_list = NULL; struct binder_node *node = t->buffer->target_node; bool oneway = !!(t->flags & TF_ONE_WAY); - bool wakeup = true; + bool pending_async = false; BUG_ON(!node); binder_node_lock(node); if (oneway) { BUG_ON(thread); if (node->has_async_transaction) { - target_list = &node->async_todo; - wakeup = false; + pending_async = true; } else { node->has_async_transaction = 1; } @@ -2581,19 +2640,17 @@ static bool binder_proc_transaction(struct binder_transaction *t, return false; } - if (!thread && !target_list) + if (!thread && !pending_async) thread = binder_select_thread_ilocked(proc); if (thread) - target_list = &thread->todo; - else if (!target_list) - target_list = &proc->todo; + binder_enqueue_thread_work_ilocked(thread, &t->work); + else if (!pending_async) + binder_enqueue_work_ilocked(&t->work, &proc->todo); else - BUG_ON(target_list != &node->async_todo); - - binder_enqueue_work_ilocked(&t->work, target_list); + binder_enqueue_work_ilocked(&t->work, &node->async_todo); - if (wakeup) + if (!pending_async) binder_wakeup_thread_ilocked(proc, thread, !oneway /* sync */); binder_inner_proc_unlock(proc); @@ -3088,10 +3145,10 @@ static void binder_transaction(struct binder_proc *proc, } } tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE; - binder_enqueue_work(proc, tcomplete, &thread->todo); t->work.type = BINDER_WORK_TRANSACTION; if (reply) { + binder_enqueue_thread_work(thread, tcomplete); binder_inner_proc_lock(target_proc); if (target_thread->is_dead) { binder_inner_proc_unlock(target_proc); @@ -3099,13 +3156,21 @@ static void binder_transaction(struct binder_proc *proc, } BUG_ON(t->buffer->async_transaction != 0); binder_pop_transaction_ilocked(target_thread, in_reply_to); - binder_enqueue_work_ilocked(&t->work, &target_thread->todo); + binder_enqueue_thread_work_ilocked(target_thread, &t->work); binder_inner_proc_unlock(target_proc); wake_up_interruptible_sync(&target_thread->wait); binder_free_transaction(in_reply_to); } else if (!(t->flags & TF_ONE_WAY)) { BUG_ON(t->buffer->async_transaction != 0); binder_inner_proc_lock(proc); + /* + * Defer the TRANSACTION_COMPLETE, so we don't return to + * userspace immediately; this allows the target process to + * immediately start processing this transaction, reducing + * latency. We will then return the TRANSACTION_COMPLETE when + * the target replies (or there is an error). + */ + binder_enqueue_deferred_thread_work_ilocked(thread, tcomplete); t->need_reply = 1; t->from_parent = thread->transaction_stack; thread->transaction_stack = t; @@ -3119,6 +3184,7 @@ static void binder_transaction(struct binder_proc *proc, } else { BUG_ON(target_node == NULL); BUG_ON(t->buffer->async_transaction != 1); + binder_enqueue_thread_work(thread, tcomplete); if (!binder_proc_transaction(t, target_proc, NULL)) goto err_dead_proc_or_thread; } @@ -3197,15 +3263,11 @@ err_invalid_target_handle: BUG_ON(thread->return_error.cmd != BR_OK); if (in_reply_to) { thread->return_error.cmd = BR_TRANSACTION_COMPLETE; - binder_enqueue_work(thread->proc, - &thread->return_error.work, - &thread->todo); + binder_enqueue_thread_work(thread, &thread->return_error.work); binder_send_failed_reply(in_reply_to, return_error); } else { thread->return_error.cmd = return_error; - binder_enqueue_work(thread->proc, - &thread->return_error.work, - &thread->todo); + binder_enqueue_thread_work(thread, &thread->return_error.work); } } @@ -3509,10 +3571,9 @@ static int binder_thread_write(struct binder_proc *proc, WARN_ON(thread->return_error.cmd != BR_OK); thread->return_error.cmd = BR_ERROR; - binder_enqueue_work( - thread->proc, - &thread->return_error.work, - &thread->todo); + binder_enqueue_thread_work( + thread, + &thread->return_error.work); binder_debug( BINDER_DEBUG_FAILED_TRANSACTION, "%d:%d BC_REQUEST_DEATH_NOTIFICATION failed\n", @@ -3592,9 +3653,9 @@ static int binder_thread_write(struct binder_proc *proc, if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) - binder_enqueue_work_ilocked( - &death->work, - &thread->todo); + binder_enqueue_thread_work_ilocked( + thread, + &death->work); else { binder_enqueue_work_ilocked( &death->work, @@ -3649,8 +3710,8 @@ static int binder_thread_write(struct binder_proc *proc, if (thread->looper & (BINDER_LOOPER_STATE_REGISTERED | BINDER_LOOPER_STATE_ENTERED)) - binder_enqueue_work_ilocked( - &death->work, &thread->todo); + binder_enqueue_thread_work_ilocked( + thread, &death->work); else { binder_enqueue_work_ilocked( &death->work, @@ -3824,6 +3885,8 @@ retry: break; } w = binder_dequeue_work_head_ilocked(list); + if (binder_worklist_empty_ilocked(&thread->todo)) + thread->process_todo = false; switch (w->type) { case BINDER_WORK_TRANSACTION: { diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c index 6f6f745605af..0dba2308125c 100644 --- a/drivers/android/binder_alloc.c +++ b/drivers/android/binder_alloc.c @@ -281,6 +281,9 @@ static int binder_update_page_range(struct binder_alloc *alloc, int allocate, goto err_vm_insert_page_failed; } + if (index + 1 > alloc->pages_high) + alloc->pages_high = index + 1; + trace_binder_alloc_page_end(alloc, index); /* vm_insert_page does not seem to increment the refcount */ } @@ -853,6 +856,7 @@ void binder_alloc_print_pages(struct seq_file *m, } mutex_unlock(&alloc->mutex); seq_printf(m, " pages: %d:%d:%d\n", active, lru, free); + seq_printf(m, " pages high watermark: %zu\n", alloc->pages_high); } /** diff --git a/drivers/android/binder_alloc.h b/drivers/android/binder_alloc.h index 2dd33b6df104..0b145307f1fd 100644 --- a/drivers/android/binder_alloc.h +++ b/drivers/android/binder_alloc.h @@ -92,6 +92,7 @@ struct binder_lru_page { * @pages: array of binder_lru_page * @buffer_size: size of address space specified via mmap * @pid: pid for associated binder_proc (invariant after init) + * @pages_high: high watermark of offset in @pages * * Bookkeeping structure for per-proc address space management for binder * buffers. It is normally initialized during binder_init() and binder_mmap() @@ -112,6 +113,7 @@ struct binder_alloc { size_t buffer_size; uint32_t buffer_free; int pid; + size_t pages_high; }; #ifdef CONFIG_ANDROID_BINDER_IPC_SELFTEST diff --git a/drivers/char/lp.c b/drivers/char/lp.c index 8249762192d5..be14abf70da1 100644 --- a/drivers/char/lp.c +++ b/drivers/char/lp.c @@ -659,17 +659,31 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd, return retval; } -static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout) +static int lp_set_timeout(unsigned int minor, s64 tv_sec, long tv_usec) { long to_jiffies; /* Convert to jiffies, place in lp_table */ - if ((par_timeout->tv_sec < 0) || - (par_timeout->tv_usec < 0)) { + if (tv_sec < 0 || tv_usec < 0) return -EINVAL; + + /* + * we used to not check, so let's not make this fatal, + * but deal with user space passing a 32-bit tv_nsec in + * a 64-bit field, capping the timeout to 1 second + * worth of microseconds, and capping the total at + * MAX_JIFFY_OFFSET. + */ + if (tv_usec > 999999) + tv_usec = 999999; + + if (tv_sec >= MAX_SEC_IN_JIFFIES - 1) { + to_jiffies = MAX_JIFFY_OFFSET; + } else { + to_jiffies = DIV_ROUND_UP(tv_usec, 1000000/HZ); + to_jiffies += tv_sec * (long) HZ; } - to_jiffies = DIV_ROUND_UP(par_timeout->tv_usec, 1000000/HZ); - to_jiffies += par_timeout->tv_sec * (long) HZ; + if (to_jiffies <= 0) { return -EINVAL; } @@ -677,23 +691,43 @@ static int lp_set_timeout(unsigned int minor, struct timeval *par_timeout) return 0; } +static int lp_set_timeout32(unsigned int minor, void __user *arg) +{ + s32 karg[2]; + + if (copy_from_user(karg, arg, sizeof(karg))) + return -EFAULT; + + return lp_set_timeout(minor, karg[0], karg[1]); +} + +static int lp_set_timeout64(unsigned int minor, void __user *arg) +{ + s64 karg[2]; + + if (copy_from_user(karg, arg, sizeof(karg))) + return -EFAULT; + + return lp_set_timeout(minor, karg[0], karg[1]); +} + static long lp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor; - struct timeval par_timeout; int ret; minor = iminor(file_inode(file)); mutex_lock(&lp_mutex); switch (cmd) { - case LPSETTIMEOUT: - if (copy_from_user(&par_timeout, (void __user *)arg, - sizeof (struct timeval))) { - ret = -EFAULT; + case LPSETTIMEOUT_OLD: + if (BITS_PER_LONG == 32) { + ret = lp_set_timeout32(minor, (void __user *)arg); break; } - ret = lp_set_timeout(minor, &par_timeout); + /* fallthrough for 64-bit */ + case LPSETTIMEOUT_NEW: + ret = lp_set_timeout64(minor, (void __user *)arg); break; default: ret = lp_do_ioctl(minor, cmd, arg, (void __user *)arg); @@ -709,18 +743,19 @@ static long lp_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { unsigned int minor; - struct timeval par_timeout; int ret; minor = iminor(file_inode(file)); mutex_lock(&lp_mutex); switch (cmd) { - case LPSETTIMEOUT: - if (compat_get_timeval(&par_timeout, compat_ptr(arg))) { - ret = -EFAULT; + case LPSETTIMEOUT_OLD: + if (!COMPAT_USE_64BIT_TIME) { + ret = lp_set_timeout32(minor, (void __user *)arg); break; } - ret = lp_set_timeout(minor, &par_timeout); + /* fallthrough for x32 mode */ + case LPSETTIMEOUT_NEW: + ret = lp_set_timeout64(minor, (void __user *)arg); break; #ifdef LP_STATS case LPGETSTATS: diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index ad5448f718b3..f47ef848bcd0 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -11,25 +11,30 @@ menuconfig FPGA if FPGA -config FPGA_REGION - tristate "FPGA Region" - depends on OF && FPGA_BRIDGE +config FPGA_MGR_SOCFPGA + tristate "Altera SOCFPGA FPGA Manager" + depends on ARCH_SOCFPGA || COMPILE_TEST help - FPGA Regions allow loading FPGA images under control of - the Device Tree. + FPGA manager driver support for Altera SOCFPGA. -config FPGA_MGR_ICE40_SPI - tristate "Lattice iCE40 SPI" - depends on OF && SPI +config FPGA_MGR_SOCFPGA_A10 + tristate "Altera SoCFPGA Arria10" + depends on ARCH_SOCFPGA || COMPILE_TEST + select REGMAP_MMIO help - FPGA manager driver support for Lattice iCE40 FPGAs over SPI. + FPGA manager driver support for Altera Arria10 SoCFPGA. -config FPGA_MGR_ALTERA_CVP - tristate "Altera Arria-V/Cyclone-V/Stratix-V CvP FPGA Manager" - depends on PCI +config ALTERA_PR_IP_CORE + tristate "Altera Partial Reconfiguration IP Core" + help + Core driver support for Altera Partial Reconfiguration IP component + +config ALTERA_PR_IP_CORE_PLAT + tristate "Platform support of Altera Partial Reconfiguration IP Core" + depends on ALTERA_PR_IP_CORE && OF && HAS_IOMEM help - FPGA manager driver support for Arria-V, Cyclone-V, Stratix-V - and Arria 10 Altera FPGAs using the CvP interface over PCIe. + Platform driver support for Altera Partial Reconfiguration IP + component config FPGA_MGR_ALTERA_PS_SPI tristate "Altera FPGA Passive Serial over SPI" @@ -38,25 +43,19 @@ config FPGA_MGR_ALTERA_PS_SPI FPGA manager driver support for Altera Arria/Cyclone/Stratix using the passive serial interface over SPI. -config FPGA_MGR_SOCFPGA - tristate "Altera SOCFPGA FPGA Manager" - depends on ARCH_SOCFPGA || COMPILE_TEST - help - FPGA manager driver support for Altera SOCFPGA. - -config FPGA_MGR_SOCFPGA_A10 - tristate "Altera SoCFPGA Arria10" - depends on ARCH_SOCFPGA || COMPILE_TEST - select REGMAP_MMIO +config FPGA_MGR_ALTERA_CVP + tristate "Altera Arria-V/Cyclone-V/Stratix-V CvP FPGA Manager" + depends on PCI help - FPGA manager driver support for Altera Arria10 SoCFPGA. + FPGA manager driver support for Arria-V, Cyclone-V, Stratix-V + and Arria 10 Altera FPGAs using the CvP interface over PCIe. -config FPGA_MGR_TS73XX - tristate "Technologic Systems TS-73xx SBC FPGA Manager" - depends on ARCH_EP93XX && MACH_TS72XX +config FPGA_MGR_ZYNQ_FPGA + tristate "Xilinx Zynq FPGA" + depends on ARCH_ZYNQ || COMPILE_TEST + depends on HAS_DMA help - FPGA manager driver support for the Altera Cyclone II FPGA - present on the TS-73xx SBC boards. + FPGA manager driver support for Xilinx Zynq FPGAs. config FPGA_MGR_XILINX_SPI tristate "Xilinx Configuration over Slave Serial (SPI)" @@ -65,16 +64,21 @@ config FPGA_MGR_XILINX_SPI FPGA manager driver support for Xilinx FPGA configuration over slave serial interface. -config FPGA_MGR_ZYNQ_FPGA - tristate "Xilinx Zynq FPGA" - depends on ARCH_ZYNQ || COMPILE_TEST - depends on HAS_DMA +config FPGA_MGR_ICE40_SPI + tristate "Lattice iCE40 SPI" + depends on OF && SPI help - FPGA manager driver support for Xilinx Zynq FPGAs. + FPGA manager driver support for Lattice iCE40 FPGAs over SPI. + +config FPGA_MGR_TS73XX + tristate "Technologic Systems TS-73xx SBC FPGA Manager" + depends on ARCH_EP93XX && MACH_TS72XX + help + FPGA manager driver support for the Altera Cyclone II FPGA + present on the TS-73xx SBC boards. config FPGA_BRIDGE tristate "FPGA Bridge Framework" - depends on OF help Say Y here if you want to support bridges connected between host processors and FPGAs or between FPGAs. @@ -95,18 +99,6 @@ config ALTERA_FREEZE_BRIDGE isolate one region of the FPGA from the busses while that region is being reprogrammed. -config ALTERA_PR_IP_CORE - tristate "Altera Partial Reconfiguration IP Core" - help - Core driver support for Altera Partial Reconfiguration IP component - -config ALTERA_PR_IP_CORE_PLAT - tristate "Platform support of Altera Partial Reconfiguration IP Core" - depends on ALTERA_PR_IP_CORE && OF && HAS_IOMEM - help - Platform driver support for Altera Partial Reconfiguration IP - component - config XILINX_PR_DECOUPLER tristate "Xilinx LogiCORE PR Decoupler" depends on FPGA_BRIDGE @@ -117,4 +109,19 @@ config XILINX_PR_DECOUPLER region of the FPGA from the busses while that region is being reprogrammed during partial reconfig. +config FPGA_REGION + tristate "FPGA Region" + depends on FPGA_BRIDGE + help + FPGA Region common code. A FPGA Region controls a FPGA Manager + and the FPGA Bridges associated with either a reconfigurable + region of an FPGA or a whole FPGA. + +config OF_FPGA_REGION + tristate "FPGA Region Device Tree Overlay Support" + depends on OF && FPGA_REGION + help + Support for loading FPGA images by applying a Device Tree + overlay. + endif # FPGA diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index f98dcf1d89e1..3cb276a0f88d 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -26,3 +26,4 @@ obj-$(CONFIG_XILINX_PR_DECOUPLER) += xilinx-pr-decoupler.o # High Level Interfaces obj-$(CONFIG_FPGA_REGION) += fpga-region.o +obj-$(CONFIG_OF_FPGA_REGION) += of-fpga-region.o diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 9651aa56244a..31bd2c59c305 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -2,6 +2,7 @@ * FPGA Bridge Framework Driver * * Copyright (C) 2013-2016 Altera Corporation, All Rights Reserved. + * Copyright (C) 2017 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, @@ -70,32 +71,13 @@ int fpga_bridge_disable(struct fpga_bridge *bridge) } EXPORT_SYMBOL_GPL(fpga_bridge_disable); -/** - * of_fpga_bridge_get - get an exclusive reference to a fpga bridge - * - * @np: node pointer of a FPGA bridge - * @info: fpga image specific information - * - * Return fpga_bridge struct if successful. - * Return -EBUSY if someone already has a reference to the bridge. - * Return -ENODEV if @np is not a FPGA Bridge. - */ -struct fpga_bridge *of_fpga_bridge_get(struct device_node *np, - struct fpga_image_info *info) - +static struct fpga_bridge *__fpga_bridge_get(struct device *dev, + struct fpga_image_info *info) { - struct device *dev; struct fpga_bridge *bridge; int ret = -ENODEV; - dev = class_find_device(fpga_bridge_class, NULL, np, - fpga_bridge_of_node_match); - if (!dev) - goto err_dev; - bridge = to_fpga_bridge(dev); - if (!bridge) - goto err_dev; bridge->info = info; @@ -117,8 +99,58 @@ err_dev: put_device(dev); return ERR_PTR(ret); } + +/** + * of_fpga_bridge_get - get an exclusive reference to a fpga bridge + * + * @np: node pointer of a FPGA bridge + * @info: fpga image specific information + * + * Return fpga_bridge struct if successful. + * Return -EBUSY if someone already has a reference to the bridge. + * Return -ENODEV if @np is not a FPGA Bridge. + */ +struct fpga_bridge *of_fpga_bridge_get(struct device_node *np, + struct fpga_image_info *info) +{ + struct device *dev; + + dev = class_find_device(fpga_bridge_class, NULL, np, + fpga_bridge_of_node_match); + if (!dev) + return ERR_PTR(-ENODEV); + + return __fpga_bridge_get(dev, info); +} EXPORT_SYMBOL_GPL(of_fpga_bridge_get); +static int fpga_bridge_dev_match(struct device *dev, const void *data) +{ + return dev->parent == data; +} + +/** + * fpga_bridge_get - get an exclusive reference to a fpga bridge + * @dev: parent device that fpga bridge was registered with + * + * Given a device, get an exclusive reference to a fpga bridge. + * + * Return: fpga manager struct or IS_ERR() condition containing error code. + */ +struct fpga_bridge *fpga_bridge_get(struct device *dev, + struct fpga_image_info *info) +{ + struct device *bridge_dev; + + bridge_dev = class_find_device(fpga_bridge_class, NULL, dev, + fpga_bridge_dev_match); + if (!bridge_dev) + return ERR_PTR(-ENODEV); + + return __fpga_bridge_get(bridge_dev, info); +} +EXPORT_SYMBOL_GPL(fpga_bridge_get); + /** * fpga_bridge_put - release a reference to a bridge * @@ -206,7 +238,7 @@ void fpga_bridges_put(struct list_head *bridge_list) EXPORT_SYMBOL_GPL(fpga_bridges_put); /** - * fpga_bridges_get_to_list - get a bridge, add it to a list + * of_fpga_bridge_get_to_list - get a bridge, add it to a list * * @np: node pointer of a FPGA bridge * @info: fpga image specific information @@ -216,14 +248,44 @@ EXPORT_SYMBOL_GPL(fpga_bridges_put); * * Return 0 for success, error code from of_fpga_bridge_get() othewise. */ -int fpga_bridge_get_to_list(struct device_node *np, +int of_fpga_bridge_get_to_list(struct device_node *np, + struct fpga_image_info *info, + struct list_head *bridge_list) +{ + struct fpga_bridge *bridge; + unsigned long flags; + + bridge = of_fpga_bridge_get(np, info); + if (IS_ERR(bridge)) + return PTR_ERR(bridge); + + spin_lock_irqsave(&bridge_list_lock, flags); + list_add(&bridge->node, bridge_list); + spin_unlock_irqrestore(&bridge_list_lock, flags); + + return 0; +} +EXPORT_SYMBOL_GPL(of_fpga_bridge_get_to_list); + +/** + * fpga_bridge_get_to_list - given device, get a bridge, add it to a list + * + * @dev: FPGA bridge device + * @info: fpga image specific information + * @bridge_list: list of FPGA bridges + * + * Get an exclusive reference to the bridge and and it to the list. + * + * Return 0 for success, error code from fpga_bridge_get() othewise. + */ +int fpga_bridge_get_to_list(struct device *dev, struct fpga_image_info *info, struct list_head *bridge_list) { struct fpga_bridge *bridge; unsigned long flags; - bridge = of_fpga_bridge_get(np, info); + bridge = fpga_bridge_get(dev, info); if (IS_ERR(bridge)) return PTR_ERR(bridge); @@ -303,6 +365,7 @@ int fpga_bridge_register(struct device *dev, const char *name, bridge->priv = priv; device_initialize(&bridge->dev); + bridge->dev.groups = br_ops->groups; bridge->dev.class = fpga_bridge_class; bridge->dev.parent = dev; bridge->dev.of_node = dev->of_node; @@ -381,7 +444,7 @@ static void __exit fpga_bridge_dev_exit(void) } MODULE_DESCRIPTION("FPGA Bridge Driver"); -MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); +MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); MODULE_LICENSE("GPL v2"); subsys_initcall(fpga_bridge_dev_init); diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index 188ffefa3cc3..9939d2cbc9a6 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -2,6 +2,7 @@ * FPGA Manager Core * * Copyright (C) 2013-2015 Altera Corporation + * Copyright (C) 2017 Intel Corporation * * With code from the mailing list: * Copyright (C) 2013 Xilinx, Inc. @@ -31,6 +32,40 @@ static DEFINE_IDA(fpga_mgr_ida); static struct class *fpga_mgr_class; +struct fpga_image_info *fpga_image_info_alloc(struct device *dev) +{ + struct fpga_image_info *info; + + get_device(dev); + + info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); + if (!info) { + put_device(dev); + return NULL; + } + + info->dev = dev; + + return info; +} +EXPORT_SYMBOL_GPL(fpga_image_info_alloc); + +void fpga_image_info_free(struct fpga_image_info *info) +{ + struct device *dev; + + if (!info) + return; + + dev = info->dev; + if (info->firmware_name) + devm_kfree(dev, info->firmware_name); + + devm_kfree(dev, info); + put_device(dev); +} +EXPORT_SYMBOL_GPL(fpga_image_info_free); + /* * Call the low level driver's write_init function. This will do the * device-specific things to get the FPGA into the state where it is ready to @@ -137,8 +172,9 @@ static int fpga_mgr_write_complete(struct fpga_manager *mgr, * * Return: 0 on success, negative error code otherwise. */ -int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info, - struct sg_table *sgt) +static int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, + struct fpga_image_info *info, + struct sg_table *sgt) { int ret; @@ -170,7 +206,6 @@ int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info, return fpga_mgr_write_complete(mgr, info); } -EXPORT_SYMBOL_GPL(fpga_mgr_buf_load_sg); static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr, struct fpga_image_info *info, @@ -210,8 +245,9 @@ static int fpga_mgr_buf_load_mapped(struct fpga_manager *mgr, * * Return: 0 on success, negative error code otherwise. */ -int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info, - const char *buf, size_t count) +static int fpga_mgr_buf_load(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t count) { struct page **pages; struct sg_table sgt; @@ -266,7 +302,6 @@ int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info, return rc; } -EXPORT_SYMBOL_GPL(fpga_mgr_buf_load); /** * fpga_mgr_firmware_load - request firmware and load to fpga @@ -282,9 +317,9 @@ EXPORT_SYMBOL_GPL(fpga_mgr_buf_load); * * Return: 0 on success, negative error code otherwise. */ -int fpga_mgr_firmware_load(struct fpga_manager *mgr, - struct fpga_image_info *info, - const char *image_name) +static int fpga_mgr_firmware_load(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *image_name) { struct device *dev = &mgr->dev; const struct firmware *fw; @@ -307,7 +342,18 @@ int fpga_mgr_firmware_load(struct fpga_manager *mgr, return ret; } -EXPORT_SYMBOL_GPL(fpga_mgr_firmware_load); + +int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info) +{ + if (info->sgt) + return fpga_mgr_buf_load_sg(mgr, info, info->sgt); + if (info->buf && info->count) + return fpga_mgr_buf_load(mgr, info, info->buf, info->count); + if (info->firmware_name) + return fpga_mgr_firmware_load(mgr, info, info->firmware_name); + return -EINVAL; +} +EXPORT_SYMBOL_GPL(fpga_mgr_load); static const char * const state_str[] = { [FPGA_MGR_STATE_UNKNOWN] = "unknown", @@ -364,28 +410,17 @@ ATTRIBUTE_GROUPS(fpga_mgr); static struct fpga_manager *__fpga_mgr_get(struct device *dev) { struct fpga_manager *mgr; - int ret = -ENODEV; mgr = to_fpga_manager(dev); - if (!mgr) - goto err_dev; - - /* Get exclusive use of fpga manager */ - if (!mutex_trylock(&mgr->ref_mutex)) { - ret = -EBUSY; - goto err_dev; - } if (!try_module_get(dev->parent->driver->owner)) - goto err_ll_mod; + goto err_dev; return mgr; -err_ll_mod: - mutex_unlock(&mgr->ref_mutex); err_dev: put_device(dev); - return ERR_PTR(ret); + return ERR_PTR(-ENODEV); } static int fpga_mgr_dev_match(struct device *dev, const void *data) @@ -394,10 +429,10 @@ static int fpga_mgr_dev_match(struct device *dev, const void *data) } /** - * fpga_mgr_get - get an exclusive reference to a fpga mgr + * fpga_mgr_get - get a reference to a fpga mgr * @dev: parent device that fpga mgr was registered with * - * Given a device, get an exclusive reference to a fpga mgr. + * Given a device, get a reference to a fpga mgr. * * Return: fpga manager struct or IS_ERR() condition containing error code. */ @@ -418,10 +453,10 @@ static int fpga_mgr_of_node_match(struct device *dev, const void *data) } /** - * of_fpga_mgr_get - get an exclusive reference to a fpga mgr + * of_fpga_mgr_get - get a reference to a fpga mgr * @node: device node * - * Given a device node, get an exclusive reference to a fpga mgr. + * Given a device node, get a reference to a fpga mgr. * * Return: fpga manager struct or IS_ERR() condition containing error code. */ @@ -445,12 +480,41 @@ EXPORT_SYMBOL_GPL(of_fpga_mgr_get); void fpga_mgr_put(struct fpga_manager *mgr) { module_put(mgr->dev.parent->driver->owner); - mutex_unlock(&mgr->ref_mutex); put_device(&mgr->dev); } EXPORT_SYMBOL_GPL(fpga_mgr_put); /** + * fpga_mgr_lock - Lock FPGA manager for exclusive use + * @mgr: fpga manager + * + * Given a pointer to FPGA Manager (from fpga_mgr_get() or + * of_fpga_mgr_put()) attempt to get the mutex. + * + * Return: 0 for success or -EBUSY + */ +int fpga_mgr_lock(struct fpga_manager *mgr) +{ + if (!mutex_trylock(&mgr->ref_mutex)) { + dev_err(&mgr->dev, "FPGA manager is in use.\n"); + return -EBUSY; + } + + return 0; +} +EXPORT_SYMBOL_GPL(fpga_mgr_lock); + +/** + * fpga_mgr_unlock - Unlock FPGA manager + * @mgr: fpga manager + */ +void fpga_mgr_unlock(struct fpga_manager *mgr) +{ + mutex_unlock(&mgr->ref_mutex); +} +EXPORT_SYMBOL_GPL(fpga_mgr_unlock); + +/** * fpga_mgr_register - register a low level fpga manager driver * @dev: fpga manager device from pdev * @name: fpga manager name @@ -503,6 +567,7 @@ int fpga_mgr_register(struct device *dev, const char *name, device_initialize(&mgr->dev); mgr->dev.class = fpga_mgr_class; + mgr->dev.groups = mops->groups; mgr->dev.parent = dev; mgr->dev.of_node = dev->of_node; mgr->dev.id = id; @@ -578,7 +643,7 @@ static void __exit fpga_mgr_class_exit(void) ida_destroy(&fpga_mgr_ida); } -MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); +MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); MODULE_DESCRIPTION("FPGA manager framework"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c index d9ab7c75b14f..edab2a2e03ef 100644 --- a/drivers/fpga/fpga-region.c +++ b/drivers/fpga/fpga-region.c @@ -2,6 +2,7 @@ * FPGA Region - Device Tree support for FPGA programming under Linux * * Copyright (C) 2013-2016 Altera Corporation + * Copyright (C) 2017 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, @@ -18,61 +19,30 @@ #include <linux/fpga/fpga-bridge.h> #include <linux/fpga/fpga-mgr.h> +#include <linux/fpga/fpga-region.h> #include <linux/idr.h> #include <linux/kernel.h> #include <linux/list.h> #include <linux/module.h> -#include <linux/of_platform.h> #include <linux/slab.h> #include <linux/spinlock.h> -/** - * struct fpga_region - FPGA Region structure - * @dev: FPGA Region device - * @mutex: enforces exclusive reference to region - * @bridge_list: list of FPGA bridges specified in region - * @info: fpga image specific information - */ -struct fpga_region { - struct device dev; - struct mutex mutex; /* for exclusive reference to region */ - struct list_head bridge_list; - struct fpga_image_info *info; -}; - -#define to_fpga_region(d) container_of(d, struct fpga_region, dev) - static DEFINE_IDA(fpga_region_ida); static struct class *fpga_region_class; -static const struct of_device_id fpga_region_of_match[] = { - { .compatible = "fpga-region", }, - {}, -}; -MODULE_DEVICE_TABLE(of, fpga_region_of_match); - -static int fpga_region_of_node_match(struct device *dev, const void *data) -{ - return dev->of_node == data; -} - -/** - * fpga_region_find - find FPGA region - * @np: device node of FPGA Region - * Caller will need to put_device(®ion->dev) when done. - * Returns FPGA Region struct or NULL - */ -static struct fpga_region *fpga_region_find(struct device_node *np) +struct fpga_region *fpga_region_class_find( + struct device *start, const void *data, + int (*match)(struct device *, const void *)) { struct device *dev; - dev = class_find_device(fpga_region_class, NULL, np, - fpga_region_of_node_match); + dev = class_find_device(fpga_region_class, start, data, match); if (!dev) return NULL; return to_fpga_region(dev); } +EXPORT_SYMBOL_GPL(fpga_region_class_find); /** * fpga_region_get - get an exclusive reference to a fpga region @@ -94,15 +64,13 @@ static struct fpga_region *fpga_region_get(struct fpga_region *region) } get_device(dev); - of_node_get(dev->of_node); if (!try_module_get(dev->parent->driver->owner)) { - of_node_put(dev->of_node); put_device(dev); mutex_unlock(®ion->mutex); return ERR_PTR(-ENODEV); } - dev_dbg(®ion->dev, "get\n"); + dev_dbg(dev, "get\n"); return region; } @@ -116,403 +84,99 @@ static void fpga_region_put(struct fpga_region *region) { struct device *dev = ®ion->dev; - dev_dbg(®ion->dev, "put\n"); + dev_dbg(dev, "put\n"); module_put(dev->parent->driver->owner); - of_node_put(dev->of_node); put_device(dev); mutex_unlock(®ion->mutex); } /** - * fpga_region_get_manager - get exclusive reference for FPGA manager - * @region: FPGA region - * - * Get FPGA Manager from "fpga-mgr" property or from ancestor region. - * - * Caller should call fpga_mgr_put() when done with manager. - * - * Return: fpga manager struct or IS_ERR() condition containing error code. - */ -static struct fpga_manager *fpga_region_get_manager(struct fpga_region *region) -{ - struct device *dev = ®ion->dev; - struct device_node *np = dev->of_node; - struct device_node *mgr_node; - struct fpga_manager *mgr; - - of_node_get(np); - while (np) { - if (of_device_is_compatible(np, "fpga-region")) { - mgr_node = of_parse_phandle(np, "fpga-mgr", 0); - if (mgr_node) { - mgr = of_fpga_mgr_get(mgr_node); - of_node_put(np); - return mgr; - } - } - np = of_get_next_parent(np); - } - of_node_put(np); - - return ERR_PTR(-EINVAL); -} - -/** - * fpga_region_get_bridges - create a list of bridges - * @region: FPGA region - * @overlay: device node of the overlay - * - * Create a list of bridges including the parent bridge and the bridges - * specified by "fpga-bridges" property. Note that the - * fpga_bridges_enable/disable/put functions are all fine with an empty list - * if that happens. - * - * Caller should call fpga_bridges_put(®ion->bridge_list) when - * done with the bridges. - * - * Return 0 for success (even if there are no bridges specified) - * or -EBUSY if any of the bridges are in use. - */ -static int fpga_region_get_bridges(struct fpga_region *region, - struct device_node *overlay) -{ - struct device *dev = ®ion->dev; - struct device_node *region_np = dev->of_node; - struct device_node *br, *np, *parent_br = NULL; - int i, ret; - - /* If parent is a bridge, add to list */ - ret = fpga_bridge_get_to_list(region_np->parent, region->info, - ®ion->bridge_list); - if (ret == -EBUSY) - return ret; - - if (!ret) - parent_br = region_np->parent; - - /* If overlay has a list of bridges, use it. */ - if (of_parse_phandle(overlay, "fpga-bridges", 0)) - np = overlay; - else - np = region_np; - - for (i = 0; ; i++) { - br = of_parse_phandle(np, "fpga-bridges", i); - if (!br) - break; - - /* If parent bridge is in list, skip it. */ - if (br == parent_br) - continue; - - /* If node is a bridge, get it and add to list */ - ret = fpga_bridge_get_to_list(br, region->info, - ®ion->bridge_list); - - /* If any of the bridges are in use, give up */ - if (ret == -EBUSY) { - fpga_bridges_put(®ion->bridge_list); - return -EBUSY; - } - } - - return 0; -} - -/** * fpga_region_program_fpga - program FPGA * @region: FPGA region - * @firmware_name: name of FPGA image firmware file - * @overlay: device node of the overlay - * Program an FPGA using information in the device tree. - * Function assumes that there is a firmware-name property. + * Program an FPGA using fpga image info (region->info). * Return 0 for success or negative error code. */ -static int fpga_region_program_fpga(struct fpga_region *region, - const char *firmware_name, - struct device_node *overlay) +int fpga_region_program_fpga(struct fpga_region *region) { - struct fpga_manager *mgr; + struct device *dev = ®ion->dev; + struct fpga_image_info *info = region->info; int ret; region = fpga_region_get(region); if (IS_ERR(region)) { - pr_err("failed to get fpga region\n"); + dev_err(dev, "failed to get FPGA region\n"); return PTR_ERR(region); } - mgr = fpga_region_get_manager(region); - if (IS_ERR(mgr)) { - pr_err("failed to get fpga region manager\n"); - ret = PTR_ERR(mgr); + ret = fpga_mgr_lock(region->mgr); + if (ret) { + dev_err(dev, "FPGA manager is busy\n"); goto err_put_region; } - ret = fpga_region_get_bridges(region, overlay); - if (ret) { - pr_err("failed to get fpga region bridges\n"); - goto err_put_mgr; + /* + * In some cases, we already have a list of bridges in the + * fpga region struct. Or we don't have any bridges. + */ + if (region->get_bridges) { + ret = region->get_bridges(region); + if (ret) { + dev_err(dev, "failed to get fpga region bridges\n"); + goto err_unlock_mgr; + } } ret = fpga_bridges_disable(®ion->bridge_list); if (ret) { - pr_err("failed to disable region bridges\n"); + dev_err(dev, "failed to disable bridges\n"); goto err_put_br; } - ret = fpga_mgr_firmware_load(mgr, region->info, firmware_name); + ret = fpga_mgr_load(region->mgr, info); if (ret) { - pr_err("failed to load fpga image\n"); + dev_err(dev, "failed to load FPGA image\n"); goto err_put_br; } ret = fpga_bridges_enable(®ion->bridge_list); if (ret) { - pr_err("failed to enable region bridges\n"); + dev_err(dev, "failed to enable region bridges\n"); goto err_put_br; } - fpga_mgr_put(mgr); + fpga_mgr_unlock(region->mgr); fpga_region_put(region); return 0; err_put_br: - fpga_bridges_put(®ion->bridge_list); -err_put_mgr: - fpga_mgr_put(mgr); + if (region->get_bridges) + fpga_bridges_put(®ion->bridge_list); +err_unlock_mgr: + fpga_mgr_unlock(region->mgr); err_put_region: fpga_region_put(region); return ret; } +EXPORT_SYMBOL_GPL(fpga_region_program_fpga); -/** - * child_regions_with_firmware - * @overlay: device node of the overlay - * - * If the overlay adds child FPGA regions, they are not allowed to have - * firmware-name property. - * - * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. - */ -static int child_regions_with_firmware(struct device_node *overlay) -{ - struct device_node *child_region; - const char *child_firmware_name; - int ret = 0; - - of_node_get(overlay); - - child_region = of_find_matching_node(overlay, fpga_region_of_match); - while (child_region) { - if (!of_property_read_string(child_region, "firmware-name", - &child_firmware_name)) { - ret = -EINVAL; - break; - } - child_region = of_find_matching_node(child_region, - fpga_region_of_match); - } - - of_node_put(child_region); - - if (ret) - pr_err("firmware-name not allowed in child FPGA region: %pOF", - child_region); - - return ret; -} - -/** - * fpga_region_notify_pre_apply - pre-apply overlay notification - * - * @region: FPGA region that the overlay was applied to - * @nd: overlay notification data - * - * Called after when an overlay targeted to a FPGA Region is about to be - * applied. Function will check the properties that will be added to the FPGA - * region. If the checks pass, it will program the FPGA. - * - * The checks are: - * The overlay must add either firmware-name or external-fpga-config property - * to the FPGA Region. - * - * firmware-name : program the FPGA - * external-fpga-config : FPGA is already programmed - * encrypted-fpga-config : FPGA bitstream is encrypted - * - * The overlay can add other FPGA regions, but child FPGA regions cannot have a - * firmware-name property since those regions don't exist yet. - * - * If the overlay that breaks the rules, notifier returns an error and the - * overlay is rejected before it goes into the main tree. - * - * Returns 0 for success or negative error code for failure. - */ -static int fpga_region_notify_pre_apply(struct fpga_region *region, - struct of_overlay_notify_data *nd) +int fpga_region_register(struct device *dev, struct fpga_region *region) { - const char *firmware_name = NULL; - struct fpga_image_info *info; - int ret; - - info = devm_kzalloc(®ion->dev, sizeof(*info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - region->info = info; - - /* Reject overlay if child FPGA Regions have firmware-name property */ - ret = child_regions_with_firmware(nd->overlay); - if (ret) - return ret; - - /* Read FPGA region properties from the overlay */ - if (of_property_read_bool(nd->overlay, "partial-fpga-config")) - info->flags |= FPGA_MGR_PARTIAL_RECONFIG; - - if (of_property_read_bool(nd->overlay, "external-fpga-config")) - info->flags |= FPGA_MGR_EXTERNAL_CONFIG; - - if (of_property_read_bool(nd->overlay, "encrypted-fpga-config")) - info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM; - - of_property_read_string(nd->overlay, "firmware-name", &firmware_name); - - of_property_read_u32(nd->overlay, "region-unfreeze-timeout-us", - &info->enable_timeout_us); - - of_property_read_u32(nd->overlay, "region-freeze-timeout-us", - &info->disable_timeout_us); - - of_property_read_u32(nd->overlay, "config-complete-timeout-us", - &info->config_complete_timeout_us); - - /* If FPGA was externally programmed, don't specify firmware */ - if ((info->flags & FPGA_MGR_EXTERNAL_CONFIG) && firmware_name) { - pr_err("error: specified firmware and external-fpga-config"); - return -EINVAL; - } - - /* FPGA is already configured externally. We're done. */ - if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) - return 0; - - /* If we got this far, we should be programming the FPGA */ - if (!firmware_name) { - pr_err("should specify firmware-name or external-fpga-config\n"); - return -EINVAL; - } - - return fpga_region_program_fpga(region, firmware_name, nd->overlay); -} - -/** - * fpga_region_notify_post_remove - post-remove overlay notification - * - * @region: FPGA region that was targeted by the overlay that was removed - * @nd: overlay notification data - * - * Called after an overlay has been removed if the overlay's target was a - * FPGA region. - */ -static void fpga_region_notify_post_remove(struct fpga_region *region, - struct of_overlay_notify_data *nd) -{ - fpga_bridges_disable(®ion->bridge_list); - fpga_bridges_put(®ion->bridge_list); - devm_kfree(®ion->dev, region->info); - region->info = NULL; -} - -/** - * of_fpga_region_notify - reconfig notifier for dynamic DT changes - * @nb: notifier block - * @action: notifier action - * @arg: reconfig data - * - * This notifier handles programming a FPGA when a "firmware-name" property is - * added to a fpga-region. - * - * Returns NOTIFY_OK or error if FPGA programming fails. - */ -static int of_fpga_region_notify(struct notifier_block *nb, - unsigned long action, void *arg) -{ - struct of_overlay_notify_data *nd = arg; - struct fpga_region *region; - int ret; - - switch (action) { - case OF_OVERLAY_PRE_APPLY: - pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); - break; - case OF_OVERLAY_POST_APPLY: - pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); - return NOTIFY_OK; /* not for us */ - case OF_OVERLAY_PRE_REMOVE: - pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); - return NOTIFY_OK; /* not for us */ - case OF_OVERLAY_POST_REMOVE: - pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); - break; - default: /* should not happen */ - return NOTIFY_OK; - } - - region = fpga_region_find(nd->target); - if (!region) - return NOTIFY_OK; - - ret = 0; - switch (action) { - case OF_OVERLAY_PRE_APPLY: - ret = fpga_region_notify_pre_apply(region, nd); - break; - - case OF_OVERLAY_POST_REMOVE: - fpga_region_notify_post_remove(region, nd); - break; - } - - put_device(®ion->dev); - - if (ret) - return notifier_from_errno(ret); - - return NOTIFY_OK; -} - -static struct notifier_block fpga_region_of_nb = { - .notifier_call = of_fpga_region_notify, -}; - -static int fpga_region_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct fpga_region *region; int id, ret = 0; - region = kzalloc(sizeof(*region), GFP_KERNEL); - if (!region) - return -ENOMEM; - id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); - if (id < 0) { - ret = id; - goto err_kfree; - } + if (id < 0) + return id; mutex_init(®ion->mutex); INIT_LIST_HEAD(®ion->bridge_list); - device_initialize(®ion->dev); + region->dev.groups = region->groups; region->dev.class = fpga_region_class; region->dev.parent = dev; - region->dev.of_node = np; + region->dev.of_node = dev->of_node; region->dev.id = id; dev_set_drvdata(dev, region); @@ -524,44 +188,27 @@ static int fpga_region_probe(struct platform_device *pdev) if (ret) goto err_remove; - of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); - - dev_info(dev, "FPGA Region probed\n"); - return 0; err_remove: ida_simple_remove(&fpga_region_ida, id); -err_kfree: - kfree(region); - return ret; } +EXPORT_SYMBOL_GPL(fpga_region_register); -static int fpga_region_remove(struct platform_device *pdev) +int fpga_region_unregister(struct fpga_region *region) { - struct fpga_region *region = platform_get_drvdata(pdev); - device_unregister(®ion->dev); return 0; } - -static struct platform_driver fpga_region_driver = { - .probe = fpga_region_probe, - .remove = fpga_region_remove, - .driver = { - .name = "fpga-region", - .of_match_table = of_match_ptr(fpga_region_of_match), - }, -}; +EXPORT_SYMBOL_GPL(fpga_region_unregister); static void fpga_region_dev_release(struct device *dev) { struct fpga_region *region = to_fpga_region(dev); ida_simple_remove(&fpga_region_ida, region->dev.id); - kfree(region); } /** @@ -570,36 +217,17 @@ static void fpga_region_dev_release(struct device *dev) */ static int __init fpga_region_init(void) { - int ret; - fpga_region_class = class_create(THIS_MODULE, "fpga_region"); if (IS_ERR(fpga_region_class)) return PTR_ERR(fpga_region_class); fpga_region_class->dev_release = fpga_region_dev_release; - ret = of_overlay_notifier_register(&fpga_region_of_nb); - if (ret) - goto err_class; - - ret = platform_driver_register(&fpga_region_driver); - if (ret) - goto err_plat; - return 0; - -err_plat: - of_overlay_notifier_unregister(&fpga_region_of_nb); -err_class: - class_destroy(fpga_region_class); - ida_destroy(&fpga_region_ida); - return ret; } static void __exit fpga_region_exit(void) { - platform_driver_unregister(&fpga_region_driver); - of_overlay_notifier_unregister(&fpga_region_of_nb); class_destroy(fpga_region_class); ida_destroy(&fpga_region_ida); } @@ -608,5 +236,5 @@ subsys_initcall(fpga_region_init); module_exit(fpga_region_exit); MODULE_DESCRIPTION("FPGA Region"); -MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>"); +MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c new file mode 100644 index 000000000000..119ff75522f1 --- /dev/null +++ b/drivers/fpga/of-fpga-region.c @@ -0,0 +1,504 @@ +/* + * FPGA Region - Device Tree support for FPGA programming under Linux + * + * Copyright (C) 2013-2016 Altera Corporation + * Copyright (C) 2017 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/fpga/fpga-bridge.h> +#include <linux/fpga/fpga-mgr.h> +#include <linux/fpga/fpga-region.h> +#include <linux/idr.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/of_platform.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +static const struct of_device_id fpga_region_of_match[] = { + { .compatible = "fpga-region", }, + {}, +}; +MODULE_DEVICE_TABLE(of, fpga_region_of_match); + +static int fpga_region_of_node_match(struct device *dev, const void *data) +{ + return dev->of_node == data; +} + +/** + * of_fpga_region_find - find FPGA region + * @np: device node of FPGA Region + * + * Caller will need to put_device(®ion->dev) when done. + * + * Returns FPGA Region struct or NULL + */ +static struct fpga_region *of_fpga_region_find(struct device_node *np) +{ + return fpga_region_class_find(NULL, np, fpga_region_of_node_match); +} + +/** + * of_fpga_region_get_mgr - get reference for FPGA manager + * @np: device node of FPGA region + * + * Get FPGA Manager from "fpga-mgr" property or from ancestor region. + * + * Caller should call fpga_mgr_put() when done with manager. + * + * Return: fpga manager struct or IS_ERR() condition containing error code. + */ +static struct fpga_manager *of_fpga_region_get_mgr(struct device_node *np) +{ + struct device_node *mgr_node; + struct fpga_manager *mgr; + + of_node_get(np); + while (np) { + if (of_device_is_compatible(np, "fpga-region")) { + mgr_node = of_parse_phandle(np, "fpga-mgr", 0); + if (mgr_node) { + mgr = of_fpga_mgr_get(mgr_node); + of_node_put(mgr_node); + of_node_put(np); + return mgr; + } + } + np = of_get_next_parent(np); + } + of_node_put(np); + + return ERR_PTR(-EINVAL); +} + +/** + * of_fpga_region_get_bridges - create a list of bridges + * @region: FPGA region + * + * Create a list of bridges including the parent bridge and the bridges + * specified by "fpga-bridges" property. Note that the + * fpga_bridges_enable/disable/put functions are all fine with an empty list + * if that happens. + * + * Caller should call fpga_bridges_put(®ion->bridge_list) when + * done with the bridges. + * + * Return 0 for success (even if there are no bridges specified) + * or -EBUSY if any of the bridges are in use. + */ +static int of_fpga_region_get_bridges(struct fpga_region *region) +{ + struct device *dev = ®ion->dev; + struct device_node *region_np = dev->of_node; + struct fpga_image_info *info = region->info; + struct device_node *br, *np, *parent_br = NULL; + int i, ret; + + /* If parent is a bridge, add to list */ + ret = of_fpga_bridge_get_to_list(region_np->parent, info, + ®ion->bridge_list); + + /* -EBUSY means parent is a bridge that is under use. Give up. */ + if (ret == -EBUSY) + return ret; + + /* Zero return code means parent was a bridge and was added to list. */ + if (!ret) + parent_br = region_np->parent; + + /* If overlay has a list of bridges, use it. */ + br = of_parse_phandle(info->overlay, "fpga-bridges", 0); + if (br) { + of_node_put(br); + np = info->overlay; + } else { + np = region_np; + } + + for (i = 0; ; i++) { + br = of_parse_phandle(np, "fpga-bridges", i); + if (!br) + break; + + /* If parent bridge is in list, skip it. */ + if (br == parent_br) { + of_node_put(br); + continue; + } + + /* If node is a bridge, get it and add to list */ + ret = of_fpga_bridge_get_to_list(br, info, + ®ion->bridge_list); + of_node_put(br); + + /* If any of the bridges are in use, give up */ + if (ret == -EBUSY) { + fpga_bridges_put(®ion->bridge_list); + return -EBUSY; + } + } + + return 0; +} + +/** + * child_regions_with_firmware + * @overlay: device node of the overlay + * + * If the overlay adds child FPGA regions, they are not allowed to have + * firmware-name property. + * + * Return 0 for OK or -EINVAL if child FPGA region adds firmware-name. + */ +static int child_regions_with_firmware(struct device_node *overlay) +{ + struct device_node *child_region; + const char *child_firmware_name; + int ret = 0; + + of_node_get(overlay); + + child_region = of_find_matching_node(overlay, fpga_region_of_match); + while (child_region) { + if (!of_property_read_string(child_region, "firmware-name", + &child_firmware_name)) { + ret = -EINVAL; + break; + } + child_region = of_find_matching_node(child_region, + fpga_region_of_match); + } + + of_node_put(child_region); + + if (ret) + pr_err("firmware-name not allowed in child FPGA region: %pOF", + child_region); + + return ret; +} + +/** + * of_fpga_region_parse_ov - parse and check overlay applied to region + * + * @region: FPGA region + * @overlay: overlay applied to the FPGA region + * + * Given an overlay applied to a FPGA region, parse the FPGA image specific + * info in the overlay and do some checking. + * + * Returns: + * NULL if overlay doesn't direct us to program the FPGA. + * fpga_image_info struct if there is an image to program. + * error code for invalid overlay. + */ +static struct fpga_image_info *of_fpga_region_parse_ov( + struct fpga_region *region, + struct device_node *overlay) +{ + struct device *dev = ®ion->dev; + struct fpga_image_info *info; + const char *firmware_name; + int ret; + + if (region->info) { + dev_err(dev, "Region already has overlay applied.\n"); + return ERR_PTR(-EINVAL); + } + + /* + * Reject overlay if child FPGA Regions added in the overlay have + * firmware-name property (would mean that an FPGA region that has + * not been added to the live tree yet is doing FPGA programming). + */ + ret = child_regions_with_firmware(overlay); + if (ret) + return ERR_PTR(ret); + + info = fpga_image_info_alloc(dev); + if (!info) + return ERR_PTR(-ENOMEM); + + info->overlay = overlay; + + /* Read FPGA region properties from the overlay */ + if (of_property_read_bool(overlay, "partial-fpga-config")) + info->flags |= FPGA_MGR_PARTIAL_RECONFIG; + + if (of_property_read_bool(overlay, "external-fpga-config")) + info->flags |= FPGA_MGR_EXTERNAL_CONFIG; + + if (of_property_read_bool(overlay, "encrypted-fpga-config")) + info->flags |= FPGA_MGR_ENCRYPTED_BITSTREAM; + + if (!of_property_read_string(overlay, "firmware-name", + &firmware_name)) { + info->firmware_name = devm_kstrdup(dev, firmware_name, + GFP_KERNEL); + if (!info->firmware_name) + return ERR_PTR(-ENOMEM); + } + + of_property_read_u32(overlay, "region-unfreeze-timeout-us", + &info->enable_timeout_us); + + of_property_read_u32(overlay, "region-freeze-timeout-us", + &info->disable_timeout_us); + + of_property_read_u32(overlay, "config-complete-timeout-us", + &info->config_complete_timeout_us); + + /* If overlay is not programming the FPGA, don't need FPGA image info */ + if (!info->firmware_name) { + ret = 0; + goto ret_no_info; + } + + /* + * If overlay informs us FPGA was externally programmed, specifying + * firmware here would be ambiguous. + */ + if (info->flags & FPGA_MGR_EXTERNAL_CONFIG) { + dev_err(dev, "error: specified firmware and external-fpga-config"); + ret = -EINVAL; + goto ret_no_info; + } + + return info; +ret_no_info: + fpga_image_info_free(info); + return ERR_PTR(ret); +} + +/** + * of_fpga_region_notify_pre_apply - pre-apply overlay notification + * + * @region: FPGA region that the overlay was applied to + * @nd: overlay notification data + * + * Called when an overlay targeted to a FPGA Region is about to be applied. + * Parses the overlay for properties that influence how the FPGA will be + * programmed and does some checking. If the checks pass, programs the FPGA. + * If the checks fail, overlay is rejected and does not get added to the + * live tree. + * + * Returns 0 for success or negative error code for failure. + */ +static int of_fpga_region_notify_pre_apply(struct fpga_region *region, + struct of_overlay_notify_data *nd) +{ + struct device *dev = ®ion->dev; + struct fpga_image_info *info; + int ret; + + info = of_fpga_region_parse_ov(region, nd->overlay); + if (IS_ERR(info)) + return PTR_ERR(info); + + /* If overlay doesn't program the FPGA, accept it anyway. */ + if (!info) + return 0; + + if (region->info) { + dev_err(dev, "Region already has overlay applied.\n"); + return -EINVAL; + } + + region->info = info; + ret = fpga_region_program_fpga(region); + if (ret) { + /* error; reject overlay */ + fpga_image_info_free(info); + region->info = NULL; + } + + return ret; +} + +/** + * of_fpga_region_notify_post_remove - post-remove overlay notification + * + * @region: FPGA region that was targeted by the overlay that was removed + * @nd: overlay notification data + * + * Called after an overlay has been removed if the overlay's target was a + * FPGA region. + */ +static void of_fpga_region_notify_post_remove(struct fpga_region *region, + struct of_overlay_notify_data *nd) +{ + fpga_bridges_disable(®ion->bridge_list); + fpga_bridges_put(®ion->bridge_list); + fpga_image_info_free(region->info); + region->info = NULL; +} + +/** + * of_fpga_region_notify - reconfig notifier for dynamic DT changes + * @nb: notifier block + * @action: notifier action + * @arg: reconfig data + * + * This notifier handles programming a FPGA when a "firmware-name" property is + * added to a fpga-region. + * + * Returns NOTIFY_OK or error if FPGA programming fails. + */ +static int of_fpga_region_notify(struct notifier_block *nb, + unsigned long action, void *arg) +{ + struct of_overlay_notify_data *nd = arg; + struct fpga_region *region; + int ret; + + switch (action) { + case OF_OVERLAY_PRE_APPLY: + pr_debug("%s OF_OVERLAY_PRE_APPLY\n", __func__); + break; + case OF_OVERLAY_POST_APPLY: + pr_debug("%s OF_OVERLAY_POST_APPLY\n", __func__); + return NOTIFY_OK; /* not for us */ + case OF_OVERLAY_PRE_REMOVE: + pr_debug("%s OF_OVERLAY_PRE_REMOVE\n", __func__); + return NOTIFY_OK; /* not for us */ + case OF_OVERLAY_POST_REMOVE: + pr_debug("%s OF_OVERLAY_POST_REMOVE\n", __func__); + break; + default: /* should not happen */ + return NOTIFY_OK; + } + + region = of_fpga_region_find(nd->target); + if (!region) + return NOTIFY_OK; + + ret = 0; + switch (action) { + case OF_OVERLAY_PRE_APPLY: + ret = of_fpga_region_notify_pre_apply(region, nd); + break; + + case OF_OVERLAY_POST_REMOVE: + of_fpga_region_notify_post_remove(region, nd); + break; + } + + put_device(®ion->dev); + + if (ret) + return notifier_from_errno(ret); + + return NOTIFY_OK; +} + +static struct notifier_block fpga_region_of_nb = { + .notifier_call = of_fpga_region_notify, +}; + +static int of_fpga_region_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct fpga_region *region; + struct fpga_manager *mgr; + int ret; + + /* Find the FPGA mgr specified by region or parent region. */ + mgr = of_fpga_region_get_mgr(np); + if (IS_ERR(mgr)) + return -EPROBE_DEFER; + + region = devm_kzalloc(dev, sizeof(*region), GFP_KERNEL); + if (!region) { + ret = -ENOMEM; + goto eprobe_mgr_put; + } + + region->mgr = mgr; + + /* Specify how to get bridges for this type of region. */ + region->get_bridges = of_fpga_region_get_bridges; + + ret = fpga_region_register(dev, region); + if (ret) + goto eprobe_mgr_put; + + of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); + + dev_info(dev, "FPGA Region probed\n"); + + return 0; + +eprobe_mgr_put: + fpga_mgr_put(mgr); + return ret; +} + +static int of_fpga_region_remove(struct platform_device *pdev) +{ + struct fpga_region *region = platform_get_drvdata(pdev); + + fpga_region_unregister(region); + fpga_mgr_put(region->mgr); + + return 0; +} + +static struct platform_driver of_fpga_region_driver = { + .probe = of_fpga_region_probe, + .remove = of_fpga_region_remove, + .driver = { + .name = "of-fpga-region", + .of_match_table = of_match_ptr(fpga_region_of_match), + }, +}; + +/** + * fpga_region_init - init function for fpga_region class + * Creates the fpga_region class and registers a reconfig notifier. + */ +static int __init of_fpga_region_init(void) +{ + int ret; + + ret = of_overlay_notifier_register(&fpga_region_of_nb); + if (ret) + return ret; + + ret = platform_driver_register(&of_fpga_region_driver); + if (ret) + goto err_plat; + + return 0; + +err_plat: + of_overlay_notifier_unregister(&fpga_region_of_nb); + return ret; +} + +static void __exit of_fpga_region_exit(void) +{ + platform_driver_unregister(&of_fpga_region_driver); + of_overlay_notifier_unregister(&fpga_region_of_nb); +} + +subsys_initcall(of_fpga_region_init); +module_exit(of_fpga_region_exit); + +MODULE_DESCRIPTION("FPGA Region"); +MODULE_AUTHOR("Alan Tull <atull@kernel.org>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig index 6821ed0cd5e8..513e35173aaa 100644 --- a/drivers/fsi/Kconfig +++ b/drivers/fsi/Kconfig @@ -2,9 +2,7 @@ # FSI subsystem # -menu "FSI support" - -config FSI +menuconfig FSI tristate "FSI support" select CRC4 ---help--- @@ -34,5 +32,3 @@ config FSI_SCOM This option enables an FSI based SCOM device driver. endif - -endmenu diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index eb29113e0bac..5a17bfeb80d3 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -468,7 +468,7 @@ static struct class enclosure_class = { .dev_groups = enclosure_class_groups, }; -static const char *const enclosure_status [] = { +static const char *const enclosure_status[] = { [ENCLOSURE_STATUS_UNSUPPORTED] = "unsupported", [ENCLOSURE_STATUS_OK] = "OK", [ENCLOSURE_STATUS_CRITICAL] = "critical", @@ -480,7 +480,7 @@ static const char *const enclosure_status [] = { [ENCLOSURE_STATUS_MAX] = NULL, }; -static const char *const enclosure_type [] = { +static const char *const enclosure_type[] = { [ENCLOSURE_COMPONENT_DEVICE] = "device", [ENCLOSURE_COMPONENT_ARRAY_DEVICE] = "array device", }; @@ -680,13 +680,7 @@ ATTRIBUTE_GROUPS(enclosure_component); static int __init enclosure_init(void) { - int err; - - err = class_register(&enclosure_class); - if (err) - return err; - - return 0; + return class_register(&enclosure_class); } static void __exit enclosure_exit(void) diff --git a/drivers/misc/hpilo.c b/drivers/misc/hpilo.c index 097e3092c158..cfdf0524b12e 100644 --- a/drivers/misc/hpilo.c +++ b/drivers/misc/hpilo.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Driver for the HP iLO management processor. * * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. * David Altobelli <david.altobelli@hpe.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/kernel.h> #include <linux/types.h> diff --git a/drivers/misc/hpilo.h b/drivers/misc/hpilo.h index b97672e0cf90..94dfb9e40e29 100644 --- a/drivers/misc/hpilo.h +++ b/drivers/misc/hpilo.h @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * linux/drivers/char/hpilo.h * * Copyright (C) 2008 Hewlett-Packard Development Company, L.P. * David Altobelli <david.altobelli@hp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __HPILO_H #define __HPILO_H diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 976df0013633..b8032882c865 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -78,6 +78,7 @@ static int __isl29003_read_reg(struct i2c_client *client, u32 reg, u8 mask, u8 shift) { struct isl29003_data *data = i2c_get_clientdata(client); + return (data->reg_cache[reg] & mask) >> shift; } @@ -160,6 +161,7 @@ static int isl29003_get_power_state(struct i2c_client *client) { struct isl29003_data *data = i2c_get_clientdata(client); u8 cmdreg = data->reg_cache[ISL29003_REG_COMMAND]; + return ~cmdreg & ISL29003_ADC_PD; } @@ -196,6 +198,7 @@ static ssize_t isl29003_show_range(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%i\n", isl29003_get_range(client)); } @@ -231,6 +234,7 @@ static ssize_t isl29003_show_resolution(struct device *dev, char *buf) { struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_resolution(client)); } @@ -264,6 +268,7 @@ static ssize_t isl29003_show_mode(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_mode(client)); } @@ -298,6 +303,7 @@ static ssize_t isl29003_show_power_state(struct device *dev, char *buf) { struct i2c_client *client = to_i2c_client(dev); + return sprintf(buf, "%d\n", isl29003_get_power_state(client)); } @@ -361,6 +367,7 @@ static int isl29003_init_client(struct i2c_client *client) * if one of the reads fails, we consider the init failed */ for (i = 0; i < ARRAY_SIZE(data->reg_cache); i++) { int v = i2c_smbus_read_byte_data(client, i); + if (v < 0) return -ENODEV; diff --git a/drivers/misc/lkdtm_heap.c b/drivers/misc/lkdtm_heap.c index f5494a6d4be5..65026d7de130 100644 --- a/drivers/misc/lkdtm_heap.c +++ b/drivers/misc/lkdtm_heap.c @@ -16,6 +16,8 @@ void lkdtm_OVERWRITE_ALLOCATION(void) { size_t len = 1020; u32 *data = kmalloc(len, GFP_KERNEL); + if (!data) + return; data[1024 / sizeof(u32)] = 0x12345678; kfree(data); @@ -33,6 +35,8 @@ void lkdtm_WRITE_AFTER_FREE(void) size_t offset = (len / sizeof(*base)) / 2; base = kmalloc(len, GFP_KERNEL); + if (!base) + return; pr_info("Allocated memory %p-%p\n", base, &base[offset * 2]); pr_info("Attempting bad write to freed memory at %p\n", &base[offset]); diff --git a/drivers/misc/mic/vop/vop_vringh.c b/drivers/misc/mic/vop/vop_vringh.c index fed992e2c258..bde0305ebd91 100644 --- a/drivers/misc/mic/vop/vop_vringh.c +++ b/drivers/misc/mic/vop/vop_vringh.c @@ -937,13 +937,10 @@ static long vop_ioctl(struct file *f, unsigned int cmd, unsigned long arg) dd.num_vq > MIC_MAX_VRINGS) return -EINVAL; - dd_config = kzalloc(mic_desc_size(&dd), GFP_KERNEL); - if (!dd_config) - return -ENOMEM; - if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) { - ret = -EFAULT; - goto free_ret; - } + dd_config = memdup_user(argp, mic_desc_size(&dd)); + if (IS_ERR(dd_config)) + return PTR_ERR(dd_config); + /* Ensure desc has not changed between the two reads */ if (memcmp(&dd, dd_config, sizeof(dd))) { ret = -EINVAL; @@ -995,15 +992,11 @@ _unlock_ret: ret = vop_vdev_inited(vdev); if (ret) goto __unlock_ret; - buf = kzalloc(vdev->dd->config_len, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; + buf = memdup_user(argp, vdev->dd->config_len); + if (IS_ERR(buf)) { + ret = PTR_ERR(buf); goto __unlock_ret; } - if (copy_from_user(buf, argp, vdev->dd->config_len)) { - ret = -EFAULT; - goto done; - } ret = vop_virtio_config_change(vdev, buf); done: kfree(buf); diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c index 2cde80c7bb93..9eea30f54fd6 100644 --- a/drivers/misc/vexpress-syscfg.c +++ b/drivers/misc/vexpress-syscfg.c @@ -270,10 +270,8 @@ static int vexpress_syscfg_probe(struct platform_device *pdev) /* Must use dev.parent (MFD), as that's where DT phandle points at... */ bridge = vexpress_config_bridge_register(pdev->dev.parent, &vexpress_syscfg_bridge_ops, syscfg); - if (IS_ERR(bridge)) - return PTR_ERR(bridge); - return 0; + return PTR_ERR_OR_ZERO(bridge); } static const struct platform_device_id vexpress_syscfg_id_table[] = { diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h index aa66c87c120b..3694821a6d2d 100644 --- a/include/linux/fpga/fpga-bridge.h +++ b/include/linux/fpga/fpga-bridge.h @@ -1,10 +1,11 @@ /* SPDX-License-Identifier: GPL-2.0 */ -#include <linux/device.h> -#include <linux/fpga/fpga-mgr.h> #ifndef _LINUX_FPGA_BRIDGE_H #define _LINUX_FPGA_BRIDGE_H +#include <linux/device.h> +#include <linux/fpga/fpga-mgr.h> + struct fpga_bridge; /** @@ -12,11 +13,13 @@ struct fpga_bridge; * @enable_show: returns the FPGA bridge's status * @enable_set: set a FPGA bridge as enabled or disabled * @fpga_bridge_remove: set FPGA into a specific state during driver remove + * @groups: optional attribute groups. */ struct fpga_bridge_ops { int (*enable_show)(struct fpga_bridge *bridge); int (*enable_set)(struct fpga_bridge *bridge, bool enable); void (*fpga_bridge_remove)(struct fpga_bridge *bridge); + const struct attribute_group **groups; }; /** @@ -43,6 +46,8 @@ struct fpga_bridge { struct fpga_bridge *of_fpga_bridge_get(struct device_node *node, struct fpga_image_info *info); +struct fpga_bridge *fpga_bridge_get(struct device *dev, + struct fpga_image_info *info); void fpga_bridge_put(struct fpga_bridge *bridge); int fpga_bridge_enable(struct fpga_bridge *bridge); int fpga_bridge_disable(struct fpga_bridge *bridge); @@ -50,9 +55,12 @@ int fpga_bridge_disable(struct fpga_bridge *bridge); int fpga_bridges_enable(struct list_head *bridge_list); int fpga_bridges_disable(struct list_head *bridge_list); void fpga_bridges_put(struct list_head *bridge_list); -int fpga_bridge_get_to_list(struct device_node *np, +int fpga_bridge_get_to_list(struct device *dev, struct fpga_image_info *info, struct list_head *bridge_list); +int of_fpga_bridge_get_to_list(struct device_node *np, + struct fpga_image_info *info, + struct list_head *bridge_list); int fpga_bridge_register(struct device *dev, const char *name, const struct fpga_bridge_ops *br_ops, void *priv); diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h index bfa14bc023fb..3c6de23aabdf 100644 --- a/include/linux/fpga/fpga-mgr.h +++ b/include/linux/fpga/fpga-mgr.h @@ -1,7 +1,8 @@ /* * FPGA Framework * - * Copyright (C) 2013-2015 Altera Corporation + * Copyright (C) 2013-2016 Altera Corporation + * Copyright (C) 2017 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, @@ -15,12 +16,12 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see <http://www.gnu.org/licenses/>. */ -#include <linux/mutex.h> -#include <linux/platform_device.h> - #ifndef _LINUX_FPGA_MGR_H #define _LINUX_FPGA_MGR_H +#include <linux/mutex.h> +#include <linux/platform_device.h> + struct fpga_manager; struct sg_table; @@ -83,12 +84,26 @@ enum fpga_mgr_states { * @disable_timeout_us: maximum time to disable traffic through bridge (uSec) * @config_complete_timeout_us: maximum time for FPGA to switch to operating * status in the write_complete op. + * @firmware_name: name of FPGA image firmware file + * @sgt: scatter/gather table containing FPGA image + * @buf: contiguous buffer containing FPGA image + * @count: size of buf + * @dev: device that owns this + * @overlay: Device Tree overlay */ struct fpga_image_info { u32 flags; u32 enable_timeout_us; u32 disable_timeout_us; u32 config_complete_timeout_us; + char *firmware_name; + struct sg_table *sgt; + const char *buf; + size_t count; + struct device *dev; +#ifdef CONFIG_OF + struct device_node *overlay; +#endif }; /** @@ -100,6 +115,7 @@ struct fpga_image_info { * @write_sg: write the scatter list of configuration data to the FPGA * @write_complete: set FPGA to operating state after writing is done * @fpga_remove: optional: Set FPGA into a specific state during driver remove + * @groups: optional attribute groups. * * fpga_manager_ops are the low level functions implemented by a specific * fpga manager driver. The optional ones are tested for NULL before being @@ -116,6 +132,7 @@ struct fpga_manager_ops { int (*write_complete)(struct fpga_manager *mgr, struct fpga_image_info *info); void (*fpga_remove)(struct fpga_manager *mgr); + const struct attribute_group **groups; }; /** @@ -138,14 +155,14 @@ struct fpga_manager { #define to_fpga_manager(d) container_of(d, struct fpga_manager, dev) -int fpga_mgr_buf_load(struct fpga_manager *mgr, struct fpga_image_info *info, - const char *buf, size_t count); -int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info, - struct sg_table *sgt); +struct fpga_image_info *fpga_image_info_alloc(struct device *dev); + +void fpga_image_info_free(struct fpga_image_info *info); + +int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info); -int fpga_mgr_firmware_load(struct fpga_manager *mgr, - struct fpga_image_info *info, - const char *image_name); +int fpga_mgr_lock(struct fpga_manager *mgr); +void fpga_mgr_unlock(struct fpga_manager *mgr); struct fpga_manager *of_fpga_mgr_get(struct device_node *node); diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h new file mode 100644 index 000000000000..b6520318ab9c --- /dev/null +++ b/include/linux/fpga/fpga-region.h @@ -0,0 +1,40 @@ +#ifndef _FPGA_REGION_H +#define _FPGA_REGION_H + +#include <linux/device.h> +#include <linux/fpga/fpga-mgr.h> +#include <linux/fpga/fpga-bridge.h> + +/** + * struct fpga_region - FPGA Region structure + * @dev: FPGA Region device + * @mutex: enforces exclusive reference to region + * @bridge_list: list of FPGA bridges specified in region + * @mgr: FPGA manager + * @info: FPGA image info + * @priv: private data + * @get_bridges: optional function to get bridges to a list + * @groups: optional attribute groups. + */ +struct fpga_region { + struct device dev; + struct mutex mutex; /* for exclusive reference to region */ + struct list_head bridge_list; + struct fpga_manager *mgr; + struct fpga_image_info *info; + void *priv; + int (*get_bridges)(struct fpga_region *region); + const struct attribute_group **groups; +}; + +#define to_fpga_region(d) container_of(d, struct fpga_region, dev) + +struct fpga_region *fpga_region_class_find( + struct device *start, const void *data, + int (*match)(struct device *, const void *)); + +int fpga_region_program_fpga(struct fpga_region *region); +int fpga_region_register(struct device *dev, struct fpga_region *region); +int fpga_region_unregister(struct fpga_region *region); + +#endif /* _FPGA_REGION_H */ diff --git a/include/uapi/linux/lp.h b/include/uapi/linux/lp.h index dafcfe4e4834..8589a27037d7 100644 --- a/include/uapi/linux/lp.h +++ b/include/uapi/linux/lp.h @@ -8,6 +8,8 @@ #ifndef _UAPI_LINUX_LP_H #define _UAPI_LINUX_LP_H +#include <linux/types.h> +#include <linux/ioctl.h> /* * Per POSIX guidelines, this module reserves the LP and lp prefixes @@ -88,7 +90,15 @@ #define LPGETSTATS 0x060d /* get statistics (struct lp_stats) */ #endif #define LPGETFLAGS 0x060e /* get status flags */ -#define LPSETTIMEOUT 0x060f /* set parport timeout */ +#define LPSETTIMEOUT_OLD 0x060f /* set parport timeout */ +#define LPSETTIMEOUT_NEW \ + _IOW(0x6, 0xf, __s64[2]) /* set parport timeout */ +#if __BITS_PER_LONG == 64 +#define LPSETTIMEOUT LPSETTIMEOUT_OLD +#else +#define LPSETTIMEOUT (sizeof(time_t) > sizeof(__kernel_long_t) ? \ + LPSETTIMEOUT_NEW : LPSETTIMEOUT_OLD) +#endif /* timeout for printk'ing a timeout, in jiffies (100ths of a second). This is also used for re-checking error conditions if LP_ABORT is diff --git a/tools/hv/Makefile b/tools/hv/Makefile index 31503819454d..1139d71fa0cf 100644 --- a/tools/hv/Makefile +++ b/tools/hv/Makefile @@ -7,9 +7,30 @@ CFLAGS = $(WARNINGS) -g $(shell getconf LFS_CFLAGS) CFLAGS += -D__EXPORTED_HEADERS__ -I../../include/uapi -I../../include -all: hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon +sbindir ?= /usr/sbin +libexecdir ?= /usr/libexec +sharedstatedir ?= /var/lib + +ALL_PROGRAMS := hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon + +ALL_SCRIPTS := hv_get_dhcp_info.sh hv_get_dns_info.sh hv_set_ifconfig.sh + +all: $(ALL_PROGRAMS) + %: %.c $(CC) $(CFLAGS) -o $@ $^ clean: $(RM) hv_kvp_daemon hv_vss_daemon hv_fcopy_daemon + +install: all + install -d -m 755 $(DESTDIR)$(sbindir); \ + install -d -m 755 $(DESTDIR)$(libexecdir)/hypervkvpd; \ + install -d -m 755 $(DESTDIR)$(sharedstatedir); \ + for program in $(ALL_PROGRAMS); do \ + install $$program -m 755 $(DESTDIR)$(sbindir); \ + done; \ + install -m 755 lsvmbus $(DESTDIR)$(sbindir); \ + for script in $(ALL_SCRIPTS); do \ + install $$script -m 755 $(DESTDIR)$(libexecdir)/hypervkvpd/$${script%.sh}; \ + done |