pxa2xx_spi.c
来自「linux 内核源代码」· C语言 代码 · 共 1,641 行 · 第 1/3 页
C
1,641 行
/* * Copyright (C) 2005 Stephen Street / StreetFire Sound Labs * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/init.h>#include <linux/module.h>#include <linux/device.h>#include <linux/ioport.h>#include <linux/errno.h>#include <linux/interrupt.h>#include <linux/platform_device.h>#include <linux/dma-mapping.h>#include <linux/spi/spi.h>#include <linux/workqueue.h>#include <linux/delay.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/hardware.h>#include <asm/delay.h>#include <asm/dma.h>#include <asm/arch/hardware.h>#include <asm/arch/pxa-regs.h>#include <asm/arch/pxa2xx_spi.h>MODULE_AUTHOR("Stephen Street");MODULE_DESCRIPTION("PXA2xx SSP SPI Controller");MODULE_LICENSE("GPL");#define MAX_BUSES 3#define DMA_INT_MASK (DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR)#define RESET_DMA_CHANNEL (DCSR_NODESC | DMA_INT_MASK)#define IS_DMA_ALIGNED(x) (((u32)(x)&0x07)==0)/* for testing SSCR1 changes that require SSP restart, basically * everything except the service and interrupt enables */#define SSCR1_CHANGE_MASK (SSCR1_TTELP | SSCR1_TTE | SSCR1_EBCEI | SSCR1_SCFR \ | SSCR1_ECRA | SSCR1_ECRB | SSCR1_SCLKDIR \ | SSCR1_RWOT | SSCR1_TRAIL | SSCR1_PINTE \ | SSCR1_STRF | SSCR1_EFWR |SSCR1_RFT \ | SSCR1_TFT | SSCR1_SPH | SSCR1_SPO | SSCR1_LBM)#define DEFINE_SSP_REG(reg, off) \static inline u32 read_##reg(void *p) { return __raw_readl(p + (off)); } \static inline void write_##reg(u32 v, void *p) { __raw_writel(v, p + (off)); }DEFINE_SSP_REG(SSCR0, 0x00)DEFINE_SSP_REG(SSCR1, 0x04)DEFINE_SSP_REG(SSSR, 0x08)DEFINE_SSP_REG(SSITR, 0x0c)DEFINE_SSP_REG(SSDR, 0x10)DEFINE_SSP_REG(SSTO, 0x28)DEFINE_SSP_REG(SSPSP, 0x2c)#define START_STATE ((void*)0)#define RUNNING_STATE ((void*)1)#define DONE_STATE ((void*)2)#define ERROR_STATE ((void*)-1)#define QUEUE_RUNNING 0#define QUEUE_STOPPED 1struct driver_data { /* Driver model hookup */ struct platform_device *pdev; /* SPI framework hookup */ enum pxa_ssp_type ssp_type; struct spi_master *master; /* PXA hookup */ struct pxa2xx_spi_master *master_info; /* DMA setup stuff */ int rx_channel; int tx_channel; u32 *null_dma_buf; /* SSP register addresses */ void *ioaddr; u32 ssdr_physical; /* SSP masks*/ u32 dma_cr1; u32 int_cr1; u32 clear_sr; u32 mask_sr; /* Driver message queue */ struct workqueue_struct *workqueue; struct work_struct pump_messages; spinlock_t lock; struct list_head queue; int busy; int run; /* Message Transfer pump */ struct tasklet_struct pump_transfers; /* Current message transfer state info */ struct spi_message* cur_msg; struct spi_transfer* cur_transfer; struct chip_data *cur_chip; size_t len; void *tx; void *tx_end; void *rx; void *rx_end; int dma_mapped; dma_addr_t rx_dma; dma_addr_t tx_dma; size_t rx_map_len; size_t tx_map_len; u8 n_bytes; u32 dma_width; int cs_change; int (*write)(struct driver_data *drv_data); int (*read)(struct driver_data *drv_data); irqreturn_t (*transfer_handler)(struct driver_data *drv_data); void (*cs_control)(u32 command);};struct chip_data { u32 cr0; u32 cr1; u32 psp; u32 timeout; u8 n_bytes; u32 dma_width; u32 dma_burst_size; u32 threshold; u32 dma_threshold; u8 enable_dma; u8 bits_per_word; u32 speed_hz; int (*write)(struct driver_data *drv_data); int (*read)(struct driver_data *drv_data); void (*cs_control)(u32 command);};static void pump_messages(struct work_struct *work);static int flush(struct driver_data *drv_data){ unsigned long limit = loops_per_jiffy << 1; void *reg = drv_data->ioaddr; do { while (read_SSSR(reg) & SSSR_RNE) { read_SSDR(reg); } } while ((read_SSSR(reg) & SSSR_BSY) && limit--); write_SSSR(SSSR_ROR, reg); return limit;}static void null_cs_control(u32 command){}static int null_writer(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) || (drv_data->tx == drv_data->tx_end)) return 0; write_SSDR(0, reg); drv_data->tx += n_bytes; return 1;}static int null_reader(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; u8 n_bytes = drv_data->n_bytes; while ((read_SSSR(reg) & SSSR_RNE) && (drv_data->rx < drv_data->rx_end)) { read_SSDR(reg); drv_data->rx += n_bytes; } return drv_data->rx == drv_data->rx_end;}static int u8_writer(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) || (drv_data->tx == drv_data->tx_end)) return 0; write_SSDR(*(u8 *)(drv_data->tx), reg); ++drv_data->tx; return 1;}static int u8_reader(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; while ((read_SSSR(reg) & SSSR_RNE) && (drv_data->rx < drv_data->rx_end)) { *(u8 *)(drv_data->rx) = read_SSDR(reg); ++drv_data->rx; } return drv_data->rx == drv_data->rx_end;}static int u16_writer(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) || (drv_data->tx == drv_data->tx_end)) return 0; write_SSDR(*(u16 *)(drv_data->tx), reg); drv_data->tx += 2; return 1;}static int u16_reader(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; while ((read_SSSR(reg) & SSSR_RNE) && (drv_data->rx < drv_data->rx_end)) { *(u16 *)(drv_data->rx) = read_SSDR(reg); drv_data->rx += 2; } return drv_data->rx == drv_data->rx_end;}static int u32_writer(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; if (((read_SSSR(reg) & 0x00000f00) == 0x00000f00) || (drv_data->tx == drv_data->tx_end)) return 0; write_SSDR(*(u32 *)(drv_data->tx), reg); drv_data->tx += 4; return 1;}static int u32_reader(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; while ((read_SSSR(reg) & SSSR_RNE) && (drv_data->rx < drv_data->rx_end)) { *(u32 *)(drv_data->rx) = read_SSDR(reg); drv_data->rx += 4; } return drv_data->rx == drv_data->rx_end;}static void *next_transfer(struct driver_data *drv_data){ struct spi_message *msg = drv_data->cur_msg; struct spi_transfer *trans = drv_data->cur_transfer; /* Move to next transfer */ if (trans->transfer_list.next != &msg->transfers) { drv_data->cur_transfer = list_entry(trans->transfer_list.next, struct spi_transfer, transfer_list); return RUNNING_STATE; } else return DONE_STATE;}static int map_dma_buffers(struct driver_data *drv_data){ struct spi_message *msg = drv_data->cur_msg; struct device *dev = &msg->spi->dev; if (!drv_data->cur_chip->enable_dma) return 0; if (msg->is_dma_mapped) return drv_data->rx_dma && drv_data->tx_dma; if (!IS_DMA_ALIGNED(drv_data->rx) || !IS_DMA_ALIGNED(drv_data->tx)) return 0; /* Modify setup if rx buffer is null */ if (drv_data->rx == NULL) { *drv_data->null_dma_buf = 0; drv_data->rx = drv_data->null_dma_buf; drv_data->rx_map_len = 4; } else drv_data->rx_map_len = drv_data->len; /* Modify setup if tx buffer is null */ if (drv_data->tx == NULL) { *drv_data->null_dma_buf = 0; drv_data->tx = drv_data->null_dma_buf; drv_data->tx_map_len = 4; } else drv_data->tx_map_len = drv_data->len; /* Stream map the rx buffer */ drv_data->rx_dma = dma_map_single(dev, drv_data->rx, drv_data->rx_map_len, DMA_FROM_DEVICE); if (dma_mapping_error(drv_data->rx_dma)) return 0; /* Stream map the tx buffer */ drv_data->tx_dma = dma_map_single(dev, drv_data->tx, drv_data->tx_map_len, DMA_TO_DEVICE); if (dma_mapping_error(drv_data->tx_dma)) { dma_unmap_single(dev, drv_data->rx_dma, drv_data->rx_map_len, DMA_FROM_DEVICE); return 0; } return 1;}static void unmap_dma_buffers(struct driver_data *drv_data){ struct device *dev; if (!drv_data->dma_mapped) return; if (!drv_data->cur_msg->is_dma_mapped) { dev = &drv_data->cur_msg->spi->dev; dma_unmap_single(dev, drv_data->rx_dma, drv_data->rx_map_len, DMA_FROM_DEVICE); dma_unmap_single(dev, drv_data->tx_dma, drv_data->tx_map_len, DMA_TO_DEVICE); } drv_data->dma_mapped = 0;}/* caller already set message->status; dma and pio irqs are blocked */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(msg->transfers.prev, struct spi_transfer, transfer_list); if (!last_transfer->cs_change) drv_data->cs_control(PXA2XX_CS_DEASSERT); msg->state = NULL; if (msg->complete) msg->complete(msg->context);}static int wait_ssp_rx_stall(void *ioaddr){ unsigned long limit = loops_per_jiffy << 1; while ((read_SSSR(ioaddr) & SSSR_BSY) && limit--) cpu_relax(); return limit;}static int wait_dma_channel_stop(int channel){ unsigned long limit = loops_per_jiffy << 1; while (!(DCSR(channel) & DCSR_STOPSTATE) && limit--) cpu_relax(); return limit;}void dma_error_stop(struct driver_data *drv_data, const char *msg){ void *reg = drv_data->ioaddr; /* Stop and reset */ DCSR(drv_data->rx_channel) = RESET_DMA_CHANNEL; DCSR(drv_data->tx_channel) = RESET_DMA_CHANNEL; write_SSSR(drv_data->clear_sr, reg); write_SSCR1(read_SSCR1(reg) & ~drv_data->dma_cr1, reg); if (drv_data->ssp_type != PXA25x_SSP) write_SSTO(0, reg); flush(drv_data); write_SSCR0(read_SSCR0(reg) & ~SSCR0_SSE, reg); unmap_dma_buffers(drv_data); dev_err(&drv_data->pdev->dev, "%s\n", msg); drv_data->cur_msg->state = ERROR_STATE; tasklet_schedule(&drv_data->pump_transfers);}static void dma_transfer_complete(struct driver_data *drv_data){ void *reg = drv_data->ioaddr; struct spi_message *msg = drv_data->cur_msg; /* Clear and disable interrupts on SSP and DMA channels*/ 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) dev_err(&drv_data->pdev->dev, "dma_handler: dma rx channel stop failed\n"); if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) dev_err(&drv_data->pdev->dev, "dma_transfer: ssp rx stall failed\n"); unmap_dma_buffers(drv_data); /* update the buffer pointer for the amount completed in dma */ drv_data->rx += drv_data->len - (DCMD(drv_data->rx_channel) & DCMD_LENGTH); /* read trailing data from fifo, it does not matter how many * bytes are in the fifo just read until buffer is full * or fifo is empty, which ever occurs first */ drv_data->read(drv_data); /* return count of what was actually read */ msg->actual_length += drv_data->len - (drv_data->rx_end - drv_data->rx); /* Release chip select if requested, transfer delays are * handled in pump_transfers */ if (drv_data->cs_change) drv_data->cs_control(PXA2XX_CS_DEASSERT); /* Move to next transfer */ msg->state = next_transfer(drv_data); /* Schedule transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers);}static void dma_handler(int channel, void *data){ struct driver_data *drv_data = data; u32 irq_status = DCSR(channel) & DMA_INT_MASK; if (irq_status & DCSR_BUSERR) { if (channel == drv_data->tx_channel) dma_error_stop(drv_data, "dma_handler: " "bad bus address on tx channel"); else dma_error_stop(drv_data, "dma_handler: " "bad bus address on rx channel"); return; } /* PXA255x_SSP has no timeout interrupt, wait for tailing bytes */ if ((channel == drv_data->tx_channel) && (irq_status & DCSR_ENDINTR) && (drv_data->ssp_type == PXA25x_SSP)) { /* Wait for rx to stall */ if (wait_ssp_rx_stall(drv_data->ioaddr) == 0) dev_err(&drv_data->pdev->dev, "dma_handler: ssp rx stall failed\n"); /* finish this transfer, start the next */ dma_transfer_complete(drv_data); }}static irqreturn_t dma_transfer(struct driver_data *drv_data){ u32 irq_status; void *reg = drv_data->ioaddr; irq_status = read_SSSR(reg) & drv_data->mask_sr; if (irq_status & SSSR_ROR) { dma_error_stop(drv_data, "dma_transfer: fifo overrun"); return IRQ_HANDLED; } /* Check for false positive timeout */ if ((irq_status & SSSR_TINT) && (DCSR(drv_data->tx_channel) & DCSR_RUN)) { write_SSSR(SSSR_TINT, reg); return IRQ_HANDLED; } if (irq_status & SSSR_TINT || drv_data->rx == drv_data->rx_end) { /* Clear and disable timeout interrupt, do the rest in * dma_transfer_complete */ if (drv_data->ssp_type != PXA25x_SSP) write_SSTO(0, reg);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?