[PATCH 21/29] memstick: jmb38x_ms: rework TPC execution

From: Maxim Levitsky
Date: Fri Oct 22 2010 - 19:54:57 EST


Code rewritten to make it simplier.
A lot of debug output is added to help
diagnose bugs.

No major functional changes

Signed-off-by: Maxim Levitsky<maximlevitsky@xxxxxxxxx>
---
drivers/memstick/host/jmb38x_ms.c | 199 +++++++++++++++++++++----------------
drivers/memstick/host/jmb38x_ms.h | 3 +-
2 files changed, 114 insertions(+), 88 deletions(-)

diff --git a/drivers/memstick/host/jmb38x_ms.c b/drivers/memstick/host/jmb38x_ms.c
index 5af0a52..d15118a 100644
--- a/drivers/memstick/host/jmb38x_ms.c
+++ b/drivers/memstick/host/jmb38x_ms.c
@@ -254,94 +254,121 @@ static void j38ms_write_tpc_inline(struct j38ms_host *host)
* PIO _reads_ must be aligned. Writes can be not aligned
*
*/
-static int j38ms_execute_tpc(struct memstick_host *msh)
+
+static int j38ms_execute_tpc(struct j38ms_host *host)
{
- struct j38ms_host *host = memstick_priv(msh);
- unsigned char *data;
- unsigned int data_len, cmd, t_val;
+ u32 cmd = 0, t_val;
+ unsigned int data_len = host->req->long_data ?
+ host->req->sg.length : host->req->data_len;
+ bool is_read = host->req->data_dir == READ;
+
+ if (!(j38ms_read_reg(host, STATUS) & STATUS_HAS_MEDIA)) {
+ dbg(host, "IO: card removed, refusing to send TPC");
+ host->req->error = -ENODEV;
+ return host->req->error;
+ }

- if (!(STATUS_HAS_MEDIA & readl(host->addr + STATUS))) {
- dev_dbg(&msh->dev, "no media status\n");
- host->req->error = -ETIME;
+ if (data_len > BLOCK_SIZE_MASK) {
+ dbg(host, "IO: too long TPC (len: %d)", data_len);
+ host->req->error = -ENOSYS;
return host->req->error;
}

- dev_dbg(&msh->dev, "control %08x\n", readl(host->addr + HOST_CONTROL));
- dev_dbg(&msh->dev, "status %08x\n", readl(host->addr + INT_STATUS));
- dev_dbg(&msh->dev, "hstatus %08x\n", readl(host->addr + STATUS));
+ dbg(host, "IO: Start execution of %s",
+ memstick_debug_get_tpc_name(host->req->tpc));
+
+
+ dbg_v(host, "host control: %08x", j38ms_read_reg(host, HOST_CONTROL));
+ dbg_v(host, "int status: %08x", j38ms_read_reg(host, INT_STATUS));
+ dbg_v(host, "card status: %08x", j38ms_read_reg(host, STATUS));

+ host->req->error = 0;
host->cmd_flags = 0;

cmd = host->req->tpc << 16;
- cmd |= TPC_DATA_SEL;
-
- if (host->req->data_dir == READ)
+ if (is_read)
cmd |= TPC_DIR;
- if (host->req->need_card_int)
- cmd |= TPC_WAIT_INT;
-
- data = host->req->data;
-
- if (!no_dma)
- host->cmd_flags |= DMA_DATA;

- if (host->req->long_data) {
- data_len = host->req->sg.length;
- } else {
- data_len = host->req->data_len;
- host->cmd_flags &= ~DMA_DATA;
+ if (host->req->need_card_int) {
+ dbg_v(host, "IO: Will wait for card interrupt");
+ cmd |= TPC_WAIT_INT;
+ /* No, the TPC_GET_INT doesn't work.... */
}

- if (data_len <= 8) {
- cmd &= ~(TPC_DATA_SEL | 0xf);
+ /* Special case for short TPCs */
+ if (!host->req->long_data && data_len <= 8) {
+ dbg(host, "IO: Using 8 byte register window");
host->cmd_flags |= REG_DATA;
cmd |= data_len & 0xf;
- host->cmd_flags &= ~DMA_DATA;
+
+ if (!is_read)
+ j38ms_write_tpc_inline(host);
+ goto exec;
}

- if (host->cmd_flags & DMA_DATA) {
- if (1 != pci_map_sg(host->chip->pdev, &host->req->sg, 1,
- host->req->data_dir == READ
- ? PCI_DMA_FROMDEVICE
- : PCI_DMA_TODEVICE)) {
+ /* Otherwise use internal fifo*/
+ cmd |= TPC_DATA_SEL;
+
+ if (data_len & 0x03) {
+ dbg(host, "Hardware doesn't support not-aligned len TPCs!");
+ host->req->error = -ENOSYS;
+ return host->req->error;
+ }
+
+ /* DMA */
+ if (!no_dma && host->req->long_data) {
+
+ dbg(host, "IO: Using DMA");
+ host->cmd_flags |= DMA_DATA;
+
+ if (pci_map_sg(host->chip->pdev,
+ &host->req->sg, 1, is_read
+ ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE) != 1) {
+
+ dbg(host, "IO: DMA map failed");
host->req->error = -ENOMEM;
return host->req->error;
}
- data_len = sg_dma_len(&host->req->sg);
- writel(sg_dma_address(&host->req->sg),
- host->addr + DMA_ADDRESS);
- writel(((1 << 16) & BLOCK_COUNT_MASK)
- | (data_len & BLOCK_SIZE_MASK),
- host->addr + BLOCK);
- writel(DMA_CONTROL_ENABLE, host->addr + DMA_CONTROL);
- } else if (!(host->cmd_flags & REG_DATA)) {
- writel(((1 << 16) & BLOCK_COUNT_MASK)
- | (data_len & BLOCK_SIZE_MASK),
- host->addr + BLOCK);
- t_val = readl(host->addr + INT_STATUS_ENABLE);
- t_val |= host->req->data_dir == READ
- ? INT_STATUS_FIFO_RRDY
- : INT_STATUS_FIFO_WRDY;
-
- writel(t_val, host->addr + INT_STATUS_ENABLE);
- writel(t_val, host->addr + INT_SIGNAL_ENABLE);
+
+ /* We really shouldn't pretend we support that case */
+ if (sg_dma_len(&host->req->sg) != data_len) {
+ dbg(host, "IO: DMA len mismatch");
+ host->req->error = -EFAULT;
+ return host->req->error;
+ }
+
+ j38ms_write_reg(host, BLOCK, data_len | BLOCK_COUNT_1BLOCK);
+ j38ms_write_reg(host, DMA_ADDRESS,
+ sg_dma_address(&host->req->sg));
+ j38ms_write_reg(host, DMA_CONTROL, DMA_CONTROL_ENABLE);
+ /* PIO */
} else {
- cmd &= ~(TPC_DATA_SEL | 0xf);
- host->cmd_flags |= REG_DATA;
- cmd |= data_len & 0xf;

- if (host->req->data_dir == WRITE)
- j38ms_write_tpc_inline(host);
- }
+ dbg(host, "IO: Using PIO");
+ host->cmd_flags |= PIO_DATA;

- mod_timer(&host->timer, jiffies + host->timeout_jiffies);
- writel(HOST_CONTROL_LED | readl(host->addr + HOST_CONTROL),
- host->addr + HOST_CONTROL);
- host->req->error = 0;
+ host->pio_offset = 0;
+ host->pio_tmp_buf_len = 0;
+ memset(host->pio_tmp_buf, 0, 4);

- writel(cmd, host->addr + TPC);
- dev_dbg(&msh->dev, "executing TPC %08x, len %x\n", cmd, data_len);
+ if (host->req->long_data)
+ sg_miter_start(&host->pio_sg_iter,
+ &host->req->sg, 1, SG_MITER_ATOMIC |
+ (is_read ? SG_MITER_FROM_SG : SG_MITER_TO_SG));
+
+ /* Enable FIFO empty interrupts */
+ t_val = is_read ? INT_STATUS_FIFO_RRDY : INT_STATUS_FIFO_WRDY;

+ j38ms_write_reg(host, BLOCK, data_len | BLOCK_COUNT_1BLOCK);
+ j38ms_set_reg_mask(host, INT_SIGNAL_ENABLE, t_val);
+ j38ms_set_reg_mask(host, INT_STATUS_ENABLE, t_val);
+ }
+exec:
+ mod_timer(&host->timer, jiffies + host->timeout_jiffies);
+
+ /* Let the TPC fly... */
+ j38ms_write_reg(host, TPC, cmd);
+ j38ms_set_reg_mask(host, HOST_CONTROL, HOST_CONTROL_LED);
return 0;
}

@@ -349,44 +376,42 @@ static int j38ms_execute_tpc(struct memstick_host *msh)
static void j38ms_complete_tpc(struct memstick_host *msh, int last)
{
struct j38ms_host *host = memstick_priv(msh);
- unsigned int t_val = 0;
int rc;
-
del_timer(&host->timer);

- dev_dbg(&msh->dev, "c control %08x\n",
- readl(host->addr + HOST_CONTROL));
- dev_dbg(&msh->dev, "c status %08x\n",
- readl(host->addr + INT_STATUS));
- dev_dbg(&msh->dev, "c hstatus %08x\n", readl(host->addr + STATUS));
+ dbg(host, "IO: TPC complete (error : %d)", host->req->error);

- host->req->int_reg = readl(host->addr + STATUS) & 0xff;
+ j38ms_write_reg(host, BLOCK, 0);
+ j38ms_write_reg(host, DMA_CONTROL, 0);

- writel(0, host->addr + BLOCK);
- writel(0, host->addr + DMA_CONTROL);
+ dbg_v(host, "host control: %08x", j38ms_read_reg(host, HOST_CONTROL));
+ dbg_v(host, "int status: %08x", j38ms_read_reg(host, INT_STATUS));
+ dbg_v(host, "card status: %08x", j38ms_read_reg(host, STATUS));
+
+ if (host->req->need_card_int)
+ host->req->int_reg = j38ms_read_reg(host, STATUS) & 0xFF;

if (host->cmd_flags & DMA_DATA) {
pci_unmap_sg(host->chip->pdev, &host->req->sg, 1,
- host->req->data_dir == READ
- ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);
- } else {
- t_val = readl(host->addr + INT_STATUS_ENABLE);
- if (host->req->data_dir == READ)
- t_val &= ~INT_STATUS_FIFO_RRDY;
- else
- t_val &= ~INT_STATUS_FIFO_WRDY;
+ host->req->data_dir == READ
+ ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE);

- writel(t_val, host->addr + INT_STATUS_ENABLE);
- writel(t_val, host->addr + INT_SIGNAL_ENABLE);
+ } else if (host->cmd_flags & PIO_DATA) {
+ u32 t_val = INT_STATUS_FIFO_RRDY | INT_STATUS_FIFO_WRDY;
+
+ /* This is not known well */
+ j38ms_clear_reg_mask(host, INT_STATUS_ENABLE, t_val);
+
+ /* This disables the IRQ to host, really */
+ j38ms_clear_reg_mask(host, INT_SIGNAL_ENABLE, t_val);
}

- writel((~HOST_CONTROL_LED) & readl(host->addr + HOST_CONTROL),
- host->addr + HOST_CONTROL);
+ j38ms_clear_reg_mask(host, HOST_CONTROL, HOST_CONTROL_LED);

if (!last) {
do {
rc = memstick_next_req(msh, &host->req);
- } while (!rc && j38ms_execute_tpc(msh));
+ } while (!rc && j38ms_execute_tpc(host));
} else {
do {
rc = memstick_next_req(msh, &host->req);
@@ -488,7 +513,7 @@ static void j38ms_submit_tasklet(unsigned long data)
do {
rc = memstick_next_req(msh, &host->req);
dev_dbg(&host->chip->pdev->dev, "tasklet req %d\n", rc);
- } while (!rc && j38ms_execute_tpc(msh));
+ } while (!rc && j38ms_execute_tpc(host));
}
spin_unlock_irqrestore(&host->lock, flags);
}
diff --git a/drivers/memstick/host/jmb38x_ms.h b/drivers/memstick/host/jmb38x_ms.h
index d7dffb6..429ed49 100644
--- a/drivers/memstick/host/jmb38x_ms.h
+++ b/drivers/memstick/host/jmb38x_ms.h
@@ -173,7 +173,8 @@ enum {
CMD_READY = 0x01,
FIFO_READY = 0x02,
REG_DATA = 0x04,
- DMA_DATA = 0x08
+ DMA_DATA = 0x08,
+ PIO_DATA = 0x10
};


--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/