Commit c6cc89b0 authored by Mikhail Karpenko's avatar Mikhail Karpenko

Split long PRDT enries

Each PRDT entry is limited to 4Mb and this was fixed. Tests look fine.
parent 33ec5861
...@@ -71,6 +71,8 @@ static void finish_cmd(struct device *dev, struct elphel_ahci_priv *dpriv); ...@@ -71,6 +71,8 @@ static void finish_cmd(struct device *dev, struct elphel_ahci_priv *dpriv);
static int process_cmd(struct device *dev, struct elphel_ahci_priv *dpriv, struct ata_port *port); static int process_cmd(struct device *dev, struct elphel_ahci_priv *dpriv, struct ata_port *port);
//static void start_cmd(struct device *dev, struct elphel_ahci_priv *dpriv, struct ata_port *port); //static void start_cmd(struct device *dev, struct elphel_ahci_priv *dpriv, struct ata_port *port);
static inline size_t get_size_from(const struct fvec *vects, int index, size_t offset, int all); static inline size_t get_size_from(const struct fvec *vects, int index, size_t offset, int all);
static inline void vectmov(struct fvec *vec, size_t len);
static inline void vectsplit(struct fvec *vect, struct fvec *parts, size_t *n_elem);
static ssize_t set_load_flag(struct device *dev, struct device_attribute *attr, static ssize_t set_load_flag(struct device *dev, struct device_attribute *attr,
const char *buff, size_t buff_sz) const char *buff, size_t buff_sz)
...@@ -141,8 +143,8 @@ static irqreturn_t elphel_irq_handler(int irq, void * dev_instance) ...@@ -141,8 +143,8 @@ static irqreturn_t elphel_irq_handler(int irq, void * dev_instance)
writel(host_irq_stat, hpriv->mmio + HOST_IRQ_STAT); writel(host_irq_stat, hpriv->mmio + HOST_IRQ_STAT);
handled = IRQ_HANDLED; handled = IRQ_HANDLED;
// if (process_cmd(host->dev, dpriv, host->ports[0]) == 0) if (process_cmd(host->dev, dpriv, host->ports[0]) == 0)
// finish_cmd(host->dev, dpriv); finish_cmd(host->dev, dpriv);
} else { } else {
/* pass handling to AHCI level */ /* pass handling to AHCI level */
handled = ahci_single_irq_intr(irq, dev_instance); handled = ahci_single_irq_intr(irq, dev_instance);
...@@ -256,7 +258,6 @@ static int elphel_drv_probe(struct platform_device *pdev) ...@@ -256,7 +258,6 @@ static int elphel_drv_probe(struct platform_device *pdev)
ret = init_buffers(dev, &dpriv->fbuffs); ret = init_buffers(dev, &dpriv->fbuffs);
if (ret != 0) if (ret != 0)
return ret; return ret;
// sg_init_table(dpriv->sgl, MAX_DATA_CHUNKS);
init_vectors(dpriv); init_vectors(dpriv);
match = of_match_device(ahci_elphel_of_match, &pdev->dev); match = of_match_device(ahci_elphel_of_match, &pdev->dev);
...@@ -640,7 +641,7 @@ static int map_vectors(struct elphel_ahci_priv *dpriv) ...@@ -640,7 +641,7 @@ static int map_vectors(struct elphel_ahci_priv *dpriv)
vect = chunks[i]; vect = chunks[i];
} }
if (total_sz > dpriv->max_data_sz) { if (total_sz > dpriv->max_data_sz) {
// truncate current buffer and finish mapping /* truncate current buffer and finish mapping */
tail = total_sz - dpriv->max_data_sz; tail = total_sz - dpriv->max_data_sz;
vect.iov_len -= tail; vect.iov_len -= tail;
dpriv->curr_data_chunk = i; dpriv->curr_data_chunk = i;
...@@ -652,7 +653,20 @@ static int map_vectors(struct elphel_ahci_priv *dpriv) ...@@ -652,7 +653,20 @@ static int map_vectors(struct elphel_ahci_priv *dpriv)
finish = 1; finish = 1;
} }
if (vect.iov_len != 0) { if (vect.iov_len != 0) {
if (vect.iov_len < MAX_PRDT_LEN) {
dpriv->sgl[index++] = vect; dpriv->sgl[index++] = vect;
} else {
/* current vector is too long and can not be mapped to a single PRDT entry, split it */
vectsplit(&vect, dpriv->sgl, &index);
if (vect.iov_len < MAX_PRDT_LEN) {
dpriv->sgl[index++] = vect;
} else {
/* free slots in PRDT table have ended */
dpriv->curr_data_chunk = i;
dpriv->curr_data_offset = (unsigned char *)vect.iov_base - (unsigned char *)chunks[i].iov_base;
finish = 1;
}
}
} }
if (finish) if (finish)
break; break;
...@@ -666,6 +680,21 @@ static int map_vectors(struct elphel_ahci_priv *dpriv) ...@@ -666,6 +680,21 @@ static int map_vectors(struct elphel_ahci_priv *dpriv)
return index; return index;
} }
static inline void vectsplit(struct fvec *vect, struct fvec *parts, size_t *n_elem)
{
size_t len;
struct fvec split;
while (vect->iov_len > MAX_PRDT_LEN && *n_elem < MAX_SGL_LEN) {
len = MAX_PRDT_LEN - MAX_PRDT_LEN % PHY_BLOCK_SIZE;
split.iov_base = vect->iov_base;
split.iov_dma = vect->iov_dma;
split.iov_len = len;
vectmov(vect, len);
parts[*n_elem] = split;
*n_elem = *n_elem + 1;
}
}
static inline void vectcpy(struct fvec *dest, void *src, size_t len) static inline void vectcpy(struct fvec *dest, void *src, size_t len)
{ {
unsigned char *d = (unsigned char *)dest->iov_base; unsigned char *d = (unsigned char *)dest->iov_base;
...@@ -1236,7 +1265,6 @@ static ssize_t rawdev_write(struct device *dev, ///< ...@@ -1236,7 +1265,6 @@ static ssize_t rawdev_write(struct device *dev, ///<
if ((dpriv->flags & PROC_CMD) || dont_process) { if ((dpriv->flags & PROC_CMD) || dont_process) {
// we are not ready yet // we are not ready yet
printk(KERN_DEBUG ">>> not ready: flags = %u\n", dpriv->flags);
return -EAGAIN; return -EAGAIN;
} }
...@@ -1315,24 +1343,24 @@ static ssize_t rawdev_write(struct device *dev, ///< ...@@ -1315,24 +1343,24 @@ static ssize_t rawdev_write(struct device *dev, ///<
for (i = 0; i < MAX_DATA_CHUNKS; i++) { for (i = 0; i < MAX_DATA_CHUNKS; i++) {
printk(KERN_DEBUG ">>>\tslot: %i; len: %u\n", i, dpriv->data_chunks[i].iov_len); printk(KERN_DEBUG ">>>\tslot: %i; len: %u\n", i, dpriv->data_chunks[i].iov_len);
} }
if (check_chunks(dpriv->data_chunks) != 0) { // if (check_chunks(dpriv->data_chunks) != 0) {
dont_process = 1; // dont_process = 1;
return -EINVAL; // return -EINVAL;
} // }
process_cmd(dev, dpriv, port); process_cmd(dev, dpriv, port);
while (dpriv->flags & PROC_CMD) { // while (dpriv->flags & PROC_CMD) {
#ifndef DEBUG_DONT_WRITE //#ifndef DEBUG_DONT_WRITE
while (dpriv->flags & IRQ_SIMPLE) { // while (dpriv->flags & IRQ_SIMPLE) {
printk_once(KERN_DEBUG ">>> waiting for interrupt\n"); // printk_once(KERN_DEBUG ">>> waiting for interrupt\n");
msleep_interruptible(1); // msleep_interruptible(1);
} // }
#endif //#endif
printk(KERN_DEBUG ">>> proceeding to next cmd chunk\n"); // printk(KERN_DEBUG ">>> proceeding to next cmd chunk\n");
sg_elems = process_cmd(dev, dpriv, port); // sg_elems = process_cmd(dev, dpriv, port);
if (sg_elems == 0) // if (sg_elems == 0)
finish_cmd(dev, dpriv); // finish_cmd(dev, dpriv);
} // }
// if (chunks[CHUNK_DATA_0].iov_len != 0) // if (chunks[CHUNK_DATA_0].iov_len != 0)
// dma_unmap_single(dev, chunks[CHUNK_DATA_0].iov_dma, chunks[CHUNK_DATA_0].iov_len, DMA_TO_DEVICE); // dma_unmap_single(dev, chunks[CHUNK_DATA_0].iov_dma, chunks[CHUNK_DATA_0].iov_len, DMA_TO_DEVICE);
// if (chunks[CHUNK_DATA_1].iov_len != 0) // if (chunks[CHUNK_DATA_1].iov_len != 0)
......
...@@ -34,6 +34,8 @@ ...@@ -34,6 +34,8 @@
#define CMD_FIS_LEN 5 #define CMD_FIS_LEN 5
/** This is used to get 28-bit address from 64-bit value */ /** This is used to get 28-bit address from 64-bit value */
#define ADDR_MASK_28_BIT ((u64)0xfffffff) #define ADDR_MASK_28_BIT ((u64)0xfffffff)
/** A maximum of length of 4MB may exist for PRDT entry */
#define MAX_PRDT_LEN 0x3fffff
/** An array or JPEG frame chunks contains pointers to JPEG leading marker, /** An array or JPEG frame chunks contains pointers to JPEG leading marker,
* JPEG header, Exif data if present, stuffing bytes chunk which aligns * JPEG header, Exif data if present, stuffing bytes chunk which aligns
* the frame size to disk sector boundary, JPEG data which * the frame size to disk sector boundary, JPEG data which
...@@ -46,6 +48,9 @@ ...@@ -46,6 +48,9 @@
#define DEFAULT_PORT_NUM 0 #define DEFAULT_PORT_NUM 0
/** Align buffers length to this amount of bytes */ /** Align buffers length to this amount of bytes */
#define ALIGNMENT_SIZE 32 #define ALIGNMENT_SIZE 32
/** Maximum number of entries in PRDT table. HW max is 64k.
* Set this value the same as AHCI_MAX_SG in ahci.h */
#define MAX_SGL_LEN 168
/** This structure holds raw device buffer pointers */ /** This structure holds raw device buffer pointers */
struct drv_pointers { struct drv_pointers {
...@@ -102,7 +107,7 @@ struct elphel_ahci_priv { ...@@ -102,7 +107,7 @@ struct elphel_ahci_priv {
struct drv_pointers lba_ptr; struct drv_pointers lba_ptr;
struct frame_buffers fbuffs; struct frame_buffers fbuffs;
struct fvec data_chunks[MAX_DATA_CHUNKS]; struct fvec data_chunks[MAX_DATA_CHUNKS];
struct fvec sgl[MAX_DATA_CHUNKS]; struct fvec sgl[MAX_SGL_LEN];
int sg_elems; int sg_elems;
int curr_data_chunk; ///< index of a data chunk used during last transaction int curr_data_chunk; ///< index of a data chunk used during last transaction
size_t curr_data_offset; ///< offset of the last byte in a data chunk pointed to by @e curr_data_chunk size_t curr_data_offset; ///< offset of the last byte in a data chunk pointed to by @e curr_data_chunk
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment