return 0;
 }
 
+static inline int fpga_mgr_parse_header(struct fpga_manager *mgr,
+                                       struct fpga_image_info *info,
+                                       const char *buf, size_t count)
+{
+       if (mgr->mops->parse_header)
+               return mgr->mops->parse_header(mgr, info, buf, count);
+       return 0;
+}
+
 static inline int fpga_mgr_write_init(struct fpga_manager *mgr,
                                      struct fpga_image_info *info,
                                      const char *buf, size_t count)
 EXPORT_SYMBOL_GPL(fpga_image_info_free);
 
 /*
- * Call the low level driver's write_init function.  This will do the
+ * Call the low level driver's parse_header function with entire FPGA image
+ * buffer on the input. This will set info->header_size and info->data_size.
+ */
+static int fpga_mgr_parse_header_mapped(struct fpga_manager *mgr,
+                                       struct fpga_image_info *info,
+                                       const char *buf, size_t count)
+{
+       int ret;
+
+       mgr->state = FPGA_MGR_STATE_PARSE_HEADER;
+       ret = fpga_mgr_parse_header(mgr, info, buf, count);
+
+       if (info->header_size + info->data_size > count) {
+               dev_err(&mgr->dev, "Bitsream data outruns FPGA image\n");
+               ret = -EINVAL;
+       }
+
+       if (ret) {
+               dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
+               mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
+       }
+
+       return ret;
+}
+
+/*
+ * Call the low level driver's parse_header function with first fragment of
+ * scattered FPGA image on the input. If header fits first fragment,
+ * parse_header will set info->header_size and info->data_size. If it is not,
+ * parse_header will set desired size to info->header_size and -EAGAIN will be
+ * returned.
+ */
+static int fpga_mgr_parse_header_sg_first(struct fpga_manager *mgr,
+                                         struct fpga_image_info *info,
+                                         struct sg_table *sgt)
+{
+       struct sg_mapping_iter miter;
+       int ret;
+
+       mgr->state = FPGA_MGR_STATE_PARSE_HEADER;
+
+       sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
+       if (sg_miter_next(&miter) &&
+           miter.length >= info->header_size)
+               ret = fpga_mgr_parse_header(mgr, info, miter.addr, miter.length);
+       else
+               ret = -EAGAIN;
+       sg_miter_stop(&miter);
+
+       if (ret && ret != -EAGAIN) {
+               dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
+               mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
+       }
+
+       return ret;
+}
+
+/*
+ * Copy scattered FPGA image fragments to temporary buffer and call the
+ * low level driver's parse_header function. This should be called after
+ * fpga_mgr_parse_header_sg_first() returned -EAGAIN. In case of success,
+ * pointer to the newly allocated image header copy will be returned and
+ * its size will be set into *ret_size. Returned buffer needs to be freed.
+ */
+static void *fpga_mgr_parse_header_sg(struct fpga_manager *mgr,
+                                     struct fpga_image_info *info,
+                                     struct sg_table *sgt, size_t *ret_size)
+{
+       size_t len, new_header_size, header_size = 0;
+       char *new_buf, *buf = NULL;
+       int ret;
+
+       do {
+               new_header_size = info->header_size;
+               if (new_header_size <= header_size) {
+                       dev_err(&mgr->dev, "Requested invalid header size\n");
+                       ret = -EFAULT;
+                       break;
+               }
+
+               new_buf = krealloc(buf, new_header_size, GFP_KERNEL);
+               if (!new_buf) {
+                       ret = -ENOMEM;
+                       break;
+               }
+
+               buf = new_buf;
+
+               len = sg_pcopy_to_buffer(sgt->sgl, sgt->nents,
+                                        buf + header_size,
+                                        new_header_size - header_size,
+                                        header_size);
+               if (len != new_header_size - header_size) {
+                       ret = -EFAULT;
+                       break;
+               }
+
+               header_size = new_header_size;
+               ret = fpga_mgr_parse_header(mgr, info, buf, header_size);
+       } while (ret == -EAGAIN);
+
+       if (ret) {
+               dev_err(&mgr->dev, "Error while parsing FPGA image header\n");
+               mgr->state = FPGA_MGR_STATE_PARSE_HEADER_ERR;
+               kfree(buf);
+               buf = ERR_PTR(ret);
+       }
+
+       *ret_size = header_size;
+
+       return buf;
+}
+
+/*
+ * 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.
+ * receive an FPGA image. The low level driver gets to see at least first
+ * info->header_size bytes in the buffer. If info->header_size is 0,
+ * write_init will not get any bytes of image buffer.
  */
 static int fpga_mgr_write_init_buf(struct fpga_manager *mgr,
                                   struct fpga_image_info *info,
                                   const char *buf, size_t count)
 {
+       size_t header_size = info->header_size;
        int ret;
 
        mgr->state = FPGA_MGR_STATE_WRITE_INIT;
-       if (!mgr->mops->initial_header_size) {
+
+       if (header_size > count)
+               ret = -EINVAL;
+       else if (!header_size)
                ret = fpga_mgr_write_init(mgr, info, NULL, 0);
-       } else {
-               count = min(mgr->mops->initial_header_size, count);
+       else
                ret = fpga_mgr_write_init(mgr, info, buf, count);
-       }
 
        if (ret) {
                dev_err(&mgr->dev, "Error preparing FPGA for writing\n");
        return 0;
 }
 
-static int fpga_mgr_write_init_sg(struct fpga_manager *mgr,
-                                 struct fpga_image_info *info,
-                                 struct sg_table *sgt)
+static int fpga_mgr_prepare_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)
+       /* Short path. Low level driver don't care about image header. */
+       if (!mgr->mops->initial_header_size && !mgr->mops->parse_header)
                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);
+       ret = fpga_mgr_parse_header_sg_first(mgr, info, sgt);
+       /* If 0, header fits first fragment, call write_init on it */
+       if (!ret) {
+               sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
+               if (sg_miter_next(&miter)) {
+                       ret = fpga_mgr_write_init_buf(mgr, info, miter.addr,
+                                                     miter.length);
+                       sg_miter_stop(&miter);
+                       return ret;
+               }
                sg_miter_stop(&miter);
+       /*
+        * If -EAGAIN, more sg buffer is needed,
+        * otherwise an error has occurred.
+        */
+       } else if (ret != -EAGAIN) {
                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;
+       /*
+        * Copy the fragments into temporary memory.
+        * Copying is done inside fpga_mgr_parse_header_sg().
+        */
+       buf = fpga_mgr_parse_header_sg(mgr, info, sgt, &len);
+       if (IS_ERR(buf))
+               return PTR_ERR(buf);
 
-       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);
 {
        int ret;
 
-       ret = fpga_mgr_write_init_sg(mgr, info, sgt);
+       ret = fpga_mgr_prepare_sg(mgr, info, sgt);
        if (ret)
                return ret;
 
        if (mgr->mops->write_sg) {
                ret = fpga_mgr_write_sg(mgr, sgt);
        } else {
+               size_t length, count = 0, data_size = info->data_size;
                struct sg_mapping_iter miter;
 
                sg_miter_start(&miter, sgt->sgl, sgt->nents, SG_MITER_FROM_SG);
+
+               if (mgr->mops->skip_header &&
+                   !sg_miter_skip(&miter, info->header_size)) {
+                       ret = -EINVAL;
+                       goto out;
+               }
+
                while (sg_miter_next(&miter)) {
-                       ret = fpga_mgr_write(mgr, miter.addr, miter.length);
+                       if (data_size)
+                               length = min(miter.length, data_size - count);
+                       else
+                               length = miter.length;
+
+                       ret = fpga_mgr_write(mgr, miter.addr, length);
                        if (ret)
                                break;
+
+                       count += length;
+                       if (data_size && count >= data_size)
+                               break;
                }
                sg_miter_stop(&miter);
        }
 
+out:
        if (ret) {
                dev_err(&mgr->dev, "Error while writing image data to FPGA\n");
                mgr->state = FPGA_MGR_STATE_WRITE_ERR;
 {
        int ret;
 
+       ret = fpga_mgr_parse_header_mapped(mgr, info, buf, count);
+       if (ret)
+               return ret;
+
        ret = fpga_mgr_write_init_buf(mgr, info, buf, count);
        if (ret)
                return ret;
 
+       if (mgr->mops->skip_header) {
+               buf += info->header_size;
+               count -= info->header_size;
+       }
+
+       if (info->data_size)
+               count = info->data_size;
+
        /*
         * Write the FPGA image to the FPGA.
         */
  */
 int fpga_mgr_load(struct fpga_manager *mgr, struct fpga_image_info *info)
 {
+       info->header_size = mgr->mops->initial_header_size;
+
        if (info->sgt)
                return fpga_mgr_buf_load_sg(mgr, info, info->sgt);
        if (info->buf && info->count)
        [FPGA_MGR_STATE_FIRMWARE_REQ] =         "firmware request",
        [FPGA_MGR_STATE_FIRMWARE_REQ_ERR] =     "firmware request error",
 
+       /* Parse FPGA image header */
+       [FPGA_MGR_STATE_PARSE_HEADER] =         "parse header",
+       [FPGA_MGR_STATE_PARSE_HEADER_ERR] =     "parse header error",
+
        /* Preparing FPGA to receive image */
        [FPGA_MGR_STATE_WRITE_INIT] =           "write init",
        [FPGA_MGR_STATE_WRITE_INIT_ERR] =       "write init error",
 
  * @FPGA_MGR_STATE_RESET: FPGA in reset state
  * @FPGA_MGR_STATE_FIRMWARE_REQ: firmware request in progress
  * @FPGA_MGR_STATE_FIRMWARE_REQ_ERR: firmware request failed
+ * @FPGA_MGR_STATE_PARSE_HEADER: parse FPGA image header
+ * @FPGA_MGR_STATE_PARSE_HEADER_ERR: Error during PARSE_HEADER stage
  * @FPGA_MGR_STATE_WRITE_INIT: preparing FPGA for programming
  * @FPGA_MGR_STATE_WRITE_INIT_ERR: Error during WRITE_INIT stage
  * @FPGA_MGR_STATE_WRITE: writing image to FPGA
        FPGA_MGR_STATE_FIRMWARE_REQ,
        FPGA_MGR_STATE_FIRMWARE_REQ_ERR,
 
-       /* write sequence: init, write, complete */
+       /* write sequence: parse header, init, write, complete */
+       FPGA_MGR_STATE_PARSE_HEADER,
+       FPGA_MGR_STATE_PARSE_HEADER_ERR,
        FPGA_MGR_STATE_WRITE_INIT,
        FPGA_MGR_STATE_WRITE_INIT_ERR,
        FPGA_MGR_STATE_WRITE,
  * @sgt: scatter/gather table containing FPGA image
  * @buf: contiguous buffer containing FPGA image
  * @count: size of buf
+ * @header_size: size of image header.
+ * @data_size: size of image data to be sent to the device. If not specified,
+ *     whole image will be used. Header may be skipped in either case.
  * @region_id: id of target region
  * @dev: device that owns this
  * @overlay: Device Tree overlay
        struct sg_table *sgt;
        const char *buf;
        size_t count;
+       size_t header_size;
+       size_t data_size;
        int region_id;
        struct device *dev;
 #ifdef CONFIG_OF
 
 /**
  * struct fpga_manager_ops - ops for low level fpga manager drivers
- * @initial_header_size: Maximum number of bytes that should be passed into write_init
+ * @initial_header_size: minimum number of bytes that should be passed into
+ *     parse_header and write_init.
+ * @skip_header: bool flag to tell fpga-mgr core whether it should skip
+ *     info->header_size part at the beginning of the image when invoking
+ *     write callback.
  * @state: returns an enum value of the FPGA's state
  * @status: returns status of the FPGA, including reconfiguration error code
+ * @parse_header: parse FPGA image header to set info->header_size and
+ *     info->data_size. In case the input buffer is not large enough, set
+ *     required size to info->header_size and return -EAGAIN.
  * @write_init: prepare the FPGA to receive configuration data
  * @write: write count bytes of configuration data to the FPGA
  * @write_sg: write the scatter list of configuration data to the FPGA
  */
 struct fpga_manager_ops {
        size_t initial_header_size;
+       bool skip_header;
        enum fpga_mgr_states (*state)(struct fpga_manager *mgr);
        u64 (*status)(struct fpga_manager *mgr);
+       int (*parse_header)(struct fpga_manager *mgr,
+                           struct fpga_image_info *info,
+                           const char *buf, size_t count);
        int (*write_init)(struct fpga_manager *mgr,
                          struct fpga_image_info *info,
                          const char *buf, size_t count);