diff options
Diffstat (limited to 'drivers/fpga/fpga-mgr.c')
| -rw-r--r-- | drivers/fpga/fpga-mgr.c | 236 | 
1 files changed, 206 insertions, 30 deletions
| diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index f0a69d3e60a5..86d2cb203533 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -25,16 +25,106 @@  #include <linux/of.h>  #include <linux/mutex.h>  #include <linux/slab.h> +#include <linux/scatterlist.h> +#include <linux/highmem.h>  static DEFINE_IDA(fpga_mgr_ida);  static struct class *fpga_mgr_class; +/* + * 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 + * receive an FPGA image. The low level driver only gets to see the first + * initial_header_size bytes in the buffer. + */ +static int fpga_mgr_write_init_buf(struct fpga_manager *mgr, +				   struct fpga_image_info *info, +				   const char *buf, size_t count) +{ +	int ret; + +	mgr->state = FPGA_MGR_STATE_WRITE_INIT; +	if (!mgr->mops->initial_header_size) +		ret = mgr->mops->write_init(mgr, info, NULL, 0); +	else +		ret = mgr->mops->write_init( +		    mgr, info, buf, min(mgr->mops->initial_header_size, count)); + +	if (ret) { +		dev_err(&mgr->dev, "Error preparing FPGA for writing\n"); +		mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; +		return ret; +	} + +	return 0; +} + +static int fpga_mgr_write_init_sg(struct fpga_manager *mgr, +				  struct fpga_image_info *info, +				  struct sg_table *sgt) +{ +	struct sg_mapping_iter miter; +	size_t len; +	char *buf; +	int ret; + +	if (!mgr->mops->initial_header_size) +		return fpga_mgr_write_init_buf(mgr, info, NULL, 0); + +	/* +	 * First try to use miter to map the first fragment to access the +	 * header, this is the typical path. +	 */ +	sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); +	if (sg_miter_next(&miter) && +	    miter.length >= mgr->mops->initial_header_size) { +		ret = fpga_mgr_write_init_buf(mgr, info, miter.addr, +					      miter.length); +		sg_miter_stop(&miter); +		return ret; +	} +	sg_miter_stop(&miter); + +	/* Otherwise copy the fragments into temporary memory. */ +	buf = kmalloc(mgr->mops->initial_header_size, GFP_KERNEL); +	if (!buf) +		return -ENOMEM; + +	len = sg_copy_to_buffer(sgt->sgl, sgt->nents, buf, +				mgr->mops->initial_header_size); +	ret = fpga_mgr_write_init_buf(mgr, info, buf, len); + +	kfree(buf); + +	return ret; +} + +/* + * After all the FPGA image has been written, do the device specific steps to + * finish and set the FPGA into operating mode. + */ +static int fpga_mgr_write_complete(struct fpga_manager *mgr, +				   struct fpga_image_info *info) +{ +	int ret; + +	mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; +	ret = mgr->mops->write_complete(mgr, info); +	if (ret) { +		dev_err(&mgr->dev, "Error after writing image data to FPGA\n"); +		mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; +		return ret; +	} +	mgr->state = FPGA_MGR_STATE_OPERATING; + +	return 0; +} +  /** - * fpga_mgr_buf_load - load fpga from image in buffer + * fpga_mgr_buf_load_sg - load fpga from image in buffer from a scatter list   * @mgr:	fpga manager   * @info:	fpga image specific information - * @buf:	buffer contain fpga image - * @count:	byte count of buf + * @sgt:	scatterlist table   *   * Step the low level fpga manager through the device-specific steps of getting   * an FPGA ready to be configured, writing the image to it, then doing whatever @@ -42,54 +132,139 @@ static struct class *fpga_mgr_class;   * mgr pointer from of_fpga_mgr_get() or fpga_mgr_get() and checked that it is   * not an error code.   * + * This is the preferred entry point for FPGA programming, it does not require + * any contiguous kernel memory. + *   * 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) +int fpga_mgr_buf_load_sg(struct fpga_manager *mgr, struct fpga_image_info *info, +			 struct sg_table *sgt)  { -	struct device *dev = &mgr->dev;  	int ret; -	/* -	 * 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 receive an FPGA image. The low level driver only gets to -	 * see the first initial_header_size bytes in the buffer. -	 */ -	mgr->state = FPGA_MGR_STATE_WRITE_INIT; -	ret = mgr->mops->write_init(mgr, info, buf, -				    min(mgr->mops->initial_header_size, count)); +	ret = fpga_mgr_write_init_sg(mgr, info, sgt); +	if (ret) +		return ret; + +	/* Write the FPGA image to the FPGA. */ +	mgr->state = FPGA_MGR_STATE_WRITE; +	if (mgr->mops->write_sg) { +		ret = mgr->mops->write_sg(mgr, sgt); +	} else { +		struct sg_mapping_iter miter; + +		sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG); +		while (sg_miter_next(&miter)) { +			ret = mgr->mops->write(mgr, miter.addr, miter.length); +			if (ret) +				break; +		} +		sg_miter_stop(&miter); +	} +  	if (ret) { -		dev_err(dev, "Error preparing FPGA for writing\n"); -		mgr->state = FPGA_MGR_STATE_WRITE_INIT_ERR; +		dev_err(&mgr->dev, "Error while writing image data to FPGA\n"); +		mgr->state = FPGA_MGR_STATE_WRITE_ERR;  		return ret;  	} +	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, +				    const char *buf, size_t count) +{ +	int ret; + +	ret = fpga_mgr_write_init_buf(mgr, info, buf, count); +	if (ret) +		return ret; +  	/*  	 * Write the FPGA image to the FPGA.  	 */  	mgr->state = FPGA_MGR_STATE_WRITE;  	ret = mgr->mops->write(mgr, buf, count);  	if (ret) { -		dev_err(dev, "Error while writing image data to FPGA\n"); +		dev_err(&mgr->dev, "Error while writing image data to FPGA\n");  		mgr->state = FPGA_MGR_STATE_WRITE_ERR;  		return ret;  	} +	return fpga_mgr_write_complete(mgr, info); +} + +/** + * fpga_mgr_buf_load - load fpga from image in buffer + * @mgr:	fpga manager + * @flags:	flags setting fpga confuration modes + * @buf:	buffer contain fpga image + * @count:	byte count of buf + * + * Step the low level fpga manager through the device-specific steps of getting + * an FPGA ready to be configured, writing the image to it, then doing whatever + * post-configuration steps necessary.  This code assumes the caller got the + * mgr pointer from of_fpga_mgr_get() and checked that it is not an error code. + * + * 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) +{ +	struct page **pages; +	struct sg_table sgt; +	const void *p; +	int nr_pages; +	int index; +	int rc; +  	/* -	 * After all the FPGA image has been written, do the device specific -	 * steps to finish and set the FPGA into operating mode. +	 * This is just a fast path if the caller has already created a +	 * contiguous kernel buffer and the driver doesn't require SG, non-SG +	 * drivers will still work on the slow path.  	 */ -	mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE; -	ret = mgr->mops->write_complete(mgr, info); -	if (ret) { -		dev_err(dev, "Error after writing image data to FPGA\n"); -		mgr->state = FPGA_MGR_STATE_WRITE_COMPLETE_ERR; -		return ret; +	if (mgr->mops->write) +		return fpga_mgr_buf_load_mapped(mgr, info, buf, count); + +	/* +	 * Convert the linear kernel pointer into a sg_table of pages for use +	 * by the driver. +	 */ +	nr_pages = DIV_ROUND_UP((unsigned long)buf + count, PAGE_SIZE) - +		   (unsigned long)buf / PAGE_SIZE; +	pages = kmalloc_array(nr_pages, sizeof(struct page *), GFP_KERNEL); +	if (!pages) +		return -ENOMEM; + +	p = buf - offset_in_page(buf); +	for (index = 0; index < nr_pages; index++) { +		if (is_vmalloc_addr(p)) +			pages[index] = vmalloc_to_page(p); +		else +			pages[index] = kmap_to_page((void *)p); +		if (!pages[index]) { +			kfree(pages); +			return -EFAULT; +		} +		p += PAGE_SIZE;  	} -	mgr->state = FPGA_MGR_STATE_OPERATING; -	return 0; +	/* +	 * The temporary pages list is used to code share the merging algorithm +	 * in sg_alloc_table_from_pages +	 */ +	rc = sg_alloc_table_from_pages(&sgt, pages, index, offset_in_page(buf), +				       count, GFP_KERNEL); +	kfree(pages); +	if (rc) +		return rc; + +	rc = fpga_mgr_buf_load_sg(mgr, info, &sgt); +	sg_free_table(&sgt); + +	return rc;  }  EXPORT_SYMBOL_GPL(fpga_mgr_buf_load); @@ -291,8 +466,9 @@ int fpga_mgr_register(struct device *dev, const char *name,  	struct fpga_manager *mgr;  	int id, ret; -	if (!mops || !mops->write_init || !mops->write || -	    !mops->write_complete || !mops->state) { +	if (!mops || !mops->write_complete || !mops->state || +	    !mops->write_init || (!mops->write && !mops->write_sg) || +	    (mops->write && mops->write_sg)) {  		dev_err(dev, "Attempt to register without fpga_manager_ops\n");  		return -EINVAL;  	} | 
