add spi driver support

This commit is contained in:
luowei
2014-03-07 19:01:09 +08:00
parent 481ff175a5
commit b1d4d15af2
8 changed files with 2389 additions and 0 deletions

17
drivers/spi/Kconfig Normal file → Executable file
View File

@@ -508,6 +508,23 @@ config SPI_DW_MMIO
tristate "Memory-mapped io interface driver for DW SPI core"
depends on SPI_DESIGNWARE && HAVE_CLK
config SPI_ROCKCHIP_CORE
tristate "ROCKCHIP SPI controller core support"
help
general driver for SPI controller core from ROCKCHIP
config SPI_ROCKCHIP
tristate "ROCKCHIP SPI interface driver"
depends on SPI_ROCKCHIP_CORE
config SPI_ROCKCHIP_DMA
bool "DMA support for ROCKCHIP SPI"
depends on SPI_ROCKCHIP
config SPI_ROCKCHIP_TEST
bool "ROCKCHIP spi test code"
depends on SPI_ROCKCHIP
#
# There are lots of SPI device types, with sensors and memory
# being probably the most widely used ones.

4
drivers/spi/Makefile Normal file → Executable file
View File

@@ -74,3 +74,7 @@ obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o
obj-$(CONFIG_SPI_TXX9) += spi-txx9.o
obj-$(CONFIG_SPI_XCOMM) += spi-xcomm.o
obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
obj-$(CONFIG_SPI_ROCKCHIP_CORE) += spi-rockchip-core.o
obj-$(CONFIG_SPI_ROCKCHIP_DMA) += spi-rockchip-dma.o
obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
obj-$(CONFIG_SPI_ROCKCHIP_TEST) += spi-rockchip-test.o

1106
drivers/spi/spi-rockchip-core.c Executable file

File diff suppressed because it is too large Load Diff

355
drivers/spi/spi-rockchip-core.h Executable file
View File

@@ -0,0 +1,355 @@
#ifndef DW_SPI_HEADER_H
#define DW_SPI_HEADER_H
#include <linux/io.h>
#include <linux/scatterlist.h>
#if 1
#define DBG_SPI(x...) if(atomic_read(&dws->debug_flag) == 1) printk(x)
#else
#define DBG_SPI(x...)
#endif
/* SPI register offsets */
#define SPIM_CTRLR0 0x0000
#define SPIM_CTRLR1 0x0004
#define SPIM_SSIENR 0x0008
#define SPIM_SER 0x000c
#define SPIM_BAUDR 0x0010
#define SPIM_TXFTLR 0x0014
#define SPIM_RXFTLR 0x0018
#define SPIM_TXFLR 0x001c
#define SPIM_RXFLR 0x0020
#define SPIM_SR 0x0024
#define SPIM_IPR 0x0028
#define SPIM_IMR 0x002c
#define SPIM_ISR 0x0030
#define SPIM_RISR 0x0034
#define SPIM_ICR 0x0038
#define SPIM_DMACR 0x003c
#define SPIM_DMATDLR 0x0040
#define SPIM_DMARDLR 0x0044
#define SPIM_TXDR 0x0400
#define SPIM_RXDR 0x0800
/* --------Bit fields in CTRLR0--------begin */
#define SPI_DFS_OFFSET 0 /* Data Frame Size */
#define SPI_DFS_4BIT 0x00
#define SPI_DFS_8BIT 0x01
#define SPI_DFS_16BIT 0x02
#define SPI_DFS_RESV 0x03
#define SPI_FRF_OFFSET 16 /* Frame Format */
#define SPI_FRF_SPI 0x00 /* motorola spi */
#define SPI_FRF_SSP 0x01 /* Texas Instruments SSP*/
#define SPI_FRF_MICROWIRE 0x02 /* National Semiconductors Microwire */
#define SPI_FRF_RESV 0x03
#define SPI_MODE_OFFSET 6 /* SCPH & SCOL */
#define SPI_SCPH_OFFSET 6 /* Serial Clock Phase */
#define SPI_SCPH_TOGMID 0 /* Serial clock toggles in middle of first data bit */
#define SPI_SCPH_TOGSTA 1 /* Serial clock toggles at start of first data bit */
#define SPI_SCOL_OFFSET 7 /* Serial Clock Polarity */
#define SPI_OPMOD_OFFSET 20
#define SPI_OPMOD_MASTER 0
#define SPI_OPMOD_SLAVE 1
#define SPI_TMOD_OFFSET 18 /* Transfer Mode */
//#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET)
#define SPI_TMOD_TR 0x00 /* xmit & recv */
#define SPI_TMOD_TO 0x01 /* xmit only */
#define SPI_TMOD_RO 0x02 /* recv only */
#define SPI_TMOD_RESV 0x03
#define SPI_CFS_OFFSET 2 /* Control Frame Size */
#define SPI_CSM_OFFSET 8 /* Chip Select Mode */
#define SPI_CSM_KEEP 0x00 /* ss_n keep low after every frame data is transferred */
#define SPI_CSM_HALF 0x01 /* ss_n be high for half sclk_out cycles after every frame data is transferred */
#define SPI_CSM_ONE 0x02 /* ss_n be high for one sclk_out cycle after every frame data is transferred */
#define SPI_SSN_DELAY_OFFSET 10
#define SPI_SSN_DELAY_HALF 0x00
#define SPI_SSN_DELAY_ONE 0x01
#define SPI_HALF_WORLD_TX_OFFSET 13
#define SPI_HALF_WORLD_ON 0x00
#define SPI_HALF_WORLD_OFF 0x01
/* --------Bit fields in CTRLR0--------end */
/* Bit fields in SR, 7 bits */
#define SR_MASK 0x7f /* cover 7 bits */
#define SR_BUSY (1 << 0)
#define SR_TF_FULL (1 << 1)
#define SR_TF_EMPT (1 << 2)
#define SR_RF_EMPT (1 << 3)
#define SR_RF_FULL (1 << 4)
/* Bit fields in ISR, IMR, RISR, 7 bits */
#define SPI_INT_TXEI (1 << 0)
#define SPI_INT_TXOI (1 << 1)
#define SPI_INT_RXUI (1 << 2)
#define SPI_INT_RXOI (1 << 3)
#define SPI_INT_RXFI (1 << 4)
/* Bit fields in DMACR */
#define SPI_DMACR_TX_ENABLE (1 << 1)
#define SPI_DMACR_RX_ENABLE (1 << 0)
/* Bit fields in ICR */
#define SPI_CLEAR_INT_ALL (1<< 0)
#define SPI_CLEAR_INT_RXUI (1 << 1)
#define SPI_CLEAR_INT_RXOI (1 << 2)
#define SPI_CLEAR_INT_TXOI (1 << 3)
#if 0
/* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0
#define SPI_FRF_OFFSET 4
#define SPI_FRF_SPI 0x0
#define SPI_FRF_SSP 0x1
#define SPI_FRF_MICROWIRE 0x2
#define SPI_FRF_RESV 0x3
#define SPI_MODE_OFFSET 6
#define SPI_SCPH_OFFSET 6
#define SPI_SCOL_OFFSET 7
#define SPI_TMOD_OFFSET 8
#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET)
#define SPI_TMOD_TR 0x0 /* xmit & recv */
#define SPI_TMOD_TO 0x1 /* xmit only */
#define SPI_TMOD_RO 0x2 /* recv only */
#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */
#define SPI_SLVOE_OFFSET 10
#define SPI_SRL_OFFSET 11
#define SPI_CFS_OFFSET 12
/* Bit fields in SR, 7 bits */
#define SR_MASK 0x7f /* cover 7 bits */
#define SR_BUSY (1 << 0)
#define SR_TF_NOT_FULL (1 << 1)
#define SR_TF_EMPT (1 << 2)
#define SR_RF_NOT_EMPT (1 << 3)
#define SR_RF_FULL (1 << 4)
#define SR_TX_ERR (1 << 5)
#define SR_DCOL (1 << 6)
/* Bit fields in ISR, IMR, RISR, 7 bits */
#define SPI_INT_TXEI (1 << 0)
#define SPI_INT_TXOI (1 << 1)
#define SPI_INT_RXUI (1 << 2)
#define SPI_INT_RXOI (1 << 3)
#define SPI_INT_RXFI (1 << 4)
#define SPI_INT_MSTI (1 << 5)
/* Bit fields in DMACR */
#define SPI_DMACR_TX_ENABLE (1 << 1)
#define SPI_DMACR_RX_ENABLE (1 << 0)
/* Bit fields in ICR */
#define SPI_CLEAR_INT_ALL (1<< 0)
#define SPI_CLEAR_INT_RXUI (1 << 1)
#define SPI_CLEAR_INT_RXOI (1 << 2)
#define SPI_CLEAR_INT_TXOI (1 << 3)
/* TX RX interrupt level threshold, max can be 256 */
#define SPI_INT_THRESHOLD 16
#endif
enum dw_ssi_type {
SSI_MOTO_SPI = 0,
SSI_TI_SSP,
SSI_NS_MICROWIRE,
};
struct dw_spi;
struct dw_spi_dma_ops {
int (*dma_init)(struct dw_spi *dws);
void (*dma_exit)(struct dw_spi *dws);
int (*dma_transfer)(struct dw_spi *dws, int cs_change);
};
struct dw_spi {
struct spi_master *master;
struct spi_device *cur_dev;
struct device *parent_dev;
enum dw_ssi_type type;
char name[16];
struct clk *clk_spi;
struct clk *pclk_spi;
void __iomem *regs;
unsigned long paddr;
u32 iolen;
int irq;
u32 fifo_len; /* depth of the FIFO buffer */
u32 max_freq; /* max bus freq supported */
u16 bus_num;
u16 num_cs; /* supported slave numbers */
/* 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;
struct chip_data *prev_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; /* current is a 1/2 bytes op */
u8 max_bits_per_word; /* maxim is 16b */
u32 dma_width;
int cs_change;
irqreturn_t (*transfer_handler)(struct dw_spi *dws);
void (*cs_control)(struct dw_spi *dws, u32 cs, u8 flag);
/* Dma info */
int dma_inited;
struct dma_chan *txchan;
struct scatterlist tx_sgl;
struct dma_chan *rxchan;
struct scatterlist rx_sgl;
int dma_chan_done;
struct device *dma_dev;
dma_addr_t dma_addr; /* phy address of the Data register */
struct dw_spi_dma_ops *dma_ops;
void *dma_priv; /* platform relate info */
//struct pci_dev *dmac;
atomic_t debug_flag;
/* Bus interface info */
void *priv;
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs;
#endif
};
static inline u32 dw_readl(struct dw_spi *dws, u32 offset)
{
return __raw_readl(dws->regs + offset);
}
static inline void dw_writel(struct dw_spi *dws, u32 offset, u32 val)
{
__raw_writel(val, dws->regs + offset);
}
static inline u16 dw_readw(struct dw_spi *dws, u32 offset)
{
return __raw_readw(dws->regs + offset);
}
static inline void dw_writew(struct dw_spi *dws, u32 offset, u16 val)
{
__raw_writew(val, dws->regs + offset);
}
static inline void spi_enable_chip(struct dw_spi *dws, int enable)
{
dw_writel(dws, SPIM_SSIENR, (enable ? 1 : 0));
}
static inline void spi_set_clk(struct dw_spi *dws, u16 div)
{
dw_writel(dws, SPIM_BAUDR, div);
}
static inline void spi_chip_sel(struct dw_spi *dws, u16 cs)
{
if (cs > dws->num_cs)
return;
if (dws->cs_control)
dws->cs_control(dws, cs, 1);
dw_writel(dws, SPIM_SER, 1 << cs);
DBG_SPI("%s:cs=%d\n",__func__,cs);
}
static inline void spi_cs_control(struct dw_spi *dws, u32 cs, u8 flag)
{
if (flag)
dw_writel(dws, SPIM_SER, 1 << cs);
else
dw_writel(dws, SPIM_SER, 0);
return;
}
/* Disable IRQ bits */
static inline void spi_mask_intr(struct dw_spi *dws, u32 mask)
{
u32 new_mask;
new_mask = dw_readl(dws, SPIM_IMR) & ~mask;
dw_writel(dws, SPIM_IMR, new_mask);
}
/* Enable IRQ bits */
static inline void spi_umask_intr(struct dw_spi *dws, u32 mask)
{
u32 new_mask;
new_mask = dw_readl(dws, SPIM_IMR) | mask;
dw_writel(dws, SPIM_IMR, new_mask);
}
/*
* Each SPI slave device to work with dw_api controller should
* has such a structure claiming its working mode (PIO/DMA etc),
* which can be save in the "controller_data" member of the
* struct spi_device
*/
struct dw_spi_chip {
u8 poll_mode; /* 0 for contoller polling mode */
u8 type; /* SPI/SSP/Micrwire */
u8 enable_dma;
void (*cs_control)(u32 command);
};
extern int dw_spi_add_host(struct dw_spi *dws);
extern void dw_spi_remove_host(struct dw_spi *dws);
extern int dw_spi_suspend_host(struct dw_spi *dws);
extern int dw_spi_resume_host(struct dw_spi *dws);
extern void dw_spi_xfer_done(struct dw_spi *dws);
/* platform related setup */
extern int dw_spi_dma_init(struct dw_spi *dws); /* Intel MID platforms */
#endif /* SPIM_HEADER_H */

223
drivers/spi/spi-rockchip-dma.c Executable file
View File

@@ -0,0 +1,223 @@
/*
* Special handling for DW core on Intel MID platform
*
* Copyright (c) 2009, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_data/spi-rockchip.h>
#include "spi-rockchip-core.h"
#ifdef CONFIG_SPI_ROCKCHIP_DMA
struct spi_dma_slave {
struct dma_chan *ch;
enum dma_transfer_direction direction;
unsigned int dmach;
};
struct spi_dma {
struct spi_dma_slave dmas_tx;
struct spi_dma_slave dmas_rx;
};
static bool mid_spi_dma_chan_filter(struct dma_chan *chan, void *param)
{
struct dw_spi *dws = param;
int ret = 0;
ret = dws->parent_dev && (&dws->parent_dev == chan->device->dev);
printk("%s:ret=%d\n",__func__, ret);
return ret;
}
static int mid_spi_dma_init(struct dw_spi *dws)
{
struct spi_dma *dw_dma = dws->dma_priv;
struct spi_dma_slave *rxs, *txs;
dma_cap_mask_t mask;
/*
* Get pci device for DMA controller, currently it could only
* be the DMA controller of either Moorestown or Medfield
*/
//dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0813, NULL);
//if (!dws->dmac)
// dws->dmac = pci_get_device(PCI_VENDOR_ID_INTEL, 0x0827, NULL);
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
/* 1. Init rx channel */
dws->rxchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
if (!dws->rxchan)
goto err_exit;
rxs = &dw_dma->dmas_rx;
//rxs->hs_mode = LNW_DMA_HW_HS;
//rxs->cfg_mode = LNW_DMA_PER_TO_MEM;
dws->rxchan->private = rxs;
/* 2. Init tx channel */
dws->txchan = dma_request_channel(mask, mid_spi_dma_chan_filter, dws);
if (!dws->txchan)
goto free_rxchan;
txs = &dw_dma->dmas_tx;
//txs->hs_mode = LNW_DMA_HW_HS;
//txs->cfg_mode = LNW_DMA_MEM_TO_PER;
dws->txchan->private = txs;
dws->dma_inited = 1;
return 0;
free_rxchan:
dma_release_channel(dws->rxchan);
err_exit:
return -1;
}
static void mid_spi_dma_exit(struct dw_spi *dws)
{
dma_release_channel(dws->txchan);
dma_release_channel(dws->rxchan);
}
/*
* dws->dma_chan_done is cleared before the dma transfer starts,
* callback for rx/tx channel will each increment it by 1.
* Reaching 2 means the whole spi transaction is done.
*/
static void dw_spi_dma_done(void *arg)
{
struct dw_spi *dws = arg;
if (++dws->dma_chan_done != 2)
return;
dw_spi_xfer_done(dws);
}
static int mid_spi_dma_transfer(struct dw_spi *dws, int cs_change)
{
struct dma_async_tx_descriptor *txdesc = NULL, *rxdesc = NULL;
struct dma_chan *txchan, *rxchan;
struct dma_slave_config txconf, rxconf;
u16 dma_ctrl = 0;
/* 1. setup DMA related registers */
if (cs_change) {
spi_enable_chip(dws, 0);
dw_writew(dws, SPIM_DMARDLR, 0xf);
dw_writew(dws, SPIM_DMATDLR, 0x10);
if (dws->tx_dma)
dma_ctrl |= 0x2;
if (dws->rx_dma)
dma_ctrl |= 0x1;
dw_writew(dws, SPIM_DMACR, dma_ctrl);
spi_enable_chip(dws, 1);
}
dws->dma_chan_done = 0;
txchan = dws->txchan;
rxchan = dws->rxchan;
/* 2. Prepare the TX dma transfer */
txconf.direction = DMA_MEM_TO_DEV;
txconf.dst_addr = dws->dma_addr;
txconf.dst_maxburst = 0x03;//LNW_DMA_MSIZE_16;
txconf.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
txconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
txconf.device_fc = false;
txchan->device->device_control(txchan, DMA_SLAVE_CONFIG,
(unsigned long) &txconf);
memset(&dws->tx_sgl, 0, sizeof(dws->tx_sgl));
dws->tx_sgl.dma_address = dws->tx_dma;
dws->tx_sgl.length = dws->len;
txdesc = dmaengine_prep_slave_sg(txchan,
&dws->tx_sgl,
1,
DMA_MEM_TO_DEV,
DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
txdesc->callback = dw_spi_dma_done;
txdesc->callback_param = dws;
/* 3. Prepare the RX dma transfer */
rxconf.direction = DMA_DEV_TO_MEM;
rxconf.src_addr = dws->dma_addr;
rxconf.src_maxburst = 0x03; //LNW_DMA_MSIZE_16;
rxconf.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
rxconf.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
rxconf.device_fc = false;
rxchan->device->device_control(rxchan, DMA_SLAVE_CONFIG,
(unsigned long) &rxconf);
memset(&dws->rx_sgl, 0, sizeof(dws->rx_sgl));
dws->rx_sgl.dma_address = dws->rx_dma;
dws->rx_sgl.length = dws->len;
rxdesc = dmaengine_prep_slave_sg(rxchan,
&dws->rx_sgl,
1,
DMA_DEV_TO_MEM,
DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_DEST_UNMAP);
rxdesc->callback = dw_spi_dma_done;
rxdesc->callback_param = dws;
/* rx must be started before tx due to spi instinct */
rxdesc->tx_submit(rxdesc);
txdesc->tx_submit(txdesc);
return 0;
}
static struct dw_spi_dma_ops spi_dma_ops = {
.dma_init = mid_spi_dma_init,
.dma_exit = mid_spi_dma_exit,
.dma_transfer = mid_spi_dma_transfer,
};
int dw_spi_dma_init(struct dw_spi *dws)
{
dws->dma_priv = kzalloc(sizeof(struct spi_dma), GFP_KERNEL);
if (!dws->dma_priv)
return -ENOMEM;
dws->dma_ops = &spi_dma_ops;
return 0;
}
#endif

256
drivers/spi/spi-rockchip-test.c Executable file
View File

@@ -0,0 +1,256 @@
/*drivers/serial/spi_test.c -spi test driver
*
*
* 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/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/miscdevice.h>
#include <linux/platform_data/spi-rockchip.h>
#include <asm/uaccess.h>
#include "spi-rockchip-core.h"
#define MAX_SPI_BUS_NUM 2
struct spi_test_data {
struct device *dev;
struct spi_device *spi;
char *rx_buf;
int rx_len;
char *tx_buf;
int tx_len;
};
static struct spi_test_data *g_spi_test_data[MAX_SPI_BUS_NUM];
static struct dw_spi_chip spi_test_chip[] = {
{
//.poll_mode = 1,
//.enable_dma = 1,
},
{
//.poll_mode = 1,
//.enable_dma = 1,
},
};
static struct spi_board_info board_spi_test_devices[] = {
//#if defined(CONFIG_SPIM0_RK29)
{
.modalias = "spi_test_bus0",
.bus_num = 0, //0 or 1
.max_speed_hz = 12*1000*1000,
.chip_select = 0,
.mode = SPI_MODE_0,
.controller_data = &spi_test_chip[0],
},
//#endif
//#if defined(CONFIG_SPIM1_RK29)
{
.modalias = "spi_test_bus1",
.bus_num = 1, //0 or 1
.max_speed_hz = 12*1000*1000,
.chip_select = 0,
.mode = SPI_MODE_0,
.controller_data = &spi_test_chip[1],
}
//#endif
};
static ssize_t spi_test_write(struct file *file,
const char __user *buf, size_t count, loff_t *offset)
{
u8 nr_buf[8];
int nr = 0, ret;
int i = 0;
struct spi_device *spi = NULL;
char txbuf[256],rxbuf[256];
printk("%s:0:bus=0,cs=0; 1:bus=0,cs=1; 2:bus=1,cs=0; 3:bus=1,cs=1\n",__func__);
if(count > 3)
return -EFAULT;
ret = copy_from_user(nr_buf, buf, count);
if(ret < 0)
return -EFAULT;
sscanf(nr_buf, "%d", &nr);
if(nr >= 4 || nr < 0)
{
printk("%s:cmd is error\n",__func__);
return -EFAULT;
}
for(i=0; i<256; i++)
txbuf[i] = i;
/*
if((nr == 0) || (nr == 1))
{
printk("%s:error SPIM0 need selected\n",__func__);
return -EFAULT;
}
if((nr == 2) || (nr == 3))
{
printk("%s:error SPIM1 need selected\n",__func__);
return -EFAULT;
}
*/
switch(nr)
{
case 0:
if(!g_spi_test_data[0]->spi)
return -EFAULT;
spi = g_spi_test_data[0]->spi;
spi->chip_select = 0;
break;
case 1:
if(!g_spi_test_data[0]->spi)
return -EFAULT;
spi = g_spi_test_data[0]->spi;
spi->chip_select = 1;
break;
case 2:
if(!g_spi_test_data[1]->spi)
return -EFAULT;
spi = g_spi_test_data[1]->spi;
spi->chip_select = 0;
break;
case 3:
if(!g_spi_test_data[1]->spi)
return -EFAULT;
spi = g_spi_test_data[1]->spi;
spi->chip_select = 1;
break;
default:
break;
}
for(i=0; i<1; i++)
{
ret = spi_write(spi, txbuf, 256);
ret = spi_read(spi, rxbuf, 256);
ret = spi_write_then_read(spi,txbuf,256,rxbuf,256);
ret = spi_write_and_read(spi,txbuf,rxbuf,256);
printk("%s:test %d times\n\n",__func__,i+1);
}
if(!ret)
printk("%s:bus_num=%d,chip_select=%d,ok\n",__func__,spi->master->bus_num, spi->chip_select);
else
printk("%s:bus_num=%d,chip_select=%d,error\n",__func__,spi->master->bus_num, spi->chip_select);
return count;
}
static const struct file_operations spi_test_fops = {
.write = spi_test_write,
};
static struct miscdevice spi_test_misc = {
.minor = MISC_DYNAMIC_MINOR,
.name = "spi_misc_test",
.fops = &spi_test_fops,
};
static int spi_test_probe(struct spi_device *spi)
{
struct spi_test_data *spi_test_data;
int ret;
int i =0;
char txbuf[256],rxbuf[256];
if(!spi)
return -ENOMEM;
if((spi->master->bus_num >= MAX_SPI_BUS_NUM) || (spi->master->bus_num < 0))
{
printk("%s:error:bus_num=%d\n",__func__, spi->master->bus_num);
return -ENOMEM;
}
spi_test_data = (struct spi_test_data *)kzalloc(sizeof(struct spi_test_data), GFP_KERNEL);
if(!spi_test_data){
dev_err(&spi->dev, "ERR: no memory for spi_test_data\n");
return -ENOMEM;
}
spi->bits_per_word = 8;
spi_test_data->spi = spi;
spi_test_data->dev = &spi->dev;
ret = spi_setup(spi);
if (ret < 0){
dev_err(spi_test_data->dev, "ERR: fail to setup spi\n");
return -1;
}
g_spi_test_data[spi->master->bus_num] = spi_test_data;
printk("%s:bus_num=%d,ok\n",__func__,spi->master->bus_num);
return ret;
}
static const struct spi_device_id spi_test_id[] = {
{"spi_test_bus0", 0},
{"spi_test_bus1", 1},
{},
};
static struct spi_driver spi_test_driver = {
.driver = {
.name = "spi_test",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.id_table = spi_test_id,
.probe = spi_test_probe,
};
static int __init spi_test_init(void)
{
printk("%s\n",__func__);
spi_register_board_info(board_spi_test_devices, ARRAY_SIZE(board_spi_test_devices));
misc_register(&spi_test_misc);
return spi_register_driver(&spi_test_driver);
}
static void __exit spi_test_exit(void)
{
misc_deregister(&spi_test_misc);
return spi_unregister_driver(&spi_test_driver);
}
module_init(spi_test_init);
module_exit(spi_test_exit);

349
drivers/spi/spi-rockchip.c Executable file
View File

@@ -0,0 +1,349 @@
/*
* rockchip spi interface driver for DW SPI Core
*
* Copyright (c) 2014, ROCKCHIP Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License,
* version 2, as published by the Free Software Foundation.
*
* This program is distributed in the hope 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., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/clk.h>
#include <linux/dma-mapping.h>
#include <linux/dmaengine.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/platform_data/spi-rockchip.h>
#include "spi-rockchip-core.h"
#define DRIVER_NAME "rockchip_spi_driver_data"
#define SPI_MAX_FREQUENCY 24000000
struct rockchip_spi_driver_data {
struct platform_device *pdev;
struct dw_spi dws;
struct rockchip_spi_info *info;
struct clk *clk_spi;
struct clk *pclk_spi;
};
#ifdef CONFIG_OF
static struct rockchip_spi_info *rockchip_spi_parse_dt(struct device *dev)
{
struct rockchip_spi_info *info;
u32 temp;
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(dev, "memory allocation for spi_info failed\n");
return ERR_PTR(-ENOMEM);
}
if (of_property_read_u32(dev->of_node, "rockchip,spi-src-clk", &temp)) {
dev_warn(dev, "spi bus clock parent not specified, using clock at index 0 as parent\n");
info->src_clk_nr = 0;
} else {
info->src_clk_nr = temp;
}
#if 0
if (of_property_read_u32(dev->of_node, "bus-num", &temp)) {
dev_warn(dev, "number of bus not specified, assuming bus 0\n");
info->bus_num= 0;
} else {
info->bus_num = temp;
}
#endif
if (of_property_read_u32(dev->of_node, "num-cs", &temp)) {
dev_warn(dev, "number of chip select lines not specified, assuming 1 chip select line\n");
info->num_cs = 1;
} else {
info->num_cs = temp;
}
if (of_property_read_u32(dev->of_node, "max-freq", &temp)) {
dev_warn(dev, "fail to get max-freq\n");
info->spi_freq = SPI_MAX_FREQUENCY;
} else {
info->spi_freq = temp;
}
//printk("%s:line=%d,src_clk_nr=%d,bus_num=%d,num_cs=%d\n",__func__, __LINE__,info->src_clk_nr,info->bus_num,info->num_cs);
return info;
}
#else
static struct rockchip_spi_info *rockchip_spi_parse_dt(struct device *dev)
{
return dev->platform_data;
}
#endif
static int rockchip_spi_probe(struct platform_device *pdev)
{
struct resource *mem_res;
struct resource *res;
struct rockchip_spi_driver_data *sdd;
struct rockchip_spi_info *info = pdev->dev.platform_data;
struct dw_spi *dws;
struct spi_master *master;
int ret, irq;
char clk_name[16];
if (!info && pdev->dev.of_node) {
info = rockchip_spi_parse_dt(&pdev->dev);
if (IS_ERR(info))
return PTR_ERR(info);
}
if (!info) {
dev_err(&pdev->dev, "platform_data missing!\n");
return -ENODEV;
}
sdd = kzalloc(sizeof(struct rockchip_spi_driver_data), GFP_KERNEL);
if (!sdd) {
ret = -ENOMEM;
goto err_kfree;
}
sdd->pdev = pdev;
sdd->info = info;
dws = &sdd->dws;
atomic_set(&dws->debug_flag, 0);//debug flag
/* Get basic io resource and map it */
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq);
return irq;
}
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) {
dev_err(&pdev->dev, "Unable to get SPI MEM resource\n");
ret = -ENXIO;
goto err_unmap;
}
dws->regs = ioremap(mem_res->start, (mem_res->end - mem_res->start) + 1);
if (!dws->regs){
ret = -EBUSY;
goto err_unmap;
}
dws->paddr = mem_res->start;
dws->iolen = (mem_res->end - mem_res->start) + 1;
printk(KERN_INFO "dws->regs: %p\n", dws->regs);
//get bus num
if (pdev->dev.of_node) {
ret = of_alias_get_id(pdev->dev.of_node, "spi");
if (ret < 0) {
dev_err(&pdev->dev, "failed to get alias id, errno %d\n",
ret);
goto err_release_mem;
}
info->bus_num = ret;
} else {
info->bus_num = pdev->id;
}
/* Setup clocks */
sdd->clk_spi = devm_clk_get(&pdev->dev, "spi");
if (IS_ERR(sdd->clk_spi)) {
dev_err(&pdev->dev, "Unable to acquire clock 'spi'\n");
ret = PTR_ERR(sdd->clk_spi);
goto err_clk;
}
if (clk_prepare_enable(sdd->clk_spi)) {
dev_err(&pdev->dev, "Couldn't enable clock 'spi'\n");
ret = -EBUSY;
goto err_clk;
}
sprintf(clk_name, "pclk_spi%d", info->src_clk_nr);
sdd->pclk_spi = devm_clk_get(&pdev->dev, clk_name);
if (IS_ERR(sdd->pclk_spi)) {
dev_err(&pdev->dev,
"Unable to acquire clock '%s'\n", clk_name);
ret = PTR_ERR(sdd->pclk_spi);
goto err_pclk;
}
if (clk_prepare_enable(sdd->pclk_spi)) {
dev_err(&pdev->dev, "Couldn't enable clock '%s'\n", clk_name);
ret = -EBUSY;
goto err_pclk;
}
clk_set_rate(sdd->clk_spi, info->spi_freq);
dws->max_freq = clk_get_rate(sdd->clk_spi);
dws->parent_dev = &pdev->dev;
dws->bus_num = info->bus_num;
dws->num_cs = info->num_cs;
dws->irq = irq;
dws->clk_spi = sdd->clk_spi;
dws->pclk_spi = sdd->pclk_spi;
/*
* handling for rockchip paltforms, like dma setup,
* clock rate, FIFO depth.
*/
#ifdef CONFIG_SPI_ROCKCHIP_DMA
ret = dw_spi_dma_init(dws);
if (ret)
goto err_release_mem;
#endif
ret = dw_spi_add_host(dws);
if (ret)
goto err_release_mem;
platform_set_drvdata(pdev, sdd);
printk("%s:num_cs=%d,irq=%d,freq=%d ok\n",__func__, info->num_cs, irq, dws->max_freq);
return 0;
err_release_mem:
release_mem_region(mem_res->start, (mem_res->end - mem_res->start) + 1);
err_pclk:
clk_disable_unprepare(sdd->pclk_spi);
err_clk:
clk_disable_unprepare(sdd->clk_spi);
err_unmap:
iounmap(dws->regs);
err_kfree:
kfree(sdd);
return ret;
}
static int rockchip_spi_remove(struct platform_device *pdev)
{
struct rockchip_spi_driver_data *sdd = platform_get_drvdata(pdev);
platform_set_drvdata(pdev, NULL);
dw_spi_remove_host(&sdd->dws);
iounmap(sdd->dws.regs);
kfree(sdd);
}
#ifdef CONFIG_PM_SLEEP
static int rockchip_spi_suspend(struct device *dev)
{
struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
int ret = 0;
ret = dw_spi_suspend_host(&sdd->dws);
return ret;
}
static int rockchip_spi_resume(struct device *dev)
{
struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
int ret = 0;
ret = dw_spi_resume_host(&sdd->dws);
return ret;
}
#endif /* CONFIG_PM_SLEEP */
#ifdef CONFIG_PM_RUNTIME
static int rockchip_spi_runtime_suspend(struct device *dev)
{
struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
struct dw_spi *dws = &sdd->dws;
clk_disable_unprepare(sdd->clk_spi);
clk_disable_unprepare(sdd->pclk_spi);
DBG_SPI("%s\n",__func__);
return 0;
}
static int rockchip_spi_runtime_resume(struct device *dev)
{
struct rockchip_spi_driver_data *sdd = dev_get_drvdata(dev);
struct dw_spi *dws = &sdd->dws;
clk_prepare_enable(sdd->pclk_spi);
clk_prepare_enable(sdd->clk_spi);
DBG_SPI("%s\n",__func__);
return 0;
}
#endif /* CONFIG_PM_RUNTIME */
static const struct dev_pm_ops rockchip_spi_pm = {
SET_SYSTEM_SLEEP_PM_OPS(rockchip_spi_suspend, rockchip_spi_resume)
SET_RUNTIME_PM_OPS(rockchip_spi_runtime_suspend,
rockchip_spi_runtime_resume, NULL)
};
#ifdef CONFIG_OF
static const struct of_device_id rockchip_spi_dt_match[] = {
{ .compatible = "rockchip,rockchip-spi",
},
{ },
};
MODULE_DEVICE_TABLE(of, rockchip_spi_dt_match);
#endif /* CONFIG_OF */
static struct platform_driver rockchip_spi_driver = {
.driver = {
.name = "rockchip-spi",
.owner = THIS_MODULE,
.pm = &rockchip_spi_pm,
.of_match_table = of_match_ptr(rockchip_spi_dt_match),
},
.remove = rockchip_spi_remove,
};
MODULE_ALIAS("platform:rockchip-spi");
static int __init rockchip_spi_init(void)
{
return platform_driver_probe(&rockchip_spi_driver, rockchip_spi_probe);
}
subsys_initcall(rockchip_spi_init);
static void __exit rockchip_spi_exit(void)
{
platform_driver_unregister(&rockchip_spi_driver);
}
module_exit(rockchip_spi_exit);
MODULE_AUTHOR("Luo Wei <lw@rock-chips.com>");
MODULE_DESCRIPTION("ROCKCHIP SPI Controller Driver");
MODULE_LICENSE("GPL");

View File

@@ -0,0 +1,79 @@
/* include/linux/platform_data/spi-rockchip.h
*
* Copyright (C) 2014 Rockchip Electronics Ltd.
* luowei <lw@rock-chips.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __ROCKCHIP_PLAT_SPI_H
#define __ROCKCHIP_PLAT_SPI_H
#include <linux/dmaengine.h>
struct platform_device;
/**
* struct rockchip_spi_csinfo - ChipSelect description
* @fb_delay: Slave specific feedback delay.
* Refer to FB_CLK_SEL register definition in SPI chapter.
* @line: Custom 'identity' of the CS line.
*
* This is per SPI-Slave Chipselect information.
* Allocate and initialize one in machine init code and make the
* spi_board_info.controller_data point to it.
*/
struct rockchip_spi_csinfo {
u8 fb_delay;
unsigned line;
};
/**
* struct rockchip_spi_info - SPI Controller defining structure
* @src_clk_nr: Clock source index for the CLK_CFG[SPI_CLKSEL] field.
* @num_cs: Number of CS this controller emulates.
* @cfg_gpio: Configure pins for this SPI controller.
*/
struct rockchip_spi_info {
int src_clk_nr;
int spi_freq;
int num_cs;
int bus_num;
int (*cfg_gpio)(void);
dma_filter_fn filter;
u8 transfer_mode;/*full or half duplex*/
u8 poll_mode; /* 0 for contoller polling mode */
u8 type; /* SPI/SSP/Micrwire */
u8 enable_dma;
u8 slave_enable;
};
/**
* rockchip_spi_set_platdata - SPI Controller configure callback by the board
* initialization code.
* @cfg_gpio: Pointer to gpio setup function.
* @src_clk_nr: Clock the SPI controller is to use to generate SPI clocks.
* @num_cs: Number of elements in the 'cs' array.
*
* Call this from machine init code for each SPI Controller that
* has some chips attached to it.
*/
extern void rockchip_spi0_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
int num_cs);
extern void rockchip_spi1_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
int num_cs);
extern void rockchip_spi2_set_platdata(int (*cfg_gpio)(void), int src_clk_nr,
int num_cs);
/* defined by architecture to configure gpio */
extern int rockchip_spi0_cfg_gpio(void);
extern int rockchip_spi1_cfg_gpio(void);
extern int rockchip_spi2_cfg_gpio(void);
extern struct rockchip_spi_info rockchip_spi0_pdata;
extern struct rockchip_spi_info rockchip_spi1_pdata;
extern struct rockchip_spi_info rockchip_spi2_pdata;
#endif /* __ROCKCHIP_PLAT_SPI_H */