mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-10 21:07:02 +09:00
add spi driver support
This commit is contained in:
17
drivers/spi/Kconfig
Normal file → Executable file
17
drivers/spi/Kconfig
Normal file → Executable 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
4
drivers/spi/Makefile
Normal file → Executable 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
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
355
drivers/spi/spi-rockchip-core.h
Executable 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
223
drivers/spi/spi-rockchip-dma.c
Executable 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
256
drivers/spi/spi-rockchip-test.c
Executable 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
349
drivers/spi/spi-rockchip.c
Executable 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");
|
||||
79
include/linux/platform_data/spi-rockchip.h
Executable file
79
include/linux/platform_data/spi-rockchip.h
Executable 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 */
|
||||
Reference in New Issue
Block a user