⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pxa2xx_spi.c

📁 pxa3xx ssp driver for linux
💻 C
📖 第 1 页 / 共 4 页
字号:
  /*
  * 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/errno.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 Contoller");
  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)
  
  #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 1
  
  struct 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;
         void (*write)(struct driver_data *drv_data);
         void (*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 to;
         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;
         void (*write)(struct driver_data *drv_data);
        void (*read)(struct driver_data *drv_data);
         void (*cs_control)(u32 command);
 };
 
 static void pump_messages(void *data);
 
 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 restore_state(struct driver_data *drv_data)
 {
         void *reg = drv_data->ioaddr;
 
         /* Clear status and disable clock */
         write_SSSR(drv_data->clear_sr, reg);
         write_SSCR0(drv_data->cur_chip->cr0 & ~SSCR0_SSE, reg);
 
         /* Load the registers */
         write_SSCR1(drv_data->cur_chip->cr1, reg);
         write_SSCR0(drv_data->cur_chip->cr0, reg);
         if (drv_data->ssp_type != PXA25x_SSP) {
                 write_SSTO(0, reg);
                 write_SSPSP(drv_data->cur_chip->psp, reg);
         }
 }
 
 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->n_bytes;
 
         while ((read_SSSR(reg) & SSSR_TNF)
                         && (drv_data->tx < drv_data->tx_end)) {
                 write_SSDR(0, reg);
                 drv_data->tx += n_bytes;
         }
 }
 
 static void 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;
         }
 }
 
 static void u8_writer(struct driver_data *drv_data)
 {
         void *reg = drv_data->ioaddr;
 
         while ((read_SSSR(reg) & SSSR_TNF)
                         && (drv_data->tx < drv_data->tx_end)) {
                 write_SSDR(*(u8 *)(drv_data->tx), reg);
                 ++drv_data->tx;
         }
 }
 
 static void 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;
         }
 }
 
 static void u16_writer(struct driver_data *drv_data)
 {
         void *reg = drv_data->ioaddr;
 
         while ((read_SSSR(reg) & SSSR_TNF)
                         && (drv_data->tx < drv_data->tx_end)) {
                 write_SSDR(*(u16 *)(drv_data->tx), reg);
                 drv_data->tx += 2;
         }
 }
 
 static void 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;
         }
 }
 static void u32_writer(struct driver_data *drv_data)
 {
         void *reg = drv_data->ioaddr;
 
         while ((read_SSSR(reg) & SSSR_TNF)
                         && (drv_data->tx < drv_data->tx_end)) {
                 write_SSDR(*(u32 *)(drv_data->tx), reg);
                 drv_data->tx += 4;
         }
 }
 
 static void 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;
         }
 }
 
 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);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -