]> err.no Git - linux-2.6/blobdiff - drivers/spi/pxa2xx_spi.c
[S390] xpram off by one error.
[linux-2.6] / drivers / spi / pxa2xx_spi.c
index 913e1aff023527d7e78f40eeb93160bb7ccfea59..29aec77f98be53a758311f78d71cc86c26ec7a64 100644 (file)
@@ -120,6 +120,8 @@ struct driver_data {
        dma_addr_t tx_dma;
        size_t rx_map_len;
        size_t tx_map_len;
+       u8 n_bytes;
+       u32 dma_width;
        int cs_change;
        void (*write)(struct driver_data *drv_data);
        void (*read)(struct driver_data *drv_data);
@@ -139,6 +141,8 @@ struct chip_data {
        u32 threshold;
        u32 dma_threshold;
        u8 enable_dma;
+       u8 bits_per_word;
+       u32 speed_hz;
        void (*write)(struct driver_data *drv_data);
        void (*read)(struct driver_data *drv_data);
        void (*cs_control)(u32 command);
@@ -186,7 +190,7 @@ static void null_cs_control(u32 command)
 static void null_writer(struct driver_data *drv_data)
 {
        void *reg = drv_data->ioaddr;
-       u8 n_bytes = drv_data->cur_chip->n_bytes;
+       u8 n_bytes = drv_data->n_bytes;
 
        while ((read_SSSR(reg) & SSSR_TNF)
                        && (drv_data->tx < drv_data->tx_end)) {
@@ -198,7 +202,7 @@ static void null_writer(struct driver_data *drv_data)
 static void null_reader(struct driver_data *drv_data)
 {
        void *reg = drv_data->ioaddr;
-       u8 n_bytes = drv_data->cur_chip->n_bytes;
+       u8 n_bytes = drv_data->n_bytes;
 
        while ((read_SSSR(reg) & SSSR_RNE)
                        && (drv_data->rx < drv_data->rx_end)) {
@@ -256,7 +260,7 @@ static void u32_writer(struct driver_data *drv_data)
 
        while ((read_SSSR(reg) & SSSR_TNF)
                        && (drv_data->tx < drv_data->tx_end)) {
-               write_SSDR(*(u16 *)(drv_data->tx), reg);
+               write_SSDR(*(u32 *)(drv_data->tx), reg);
                drv_data->tx += 4;
        }
 }
@@ -359,25 +363,30 @@ static void unmap_dma_buffers(struct driver_data *drv_data)
 }
 
 /* caller already set message->status; dma and pio irqs are blocked */
-static void giveback(struct spi_message *message, struct driver_data *drv_data)
+static void giveback(struct driver_data *drv_data)
 {
        struct spi_transfer* last_transfer;
+       unsigned long flags;
+       struct spi_message *msg;
+
+       spin_lock_irqsave(&drv_data->lock, flags);
+       msg = drv_data->cur_msg;
+       drv_data->cur_msg = NULL;
+       drv_data->cur_transfer = NULL;
+       drv_data->cur_chip = NULL;
+       queue_work(drv_data->workqueue, &drv_data->pump_messages);
+       spin_unlock_irqrestore(&drv_data->lock, flags);
 
-       last_transfer = list_entry(message->transfers.prev,
+       last_transfer = list_entry(msg->transfers.prev,
                                        struct spi_transfer,
                                        transfer_list);
 
        if (!last_transfer->cs_change)
                drv_data->cs_control(PXA2XX_CS_DEASSERT);
 
-       message->state = NULL;
-       if (message->complete)
-               message->complete(message->context);
-
-       drv_data->cur_msg = NULL;
-       drv_data->cur_transfer = NULL;
-       drv_data->cur_chip = NULL;
-       queue_work(drv_data->workqueue, &drv_data->pump_messages);
+       msg->state = NULL;
+       if (msg->complete)
+               msg->complete(msg->context);
 }
 
 static int wait_ssp_rx_stall(void *ioaddr)
@@ -411,10 +420,11 @@ static void dma_handler(int channel, void *data, struct pt_regs *regs)
        if (irq_status & DCSR_BUSERR) {
 
                /* Disable interrupts, clear status and reset DMA */
+               write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
+               write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                if (drv_data->ssp_type != PXA25x_SSP)
                        write_SSTO(0, reg);
                write_SSSR(drv_data->clear_sr, reg);
-               write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
                DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
 
@@ -450,8 +460,8 @@ static void dma_handler(int channel, void *data, struct pt_regs *regs)
                                "dma_handler: ssp rx stall failed\n");
 
                /* Clear and disable interrupts on SSP and DMA channels*/
-               write_SSSR(drv_data->clear_sr, reg);
                write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
+               write_SSSR(drv_data->clear_sr, reg);
                DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
                DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
                if (wait_dma_channel_stop(drv_data->rx_channel) == 0)
@@ -493,10 +503,11 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
        irq_status = read_SSSR(reg) & drv_data->mask_sr;
        if (irq_status & SSSR_ROR) {
                /* Clear and disable interrupts on SSP and DMA channels*/
+               write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
+               write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                if (drv_data->ssp_type != PXA25x_SSP)
                        write_SSTO(0, reg);
                write_SSSR(drv_data->clear_sr, reg);
-               write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
                DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
                unmap_dma_buffers(drv_data);
@@ -522,10 +533,10 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
        if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) {
 
                /* Clear and disable interrupts on SSP and DMA channels*/
+               write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                if (drv_data->ssp_type != PXA25x_SSP)
                        write_SSTO(0, reg);
                write_SSSR(drv_data->clear_sr, reg);
-               write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg);
                DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL;
                DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL;
 
@@ -568,26 +579,30 @@ static irqreturn_t dma_transfer(struct driver_data *drv_data)
 
 static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
 {
-       u32 irq_status;
        struct spi_message *msg = drv_data->cur_msg;
        void *reg = drv_data->ioaddr;
-       irqreturn_t handled = IRQ_NONE;
        unsigned long limit = loops_per_jiffy << 1;
+       u32 irq_status;
+       u32 irq_mask = (read_SSCR1(reg) & SSCR1_TIE) ?
+                       drv_data->mask_sr : drv_data->mask_sr & ~SSSR_TFS;
 
-       while ((irq_status = (read_SSSR(reg) & drv_data->mask_sr))) {
+       while ((irq_status = read_SSSR(reg) & irq_mask)) {
 
                if (irq_status & SSSR_ROR) {
 
                        /* Clear and disable interrupts */
+                       write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
+                       write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
                        if (drv_data->ssp_type != PXA25x_SSP)
                                write_SSTO(0, reg);
                        write_SSSR(drv_data->clear_sr, reg);
-                       write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
 
                        if (flush(drv_data) == 0)
                                dev_err(&drv_data->pdev->dev,
                                        "interrupt_transfer: flush fail\n");
 
+                       /* Stop the SSP */
+
                        dev_warn(&drv_data->pdev->dev,
                                        "interrupt_transfer: fifo overun\n");
 
@@ -609,6 +624,7 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
                if (drv_data->tx == drv_data->tx_end) {
                        /* Disable tx interrupt */
                        write_SSCR1(read_SSCR1(reg) & ~SSCR1_TIE, reg);
+                       irq_mask = drv_data->mask_sr & ~SSSR_TFS;
 
                        /* PXA25x_SSP has no timeout, read trailing bytes */
                        if (drv_data->ssp_type == PXA25x_SSP) {
@@ -626,10 +642,10 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
                                || (drv_data->rx == drv_data->rx_end)) {
 
                        /* Clear timeout */
+                       write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
                        if (drv_data->ssp_type != PXA25x_SSP)
                                write_SSTO(0, reg);
                        write_SSSR(drv_data->clear_sr, reg);
-                       write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
 
                        /* Update total byte transfered */
                        msg->actual_length += drv_data->len;
@@ -644,24 +660,29 @@ static irqreturn_t interrupt_transfer(struct driver_data *drv_data)
 
                        /* Schedule transfer tasklet */
                        tasklet_schedule(&drv_data->pump_transfers);
-
-                       return IRQ_HANDLED;
                }
-
-               /* We did something */
-               handled = IRQ_HANDLED;
        }
 
-       return handled;
+       /* We did something */
+       return IRQ_HANDLED;
 }
 
 static irqreturn_t ssp_int(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct driver_data *drv_data = (struct driver_data *)dev_id;
+       void *reg = drv_data->ioaddr;
 
        if (!drv_data->cur_msg) {
+
+               write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg);
+               write_SSCR1(read_SSCR1(reg) & ~drv_data->int_cr1, reg);
+               if (drv_data->ssp_type != PXA25x_SSP)
+                       write_SSTO(0, reg);
+               write_SSSR(drv_data->clear_sr, reg);
+
                dev_err(&drv_data->pdev->dev, "bad message state "
-                               "in interrupt handler\n");
+                               "in interrupt handler");
+
                /* Never fail */
                return IRQ_HANDLED;
        }
@@ -677,6 +698,10 @@ static void pump_transfers(unsigned long data)
        struct spi_transfer *previous = NULL;
        struct chip_data *chip = NULL;
        void *reg = drv_data->ioaddr;
+       u32 clk_div = 0;
+       u8 bits = 0;
+       u32 speed = 0;
+       u32 cr0;
 
        /* Get current state information */
        message = drv_data->cur_msg;
@@ -686,14 +711,14 @@ static void pump_transfers(unsigned long data)
        /* Handle for abort */
        if (message->state == ERROR_STATE) {
                message->status = -EIO;
-               giveback(message, drv_data);
+               giveback(drv_data);
                return;
        }
 
        /* Handle end of message */
        if (message->state == DONE_STATE) {
                message->status = 0;
-               giveback(message, drv_data);
+               giveback(drv_data);
                return;
        }
 
@@ -710,9 +735,11 @@ static void pump_transfers(unsigned long data)
        if (flush(drv_data) == 0) {
                dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed\n");
                message->status = -EIO;
-               giveback(message, drv_data);
+               giveback(drv_data);
                return;
        }
+       drv_data->n_bytes = chip->n_bytes;
+       drv_data->dma_width = chip->dma_width;
        drv_data->cs_control = chip->cs_control;
        drv_data->tx = (void *)transfer->tx_buf;
        drv_data->tx_end = drv_data->tx + transfer->len;
@@ -724,6 +751,62 @@ static void pump_transfers(unsigned long data)
        drv_data->write = drv_data->tx ? chip->write : null_writer;
        drv_data->read = drv_data->rx ? chip->read : null_reader;
        drv_data->cs_change = transfer->cs_change;
+
+       /* Change speed and bit per word on a per transfer */
+       if (transfer->speed_hz || transfer->bits_per_word) {
+
+               /* Disable clock */
+               write_SSCR0(chip->cr0 & ~SSCR0_SSE, reg);
+               cr0 = chip->cr0;
+               bits = chip->bits_per_word;
+               speed = chip->speed_hz;
+
+               if (transfer->speed_hz)
+                       speed = transfer->speed_hz;
+
+               if (transfer->bits_per_word)
+                       bits = transfer->bits_per_word;
+
+               if (reg == SSP1_VIRT)
+                       clk_div = SSP1_SerClkDiv(speed);
+               else if (reg == SSP2_VIRT)
+                       clk_div = SSP2_SerClkDiv(speed);
+               else if (reg == SSP3_VIRT)
+                       clk_div = SSP3_SerClkDiv(speed);
+
+               if (bits <= 8) {
+                       drv_data->n_bytes = 1;
+                       drv_data->dma_width = DCMD_WIDTH1;
+                       drv_data->read = drv_data->read != null_reader ?
+                                               u8_reader : null_reader;
+                       drv_data->write = drv_data->write != null_writer ?
+                                               u8_writer : null_writer;
+               } else if (bits <= 16) {
+                       drv_data->n_bytes = 2;
+                       drv_data->dma_width = DCMD_WIDTH2;
+                       drv_data->read = drv_data->read != null_reader ?
+                                               u16_reader : null_reader;
+                       drv_data->write = drv_data->write != null_writer ?
+                                               u16_writer : null_writer;
+               } else if (bits <= 32) {
+                       drv_data->n_bytes = 4;
+                       drv_data->dma_width = DCMD_WIDTH4;
+                       drv_data->read = drv_data->read != null_reader ?
+                                               u32_reader : null_reader;
+                       drv_data->write = drv_data->write != null_writer ?
+                                               u32_writer : null_writer;
+               }
+
+               cr0 = clk_div
+                       | SSCR0_Motorola
+                       | SSCR0_DataSize(bits > 16 ? bits - 16 : bits)
+                       | SSCR0_SSE
+                       | (bits > 16 ? SSCR0_EDSS : 0);
+
+               /* Start it back up */
+               write_SSCR0(cr0, reg);
+       }
+
        message->state = RUNNING_STATE;
 
        /* Try to map dma buffer and do a dma transfer if successful */
@@ -739,13 +822,13 @@ static void pump_transfers(unsigned long data)
                if (drv_data->rx == drv_data->null_dma_buf)
                        /* No target address increment */
                        DCMD(drv_data->rx_channel) = DCMD_FLOWSRC
-                                                       | chip->dma_width
+                                                       | drv_data->dma_width
                                                        | chip->dma_burst_size
                                                        | drv_data->len;
                else
                        DCMD(drv_data->rx_channel) = DCMD_INCTRGADDR
                                                        | DCMD_FLOWSRC
-                                                       | chip->dma_width
+                                                       | drv_data->dma_width
                                                        | chip->dma_burst_size
                                                        | drv_data->len;
 
@@ -756,13 +839,13 @@ static void pump_transfers(unsigned long data)
                if (drv_data->tx == drv_data->null_dma_buf)
                        /* No source address increment */
                        DCMD(drv_data->tx_channel) = DCMD_FLOWTRG
-                                                       | chip->dma_width
+                                                       | drv_data->dma_width
                                                        | chip->dma_burst_size
                                                        | drv_data->len;
                else
                        DCMD(drv_data->tx_channel) = DCMD_INCSRCADDR
                                                        | DCMD_FLOWTRG
-                                                       | chip->dma_width
+                                                       | drv_data->dma_width
                                                        | chip->dma_burst_size
                                                        | drv_data->len;
 
@@ -824,8 +907,6 @@ static void pump_messages(void *data)
        drv_data->cur_msg = list_entry(drv_data->queue.next,
                                        struct spi_message, queue);
        list_del_init(&drv_data->cur_msg->queue);
-       drv_data->busy = 1;
-       spin_unlock_irqrestore(&drv_data->lock, flags);
 
        /* Initial message state*/
        drv_data->cur_msg->state = START_STATE;
@@ -839,6 +920,9 @@ static void pump_messages(void *data)
 
        /* Mark as busy and launch transfers */
        tasklet_schedule(&drv_data->pump_transfers);
+
+       drv_data->busy = 1;
+       spin_unlock_irqrestore(&drv_data->lock, flags);
 }
 
 static int transfer(struct spi_device *spi, struct spi_message *msg)
@@ -892,7 +976,7 @@ static int setup(struct spi_device *spi)
 
                chip->cs_control = null_cs_control;
                chip->enable_dma = 0;
-               chip->timeout = 5;
+               chip->timeout = SSP_TIMEOUT(1000);
                chip->threshold = SSCR1_RxTresh(1) | SSCR1_TxTresh(1);
                chip->dma_burst_size = drv_data->master_info->enable_dma ?
                                        DCMD_BURST8 : 0;
@@ -905,7 +989,7 @@ static int setup(struct spi_device *spi)
                if (chip_info->cs_control)
                        chip->cs_control = chip_info->cs_control;
 
-               chip->timeout = (chip_info->timeout_microsecs * 10000) / 2712;
+               chip->timeout = SSP_TIMEOUT(chip_info->timeout_microsecs);
 
                chip->threshold = SSCR1_RxTresh(chip_info->rx_threshold)
                                        | SSCR1_TxTresh(chip_info->tx_threshold);
@@ -943,10 +1027,12 @@ static int setup(struct spi_device *spi)
                clk_div = SSP3_SerClkDiv(spi->max_speed_hz);
        else
                return -ENODEV;
+       chip->speed_hz = spi->max_speed_hz;
 
        chip->cr0 = clk_div
                        | SSCR0_Motorola
-                       | SSCR0_DataSize(spi->bits_per_word & 0x0f)
+                       | SSCR0_DataSize(spi->bits_per_word > 16 ?
+                               spi->bits_per_word - 16 : spi->bits_per_word)
                        | SSCR0_SSE
                        | (spi->bits_per_word > 16 ? SSCR0_EDSS : 0);
        chip->cr1 |= (((spi->mode & SPI_CPHA) != 0) << 4)
@@ -987,6 +1073,7 @@ static int setup(struct spi_device *spi)
                kfree(chip);
                return -ENODEV;
        }
+       chip->bits_per_word = spi->bits_per_word;
 
        spi_set_ctldata(spi, chip);
 
@@ -1128,7 +1215,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
                goto out_error_master_alloc;
        }
 
-       drv_data->ioaddr = (void *)io_p2v(memory_resource->start);
+       drv_data->ioaddr = (void *)io_p2v((unsigned long)(memory_resource->start));
        drv_data->ssdr_physical = memory_resource->start + 0x00000010;
        if (platform_info->ssp_type == PXA25x_SSP) {
                drv_data->int_cr1 = SSCR1_TIE | SSCR1_RIE;
@@ -1150,7 +1237,7 @@ static int pxa2xx_spi_probe(struct platform_device *pdev)
                goto out_error_master_alloc;
        }
 
-       status = request_irq(irq, ssp_int, SA_INTERRUPT, dev->bus_id, drv_data);
+       status = request_irq(irq, ssp_int, 0, dev->bus_id, drv_data);
        if (status < 0) {
                dev_err(&pdev->dev, "can not get IRQ\n");
                goto out_error_master_alloc;