spi_imx.c
字号:
/* * drivers/spi/spi_imx.c * * Copyright (C) 2006 SWAPP * Andrea Paterniani <a.paterniani@swapp-eng.it> * * Initial version inspired by: * linux-2.6.17-rc3-mm1/drivers/spi/pxa2xx_spi.c * * 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. */#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/arch/hardware.h>#include <asm/arch/imx-dma.h>#include <asm/arch/spi_imx.h>/*-------------------------------------------------------------------------*//* SPI Registers offsets from peripheral base address */#define SPI_RXDATA (0x00)#define SPI_TXDATA (0x04)#define SPI_CONTROL (0x08)#define SPI_INT_STATUS (0x0C)#define SPI_TEST (0x10)#define SPI_PERIOD (0x14)#define SPI_DMA (0x18)#define SPI_RESET (0x1C)/* SPI Control Register Bit Fields & Masks */#define SPI_CONTROL_BITCOUNT_MASK (0xF) /* Bit Count Mask */#define SPI_CONTROL_BITCOUNT(n) (((n) - 1) & SPI_CONTROL_BITCOUNT_MASK)#define SPI_CONTROL_POL (0x1 << 4) /* Clock Polarity Mask */#define SPI_CONTROL_POL_ACT_HIGH (0x0 << 4) /* Active high pol. (0=idle) */#define SPI_CONTROL_POL_ACT_LOW (0x1 << 4) /* Active low pol. (1=idle) */#define SPI_CONTROL_PHA (0x1 << 5) /* Clock Phase Mask */#define SPI_CONTROL_PHA_0 (0x0 << 5) /* Clock Phase 0 */#define SPI_CONTROL_PHA_1 (0x1 << 5) /* Clock Phase 1 */#define SPI_CONTROL_SSCTL (0x1 << 6) /* /SS Waveform Select Mask */#define SPI_CONTROL_SSCTL_0 (0x0 << 6) /* Master: /SS stays low between SPI burst Slave: RXFIFO advanced by BIT_COUNT */#define SPI_CONTROL_SSCTL_1 (0x1 << 6) /* Master: /SS insert pulse between SPI burst Slave: RXFIFO advanced by /SS rising edge */#define SPI_CONTROL_SSPOL (0x1 << 7) /* /SS Polarity Select Mask */#define SPI_CONTROL_SSPOL_ACT_LOW (0x0 << 7) /* /SS Active low */#define SPI_CONTROL_SSPOL_ACT_HIGH (0x1 << 7) /* /SS Active high */#define SPI_CONTROL_XCH (0x1 << 8) /* Exchange */#define SPI_CONTROL_SPIEN (0x1 << 9) /* SPI Module Enable */#define SPI_CONTROL_MODE (0x1 << 10) /* SPI Mode Select Mask */#define SPI_CONTROL_MODE_SLAVE (0x0 << 10) /* SPI Mode Slave */#define SPI_CONTROL_MODE_MASTER (0x1 << 10) /* SPI Mode Master */#define SPI_CONTROL_DRCTL (0x3 << 11) /* /SPI_RDY Control Mask */#define SPI_CONTROL_DRCTL_0 (0x0 << 11) /* Ignore /SPI_RDY */#define SPI_CONTROL_DRCTL_1 (0x1 << 11) /* /SPI_RDY falling edge triggers input */#define SPI_CONTROL_DRCTL_2 (0x2 << 11) /* /SPI_RDY active low level triggers input */#define SPI_CONTROL_DATARATE (0x7 << 13) /* Data Rate Mask */#define SPI_PERCLK2_DIV_MIN (0) /* PERCLK2:4 */#define SPI_PERCLK2_DIV_MAX (7) /* PERCLK2:512 */#define SPI_CONTROL_DATARATE_MIN (SPI_PERCLK2_DIV_MAX << 13)#define SPI_CONTROL_DATARATE_MAX (SPI_PERCLK2_DIV_MIN << 13)#define SPI_CONTROL_DATARATE_BAD (SPI_CONTROL_DATARATE_MIN + 1)/* SPI Interrupt/Status Register Bit Fields & Masks */#define SPI_STATUS_TE (0x1 << 0) /* TXFIFO Empty Status */#define SPI_STATUS_TH (0x1 << 1) /* TXFIFO Half Status */#define SPI_STATUS_TF (0x1 << 2) /* TXFIFO Full Status */#define SPI_STATUS_RR (0x1 << 3) /* RXFIFO Data Ready Status */#define SPI_STATUS_RH (0x1 << 4) /* RXFIFO Half Status */#define SPI_STATUS_RF (0x1 << 5) /* RXFIFO Full Status */#define SPI_STATUS_RO (0x1 << 6) /* RXFIFO Overflow */#define SPI_STATUS_BO (0x1 << 7) /* Bit Count Overflow */#define SPI_STATUS (0xFF) /* SPI Status Mask */#define SPI_INTEN_TE (0x1 << 8) /* TXFIFO Empty Interrupt Enable */#define SPI_INTEN_TH (0x1 << 9) /* TXFIFO Half Interrupt Enable */#define SPI_INTEN_TF (0x1 << 10) /* TXFIFO Full Interrupt Enable */#define SPI_INTEN_RE (0x1 << 11) /* RXFIFO Data Ready Interrupt Enable */#define SPI_INTEN_RH (0x1 << 12) /* RXFIFO Half Interrupt Enable */#define SPI_INTEN_RF (0x1 << 13) /* RXFIFO Full Interrupt Enable */#define SPI_INTEN_RO (0x1 << 14) /* RXFIFO Overflow Interrupt Enable */#define SPI_INTEN_BO (0x1 << 15) /* Bit Count Overflow Interrupt Enable */#define SPI_INTEN (0xFF << 8) /* SPI Interrupt Enable Mask *//* SPI Test Register Bit Fields & Masks */#define SPI_TEST_TXCNT (0xF << 0) /* TXFIFO Counter */#define SPI_TEST_RXCNT_LSB (4) /* RXFIFO Counter LSB */#define SPI_TEST_RXCNT (0xF << 4) /* RXFIFO Counter */#define SPI_TEST_SSTATUS (0xF << 8) /* State Machine Status */#define SPI_TEST_LBC (0x1 << 14) /* Loop Back Control *//* SPI Period Register Bit Fields & Masks */#define SPI_PERIOD_WAIT (0x7FFF << 0) /* Wait Between Transactions */#define SPI_PERIOD_MAX_WAIT (0x7FFF) /* Max Wait Between Transactions */#define SPI_PERIOD_CSRC (0x1 << 15) /* Period Clock Source Mask */#define SPI_PERIOD_CSRC_BCLK (0x0 << 15) /* Period Clock Source is Bit Clock */#define SPI_PERIOD_CSRC_32768 (0x1 << 15) /* Period Clock Source is 32.768 KHz Clock *//* SPI DMA Register Bit Fields & Masks */#define SPI_DMA_RHDMA (0x1 << 4) /* RXFIFO Half Status */#define SPI_DMA_RFDMA (0x1 << 5) /* RXFIFO Full Status */#define SPI_DMA_TEDMA (0x1 << 6) /* TXFIFO Empty Status */#define SPI_DMA_THDMA (0x1 << 7) /* TXFIFO Half Status */#define SPI_DMA_RHDEN (0x1 << 12) /* RXFIFO Half DMA Request Enable */#define SPI_DMA_RFDEN (0x1 << 13) /* RXFIFO Full DMA Request Enable */#define SPI_DMA_TEDEN (0x1 << 14) /* TXFIFO Empty DMA Request Enable */#define SPI_DMA_THDEN (0x1 << 15) /* TXFIFO Half DMA Request Enable *//* SPI Soft Reset Register Bit Fields & Masks */#define SPI_RESET_START (0x1) /* Start *//* Default SPI configuration values */#define SPI_DEFAULT_CONTROL \( \ SPI_CONTROL_BITCOUNT(16) | \ SPI_CONTROL_POL_ACT_HIGH | \ SPI_CONTROL_PHA_0 | \ SPI_CONTROL_SPIEN | \ SPI_CONTROL_SSCTL_1 | \ SPI_CONTROL_MODE_MASTER | \ SPI_CONTROL_DRCTL_0 | \ SPI_CONTROL_DATARATE_MIN \)#define SPI_DEFAULT_ENABLE_LOOPBACK (0)#define SPI_DEFAULT_ENABLE_DMA (0)#define SPI_DEFAULT_PERIOD_WAIT (8)/*-------------------------------------------------------------------------*//*-------------------------------------------------------------------------*//* TX/RX SPI FIFO size */#define SPI_FIFO_DEPTH (8)#define SPI_FIFO_BYTE_WIDTH (2)#define SPI_FIFO_OVERFLOW_MARGIN (2)/* DMA burst lenght for half full/empty request trigger */#define SPI_DMA_BLR (SPI_FIFO_DEPTH * SPI_FIFO_BYTE_WIDTH / 2)/* Dummy char output to achieve reads. Choosing something different from all zeroes may help pattern recogition for oscilloscope analysis, but may break some drivers. */#define SPI_DUMMY_u8 0#define SPI_DUMMY_u16 ((SPI_DUMMY_u8 << 8) | SPI_DUMMY_u8)#define SPI_DUMMY_u32 ((SPI_DUMMY_u16 << 16) | SPI_DUMMY_u16)/** * Macro to change a u32 field: * @r : register to edit * @m : bit mask * @v : new value for the field correctly bit-alligned*/#define u32_EDIT(r, m, v) r = (r & ~(m)) | (v)/* Message state */#define START_STATE ((void*)0)#define RUNNING_STATE ((void*)1)#define DONE_STATE ((void*)2)#define ERROR_STATE ((void*)-1)/* Queue state */#define QUEUE_RUNNING (0)#define QUEUE_STOPPED (1)#define IS_DMA_ALIGNED(x) (((u32)(x) & 0x03) == 0)/*-------------------------------------------------------------------------*//*-------------------------------------------------------------------------*//* Driver data structs *//* Context */struct driver_data { /* Driver model hookup */ struct platform_device *pdev; /* SPI framework hookup */ struct spi_master *master; /* IMX hookup */ struct spi_imx_master *master_info; /* Memory resources and SPI regs virtual address */ struct resource *ioarea; void __iomem *regs; /* SPI RX_DATA physical address */ dma_addr_t rd_data_phys; /* Driver message queue */ struct workqueue_struct *workqueue; struct work_struct work; spinlock_t lock; struct list_head queue; int busy; int run; /* Message Transfer pump */ struct tasklet_struct pump_transfers; /* Current message, transfer and state */ struct spi_message *cur_msg; struct spi_transfer *cur_transfer; struct chip_data *cur_chip; /* Rd / Wr buffers pointers */ size_t len; void *tx; void *tx_end; void *rx; void *rx_end; u8 rd_only; u8 n_bytes; int cs_change; /* Function pointers */ irqreturn_t (*transfer_handler)(struct driver_data *drv_data); void (*cs_control)(u32 command); /* DMA setup */ int rx_channel; int tx_channel; dma_addr_t rx_dma; dma_addr_t tx_dma; int rx_dma_needs_unmap; int tx_dma_needs_unmap; size_t tx_map_len; u32 dummy_dma_buf ____cacheline_aligned;};/* Runtime state */struct chip_data { u32 control; u32 period; u32 test; u8 enable_dma:1; u8 bits_per_word; u8 n_bytes; u32 max_speed_hz; 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 __iomem *regs = drv_data->regs; volatile u32 d; dev_dbg(&drv_data->pdev->dev, "flush\n"); do { while (readl(regs + SPI_INT_STATUS) & SPI_STATUS_RR) d = readl(regs + SPI_RXDATA); } while ((readl(regs + SPI_CONTROL) & SPI_CONTROL_XCH) && limit--); return limit;}static void restore_state(struct driver_data *drv_data){ void __iomem *regs = drv_data->regs; struct chip_data *chip = drv_data->cur_chip; /* Load chip registers */ dev_dbg(&drv_data->pdev->dev, "restore_state\n" " test = 0x%08X\n" " control = 0x%08X\n", chip->test, chip->control); writel(chip->test, regs + SPI_TEST); writel(chip->period, regs + SPI_PERIOD); writel(0, regs + SPI_INT_STATUS); writel(chip->control, regs + SPI_CONTROL);}static void null_cs_control(u32 command){}static inline u32 data_to_write(struct driver_data *drv_data){ return ((u32)(drv_data->tx_end - drv_data->tx)) / drv_data->n_bytes;}static inline u32 data_to_read(struct driver_data *drv_data){ return ((u32)(drv_data->rx_end - drv_data->rx)) / drv_data->n_bytes;}static int write(struct driver_data *drv_data){ void __iomem *regs = drv_data->regs; void *tx = drv_data->tx; void *tx_end = drv_data->tx_end; u8 n_bytes = drv_data->n_bytes; u32 remaining_writes; u32 fifo_avail_space; u32 n; u16 d; /* Compute how many fifo writes to do */ remaining_writes = (u32)(tx_end - tx) / n_bytes; fifo_avail_space = SPI_FIFO_DEPTH - (readl(regs + SPI_TEST) & SPI_TEST_TXCNT); if (drv_data->rx && (fifo_avail_space > SPI_FIFO_OVERFLOW_MARGIN)) /* Fix misunderstood receive overflow */ fifo_avail_space -= SPI_FIFO_OVERFLOW_MARGIN; n = min(remaining_writes, fifo_avail_space); dev_dbg(&drv_data->pdev->dev, "write type %s\n" " remaining writes = %d\n" " fifo avail space = %d\n" " fifo writes = %d\n", (n_bytes == 1) ? "u8" : "u16", remaining_writes, fifo_avail_space, n); if (n > 0) { /* Fill SPI TXFIFO */ if (drv_data->rd_only) { tx += n * n_bytes; while (n--) writel(SPI_DUMMY_u16, regs + SPI_TXDATA); } else { if (n_bytes == 1) { while (n--) { d = *(u8*)tx; writel(d, regs + SPI_TXDATA); tx += 1; } } else { while (n--) { d = *(u16*)tx; writel(d, regs + SPI_TXDATA); tx += 2; } } } /* Trigger transfer */ writel(readl(regs + SPI_CONTROL) | SPI_CONTROL_XCH, regs + SPI_CONTROL); /* Update tx pointer */ drv_data->tx = tx; } return (tx >= tx_end);}static int read(struct driver_data *drv_data){ void __iomem *regs = drv_data->regs; void *rx = drv_data->rx; void *rx_end = drv_data->rx_end; u8 n_bytes = drv_data->n_bytes; u32 remaining_reads; u32 fifo_rxcnt; u32 n; u16 d; /* Compute how many fifo reads to do */ remaining_reads = (u32)(rx_end - rx) / n_bytes; fifo_rxcnt = (readl(regs + SPI_TEST) & SPI_TEST_RXCNT) >> SPI_TEST_RXCNT_LSB; n = min(remaining_reads, fifo_rxcnt); dev_dbg(&drv_data->pdev->dev, "read type %s\n" " remaining reads = %d\n" " fifo rx count = %d\n" " fifo reads = %d\n", (n_bytes == 1) ? "u8" : "u16", remaining_reads, fifo_rxcnt, n); if (n > 0) { /* Read SPI RXFIFO */ if (n_bytes == 1) { while (n--) { d = readl(regs + SPI_RXDATA); *((u8*)rx) = d; rx += 1; } } else { while (n--) { d = readl(regs + SPI_RXDATA); *((u16*)rx) = d; rx += 2; } } /* Update rx pointer */ drv_data->rx = rx; } return (rx >= 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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -