SmartAudio/lichee/linux-4.9/drivers/spi/spi-sunxi.c

2049 lines
56 KiB
C
Executable File

/*
* drivers/spi/spi-sunxi.c
*
* Copyright (C) 2012 - 2016 Reuuimlla Limited
* Pan Nan <pannan@reuuimllatech.com>
*
* SUNXI SPI Controller Driver
*
* 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.
*
* 2013.5.7 Mintow <duanmintao@allwinnertech.com>
* Adapt to support sun8i/sun9i of Allwinner.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/pinctrl/consumer.h>
#include <linux/spi/spi.h>
#include <linux/gpio.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spi_bitbang.h>
#include <asm/cacheflush.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#ifdef CONFIG_DMA_ENGINE
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dma/sunxi-dma.h>
#endif
#include <linux/clk/sunxi.h>
#include "spi-sunxi.h"
/* For debug */
#define SPI_EXIT() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Exit")
#define SPI_ENTER() pr_debug("%s()%d - %s\n", __func__, __LINE__, "Enter ...")
#define SPI_DBG(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SPI_INF(fmt, arg...) pr_debug("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SPI_ERR(fmt, arg...) pr_warn("%s()%d - "fmt, __func__, __LINE__, ##arg)
#define SUNXI_SPI_OK 0
#define SUNXI_SPI_FAIL -1
enum spi_mode_type {
SINGLE_HALF_DUPLEX_RX, /* single mode, half duplex read */
SINGLE_HALF_DUPLEX_TX, /* single mode, half duplex write */
SINGLE_FULL_DUPLEX_RX_TX, /* single mode, full duplex read and write */
DUAL_HALF_DUPLEX_RX, /* dual mode, half duplex read */
DUAL_HALF_DUPLEX_TX, /* dual mode, half duplex write */
QUAD_HALF_DUPLEX_RX, /* quad mode, half duplex read */
QUAD_HALF_DUPLEX_TX, /* quad mode, half duplex write */
MODE_TYPE_NULL,
};
#define SPI_DUAL_MODE 1
#define SPI_QUAD_MODE 2
#define SPI_SINGLE_MODE 4
#define SPINOR_OP_READ_1_1_2 0x3b /* Read data bytes (Dual SPI) */
#define SPINOR_OP_READ_1_1_4 0x6b /* Read data bytes (Quad SPI) */
#define SPINOR_OP_READ4_1_1_2 0x3c /* Read data bytes (Dual SPI) */
#define SPINOR_OP_READ4_1_1_4 0x6c /* Read data bytes (Quad SPI) */
#ifdef CONFIG_DMA_ENGINE
#define SPI_MAX_PAGES 32
enum spi_dma_dir {
SPI_DMA_RWNULL,
SPI_DMA_WDEV = DMA_TO_DEVICE,
SPI_DMA_RDEV = DMA_FROM_DEVICE,
};
typedef struct {
enum spi_dma_dir dir;
struct dma_chan *chan;
int nents;
struct scatterlist sg[SPI_MAX_PAGES];
struct page *pages[SPI_MAX_PAGES];
} spi_dma_info_t;
u64 sunxi_spi_dma_mask = DMA_BIT_MASK(32);
#endif
struct sunxi_spi {
struct platform_device *pdev;
struct spi_master *master;/* kzalloc */
void __iomem *base_addr; /* register */
u32 base_addr_phy;
struct clk *pclk; /* PLL clock */
struct clk *mclk; /* spi module clock */
#ifdef CONFIG_DMA_ENGINE
spi_dma_info_t dma_rx;
spi_dma_info_t dma_tx;
#endif
enum spi_mode_type mode_type;
unsigned int irq; /* irq NO. */
char dev_name[48];
int busy;
#define SPI_FREE (1<<0)
#define SPI_SUSPND (1<<1)
#define SPI_BUSY (1<<2)
int result; /* 0: succeed -1:fail */
struct workqueue_struct *workqueue;
struct work_struct work;
struct list_head queue; /* spi messages */
spinlock_t lock;
struct completion done; /* wakup another spi transfer */
/* keep select during one message */
void (*cs_control)(struct spi_device *spi, bool on);
/*
* (1) enable cs1, cs_bitmap = SPI_CHIP_SELECT_CS1;
* (2) enable cs0&cs1,cs_bitmap = SPI_CHIP_SELECT_CS0|SPI_CHIP_SELECT_CS1;
*
*/
#define SPI_CHIP_SELECT_CS0 (0x01)
#define SPI_CHIP_SELECT_CS1 (0x02)
#define SPI_CHIP_SELECT_CS2 (0x04)
#define SPI_CHIP_SELECT_CS3 (0x08)
int cs_bitmap;/* cs0- 0x1; cs1-0x2, cs0&cs1-0x3. */
u32 cdr;
struct pinctrl *pctrl;
};
static int spi_used_mask;
/* config chip select */
static s32 spi_set_cs(u32 chipselect, void __iomem *base_addr)
{
int ret;
u32 reg_val = readl(base_addr + SPI_TC_REG);
if (chipselect < 4) {
reg_val &= ~SPI_TC_SS_MASK;/* SS-chip select, clear two bits */
reg_val |= chipselect << SPI_TC_SS_BIT_POS;/* set chip select */
writel(reg_val, base_addr + SPI_TC_REG);
ret = SUNXI_SPI_OK;
} else {
SPI_ERR("Chip Select set fail! cs = %d\n", chipselect);
ret = SUNXI_SPI_FAIL;
}
return ret;
}
/* config spi */
static void spi_config_tc(u32 master, u32 config, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
/*1. POL */
if (config & SPI_POL_ACTIVE_)
reg_val |= SPI_TC_POL;/*default POL = 1 */
else
reg_val &= ~SPI_TC_POL;
/*2. PHA */
if (config & SPI_PHA_ACTIVE_)
reg_val |= SPI_TC_PHA;/*default PHA = 1 */
else
reg_val &= ~SPI_TC_PHA;
/*3. SSPOL,chip select signal polarity */
if (config & SPI_CS_HIGH_ACTIVE_)
reg_val &= ~SPI_TC_SPOL;
else
reg_val |= SPI_TC_SPOL; /*default SSPOL = 1,Low level effect */
/*4. LMTF--LSB/MSB transfer first select */
if (config & SPI_LSB_FIRST_ACTIVE_)
reg_val |= SPI_TC_FBS;
else
reg_val &= ~SPI_TC_FBS;/*default LMTF =0, MSB first */
/*master mode: set DDB,DHB,SMC,SSCTL*/
if (master == 1) {
/*5. dummy burst type */
if (config & SPI_DUMMY_ONE_ACTIVE_)
reg_val |= SPI_TC_DDB;
else
reg_val &= ~SPI_TC_DDB;/*default DDB =0, ZERO */
/*6.discard hash burst-DHB */
if (config & SPI_RECEIVE_ALL_ACTIVE_)
reg_val &= ~SPI_TC_DHB;
else
reg_val |= SPI_TC_DHB;/*default DHB =1, discard unused burst */
/*7. set SMC = 1 , SSCTL = 0 ,TPE = 1 */
reg_val &= ~SPI_TC_SSCTL;
} else {
/* tips for slave mode config */
SPI_INF("slave mode configurate control register.\n");
}
writel(reg_val, base_addr + SPI_TC_REG);
}
/* set spi clock */
static void spi_set_clk(u32 spi_clk, u32 ahb_clk, void __iomem *base_addr, u32 cdr)
{
u32 reg_val = 0;
u32 div_clk = 0;
SPI_DBG("set spi clock %d, mclk %d\n", spi_clk, ahb_clk);
reg_val = readl(base_addr + SPI_CLK_CTL_REG);
/* CDR2 */
if (cdr) {
div_clk = ahb_clk / (spi_clk * 2) - 1;
reg_val &= ~SPI_CLK_CTL_CDR2;
reg_val |= (div_clk | SPI_CLK_CTL_DRS);
SPI_DBG("CDR2 - n = %d\n", div_clk);
} else { /* CDR1 */
while (ahb_clk > spi_clk) {
div_clk++;
ahb_clk >>= 1;
}
reg_val &= ~(SPI_CLK_CTL_CDR1 | SPI_CLK_CTL_DRS);
reg_val |= (div_clk << 8);
SPI_DBG("CDR1 - n = %d\n", div_clk);
}
writel(reg_val, base_addr + SPI_CLK_CTL_REG);
}
/* start spi transfer */
static void spi_start_xfer(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
reg_val |= SPI_TC_XCH;
writel(reg_val, base_addr + SPI_TC_REG);
}
/* enable spi bus */
static void spi_enable_bus(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_EN;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* disbale spi bus */
static void spi_disable_bus(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val &= ~SPI_GC_EN;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* set master mode */
static void spi_set_master(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_MODE;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* enable transmit pause */
static void spi_enable_tp(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_TP_EN;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* soft reset spi controller */
static void spi_soft_reset(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_GC_REG);
reg_val |= SPI_GC_SRST;
writel(reg_val, base_addr + SPI_GC_REG);
}
/* enable irq type */
static void spi_enable_irq(u32 bitmap, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_INT_CTL_REG);
bitmap &= SPI_INTEN_MASK;
reg_val |= bitmap;
writel(reg_val, base_addr + SPI_INT_CTL_REG);
}
/* disable irq type */
static void spi_disable_irq(u32 bitmap, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_INT_CTL_REG);
bitmap &= SPI_INTEN_MASK;
reg_val &= ~bitmap;
writel(reg_val, base_addr + SPI_INT_CTL_REG);
}
#ifdef CONFIG_DMA_ENGINE
/* enable dma irq */
static void spi_enable_dma_irq(u32 bitmap, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_FIFO_CTL_REG);
bitmap &= SPI_FIFO_CTL_DRQEN_MASK;
reg_val |= bitmap;
writel(reg_val, base_addr + SPI_FIFO_CTL_REG);
spi_set_dma_mode(base_addr);
}
#endif
/* disable dma irq */
static void spi_disable_dma_irq(u32 bitmap, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_FIFO_CTL_REG);
bitmap &= SPI_FIFO_CTL_DRQEN_MASK;
reg_val &= ~bitmap;
writel(reg_val, base_addr + SPI_FIFO_CTL_REG);
}
/* query irq pending */
static u32 spi_qry_irq_pending(void __iomem *base_addr)
{
return (SPI_INT_STA_MASK & readl(base_addr + SPI_INT_STA_REG));
}
/* clear irq pending */
static void spi_clr_irq_pending(u32 pending_bit, void __iomem *base_addr)
{
pending_bit &= SPI_INT_STA_MASK;
writel(pending_bit, base_addr + SPI_INT_STA_REG);
}
/* query txfifo bytes */
static u32 spi_query_txfifo(void __iomem *base_addr)
{
u32 reg_val = (SPI_FIFO_STA_TX_CNT & readl(base_addr + SPI_FIFO_STA_REG));
reg_val >>= SPI_TXCNT_BIT_POS;
return reg_val;
}
/* query rxfifo bytes */
static u32 spi_query_rxfifo(void __iomem *base_addr)
{
u32 reg_val = (SPI_FIFO_STA_RX_CNT & readl(base_addr + SPI_FIFO_STA_REG));
reg_val >>= SPI_RXCNT_BIT_POS;
return reg_val;
}
/* reset fifo */
static void spi_reset_fifo(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_FIFO_CTL_REG);
reg_val |= (SPI_FIFO_CTL_RX_RST|SPI_FIFO_CTL_TX_RST);
/* Set the trigger level of RxFIFO/TxFIFO. */
reg_val &= ~(SPI_FIFO_CTL_RX_LEVEL|SPI_FIFO_CTL_TX_LEVEL);
reg_val |= (0x20<<16) | 0x20;
writel(reg_val, base_addr + SPI_FIFO_CTL_REG);
}
/* set transfer total length BC, transfer length TC and single transmit length STC */
static void spi_set_bc_tc_stc(u32 tx_len, u32 rx_len, u32 stc_len, u32 dummy_cnt, void __iomem *base_addr)
{
u32 reg_val = readl(base_addr + SPI_BURST_CNT_REG);
reg_val &= ~SPI_BC_CNT_MASK;
reg_val |= (SPI_BC_CNT_MASK & (tx_len + rx_len + dummy_cnt));
writel(reg_val, base_addr + SPI_BURST_CNT_REG);
reg_val = readl(base_addr + SPI_TRANSMIT_CNT_REG);
reg_val &= ~SPI_TC_CNT_MASK;
reg_val |= (SPI_TC_CNT_MASK & tx_len);
writel(reg_val, base_addr + SPI_TRANSMIT_CNT_REG);
reg_val = readl(base_addr + SPI_BCC_REG);
reg_val &= ~SPI_BCC_STC_MASK;
reg_val |= (SPI_BCC_STC_MASK & stc_len);
reg_val &= ~(0xf << 24);
reg_val |= (dummy_cnt << 24);
writel(reg_val, base_addr + SPI_BCC_REG);
}
/* set ss control */
static void spi_ss_ctrl(void __iomem *base_addr, u32 on_off)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
on_off &= 0x1;
if (on_off)
reg_val |= SPI_TC_SS_OWNER;
else
reg_val &= ~SPI_TC_SS_OWNER;
writel(reg_val, base_addr + SPI_TC_REG);
}
/* set ss level */
static void spi_ss_level(void __iomem *base_addr, u32 hi_lo)
{
u32 reg_val = readl(base_addr + SPI_TC_REG);
hi_lo &= 0x1;
if (hi_lo)
reg_val |= SPI_TC_SS_LEVEL;
else
reg_val &= ~SPI_TC_SS_LEVEL;
writel(reg_val, base_addr + SPI_TC_REG);
}
/* set dhb, 1: discard unused spi burst; 0: receiving all spi burst */
static void spi_set_all_burst_received(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_TC_REG);
reg_val &= ~SPI_TC_DHB;
writel(reg_val, base_addr + SPI_TC_REG);
}
static void spi_disable_dual(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val &= ~SPI_BCC_DUAL_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static void spi_enable_dual(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val &= ~SPI_BCC_QUAD_MODE;
reg_val |= SPI_BCC_DUAL_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static void spi_disable_quad(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val &= ~SPI_BCC_QUAD_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static void spi_enable_quad(void __iomem *base_addr)
{
u32 reg_val = readl(base_addr+SPI_BCC_REG);
reg_val |= SPI_BCC_QUAD_MODE;
writel(reg_val, base_addr + SPI_BCC_REG);
}
static int spi_regulator_request(struct sunxi_spi_platform_data *pdata)
{
struct regulator *regu = NULL;
if (pdata->regulator != NULL)
return 0;
/* Consider "n*" as nocare. Support "none", "nocare", "null", "" etc. */
if ((pdata->regulator_id[0] == 'n') || (pdata->regulator_id[0] == 0))
return 0;
regu = regulator_get(NULL, pdata->regulator_id);
if (IS_ERR(regu)) {
SPI_ERR("get regulator %s failed!\n", pdata->regulator_id);
return -1;
}
pdata->regulator = regu;
return 0;
}
static void spi_regulator_release(struct sunxi_spi_platform_data *pdata)
{
if (pdata->regulator == NULL)
return;
regulator_put(pdata->regulator);
pdata->regulator = NULL;
}
static int spi_regulator_enable(struct sunxi_spi_platform_data *pdata)
{
if (pdata->regulator == NULL)
return 0;
if (regulator_enable(pdata->regulator) != 0) {
SPI_ERR("enable regulator %s failed!\n", pdata->regulator_id);
return -1;
}
return 0;
}
static int spi_regulator_disable(struct sunxi_spi_platform_data *pdata)
{
if (pdata->regulator == NULL)
return 0;
if (regulator_disable(pdata->regulator) != 0) {
SPI_ERR("enable regulator %s failed!\n", pdata->regulator_id);
return -1;
}
return 0;
}
#ifdef CONFIG_DMA_ENGINE
/* ------------------------------- dma operation start----------------------- */
/* dma full done callback for spi rx */
static void sunxi_spi_dma_cb_rx(void *data)
{
struct sunxi_spi *sspi = (struct sunxi_spi *)data;
unsigned long flags = 0;
void __iomem *base_addr = sspi->base_addr;
spin_lock_irqsave(&sspi->lock, flags);
spi_disable_dma_irq(SPI_FIFO_CTL_RX_DRQEN, sspi->base_addr);
SPI_DBG("[spi-%d]: dma -read data end!\n", sspi->master->bus_num);
if (spi_query_rxfifo(base_addr) > 0) {
SPI_ERR("[spi-%d]: DMA end, but RxFIFO isn't empty! FSR: %#x\n",
sspi->master->bus_num, spi_query_rxfifo(base_addr));
sspi->result = -1; /* failed */
} else {
sspi->result = 0;
}
complete(&sspi->done);
spin_unlock_irqrestore(&sspi->lock, flags);
}
/* dma full done callback for spi tx */
static void sunxi_spi_dma_cb_tx(void *data)
{
struct sunxi_spi *sspi = (struct sunxi_spi *)data;
unsigned long flags = 0;
spin_lock_irqsave(&sspi->lock, flags);
spi_disable_dma_irq(SPI_FIFO_CTL_TX_DRQEN, sspi->base_addr);
SPI_DBG("[spi-%d]: dma -write data end!\n", sspi->master->bus_num);
spin_unlock_irqrestore(&sspi->lock, flags);
}
static int sunxi_spi_dmg_sg_cnt(void *addr, int len)
{
int npages = 0;
char *bufp = (char *)addr;
int mapbytes = 0;
int bytesleft = len;
while (bytesleft > 0) {
if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
mapbytes = bytesleft;
else
mapbytes = PAGE_SIZE - offset_in_page(bufp);
npages++;
bufp += mapbytes;
bytesleft -= mapbytes;
}
return npages;
}
static int sunxi_spi_dma_init_sg(spi_dma_info_t *info, void *addr,
int len, int write)
{
int i;
int npages = 0;
void *bufp = addr;
int mapbytes = 0;
int bytesleft = len;
if (!access_ok(write ? VERIFY_READ : VERIFY_WRITE, (void __user *)addr, len))
return -EFAULT;
npages = sunxi_spi_dmg_sg_cnt(addr, len);
WARN_ON(npages == 0);
SPI_DBG("npages = %d, len = %d\n", npages, len);
if (npages > SPI_MAX_PAGES)
npages = SPI_MAX_PAGES;
sg_init_table(info->sg, npages);
for (i = 0; i < npages; i++) {
/* If there are less bytes left than what fits
* in the current page (plus page alignment offset)
* we just feed in this, else we stuff in as much
* as we can.
*/
if (bytesleft < (PAGE_SIZE - offset_in_page(bufp)))
mapbytes = bytesleft;
else
mapbytes = PAGE_SIZE - offset_in_page(bufp);
SPI_DBG("%d: len %d, offset %ld, addr %p(%d)\n", i, mapbytes,
offset_in_page(bufp), bufp, virt_addr_valid(bufp));
if (virt_addr_valid(bufp))
sg_set_page(&info->sg[i], virt_to_page(bufp),
mapbytes, offset_in_page(bufp));
else
sg_set_page(&info->sg[i], vmalloc_to_page(bufp),
mapbytes, offset_in_page(bufp));
bufp += mapbytes;
bytesleft -= mapbytes;
}
WARN_ON(bytesleft);
info->nents = npages;
return 0;
}
/* request dma channel and set callback function */
static int sunxi_spi_prepare_dma(spi_dma_info_t *_info, enum spi_dma_dir _dir)
{
dma_cap_mask_t mask;
SPI_DBG("Init DMA, dir %d\n", _dir);
/* Try to acquire a generic DMA engine slave channel */
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
if (_info->chan == NULL) {
_info->chan = dma_request_channel(mask, NULL, NULL);
if (_info->chan == NULL) {
SPI_ERR("Request DMA(dir %d) failed!\n", _dir);
return -EINVAL;
}
}
_info->dir = _dir;
return 0;
}
static int sunxi_spi_config_dma_rx(struct sunxi_spi *sspi, struct spi_transfer *t)
{
int ret = 0;
int nents = 0;
struct dma_slave_config dma_conf = {0};
struct dma_async_tx_descriptor *dma_desc = NULL;
SPI_DBG("t->rx_buf = %p, t->len = %d\n", t->rx_buf, t->len);
ret = sunxi_spi_dma_init_sg(&sspi->dma_rx, t->rx_buf, t->len, VERIFY_WRITE);
if (ret != 0)
return ret;
dma_conf.direction = DMA_DEV_TO_MEM;
dma_conf.src_addr = sspi->base_addr_phy + SPI_RXDATA_REG;
dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_conf.src_maxburst = 1;
dma_conf.dst_maxburst = 1;
dma_conf.slave_id = sunxi_slave_id(DRQDST_SDRAM, SUNXI_SPI_DRQ_RX(sspi->master->bus_num));
dmaengine_slave_config(sspi->dma_rx.chan, &dma_conf);
nents = dma_map_sg(&sspi->pdev->dev, sspi->dma_rx.sg,
sspi->dma_rx.nents, DMA_FROM_DEVICE);
if (!nents) {
SPI_ERR("dma_map_sg(%d) failed! return %d\n", sspi->dma_rx.nents, nents);
return -ENOMEM;
}
SPI_DBG("npages = %d, nents = %d\n", sspi->dma_rx.nents, nents);
dma_desc = dmaengine_prep_slave_sg(sspi->dma_rx.chan, sspi->dma_rx.sg,
nents, DMA_FROM_DEVICE,
DMA_PREP_INTERRUPT|DMA_CTRL_ACK);
if (!dma_desc) {
SPI_ERR("[spi-%d]dmaengine_prep_slave_sg() failed!\n", sspi->master->bus_num);
dma_unmap_sg(&sspi->pdev->dev, sspi->dma_rx.sg,
sspi->dma_rx.nents, DMA_FROM_DEVICE);
return -1;
}
dma_desc->callback = sunxi_spi_dma_cb_rx;
dma_desc->callback_param = (void *)sspi;
dmaengine_submit(dma_desc);
return 0;
}
static int sunxi_spi_config_dma_tx(struct sunxi_spi *sspi, struct spi_transfer *t)
{
int ret = 0;
int nents = 0;
struct dma_slave_config dma_conf = {0};
struct dma_async_tx_descriptor *dma_desc = NULL;
SPI_DBG("t->tx_buf = %p, t->len = %d\n", t->tx_buf, t->len);
ret = sunxi_spi_dma_init_sg(&sspi->dma_tx, (void *)t->tx_buf,
t->len, VERIFY_READ);
if (ret != 0)
return ret;
dma_conf.direction = DMA_MEM_TO_DEV;
dma_conf.dst_addr = sspi->base_addr_phy + SPI_TXDATA_REG;
dma_conf.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_conf.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
dma_conf.src_maxburst = 1;
dma_conf.dst_maxburst = 1;
dma_conf.slave_id = sunxi_slave_id(SUNXI_SPI_DRQ_TX(sspi->master->bus_num), DRQSRC_SDRAM);
dmaengine_slave_config(sspi->dma_tx.chan, &dma_conf);
nents = dma_map_sg(&sspi->pdev->dev, sspi->dma_tx.sg, sspi->dma_tx.nents, DMA_TO_DEVICE);
if (!nents) {
SPI_ERR("dma_map_sg(%d) failed! return %d\n", sspi->dma_tx.nents, nents);
return -ENOMEM;
}
SPI_DBG("npages = %d, nents = %d\n", sspi->dma_tx.nents, nents);
dma_desc = dmaengine_prep_slave_sg(sspi->dma_tx.chan, sspi->dma_tx.sg,
nents, DMA_TO_DEVICE,
DMA_PREP_INTERRUPT|DMA_CTRL_ACK);
if (!dma_desc) {
SPI_ERR("[spi-%d]dmaengine_prep_slave_sg() failed!\n", sspi->master->bus_num);
dma_unmap_sg(&sspi->pdev->dev, sspi->dma_tx.sg,
sspi->dma_tx.nents, DMA_TO_DEVICE);
return -1;
}
dma_desc->callback = sunxi_spi_dma_cb_tx;
dma_desc->callback_param = (void *)sspi;
dmaengine_submit(dma_desc);
return 0;
}
/* config dma src and dst address,
* io or linear address,
* drq type,
* then enqueue
* but not trigger dma start
*/
static int sunxi_spi_config_dma(struct sunxi_spi *sspi, enum spi_dma_dir dma_dir, struct spi_transfer *t)
{
if (dma_dir == SPI_DMA_RDEV)
return sunxi_spi_config_dma_rx(sspi, t);
else
return sunxi_spi_config_dma_tx(sspi, t);
}
/* set dma start flag, if queue, it will auto restart to transfer next queue */
static int sunxi_spi_start_dma(spi_dma_info_t *_info)
{
dma_async_issue_pending(_info->chan);
return 0;
}
/* Unmap and free the SG tables */
static void sunxi_spi_dma_free_sg(struct sunxi_spi *sspi, spi_dma_info_t *info)
{
if (info->dir == SPI_DMA_RWNULL)
return;
dma_unmap_sg(&sspi->pdev->dev, info->sg, info->nents, (enum dma_data_direction)info->dir);
info->dir = SPI_DMA_RWNULL;
/* Never release the DMA channel. Duanmintao
* dma_release_channel(info->chan);
* info->chan = NULL;
*/
}
/* release dma channel, and set queue status to idle. */
static int sunxi_spi_release_dma(struct sunxi_spi *sspi, struct spi_transfer *t)
{
unsigned long flags = 0;
spin_lock_irqsave(&sspi->lock, flags);
sunxi_spi_dma_free_sg(sspi, &sspi->dma_rx);
sunxi_spi_dma_free_sg(sspi, &sspi->dma_tx);
spin_unlock_irqrestore(&sspi->lock, flags);
return 0;
}
#endif
/* ------------------------------dma operation end--------------------------- */
/* check the valid of cs id */
static int sunxi_spi_check_cs(int cs_id, struct sunxi_spi *sspi)
{
int ret = SUNXI_SPI_FAIL;
switch (cs_id) {
case 0:
ret = (sspi->cs_bitmap & SPI_CHIP_SELECT_CS0) ? SUNXI_SPI_OK : SUNXI_SPI_FAIL;
break;
case 1:
ret = (sspi->cs_bitmap & SPI_CHIP_SELECT_CS1) ? SUNXI_SPI_OK : SUNXI_SPI_FAIL;
break;
case 2:
ret = (sspi->cs_bitmap & SPI_CHIP_SELECT_CS2) ? SUNXI_SPI_OK : SUNXI_SPI_FAIL;
break;
case 3:
ret = (sspi->cs_bitmap & SPI_CHIP_SELECT_CS3) ? SUNXI_SPI_OK : SUNXI_SPI_FAIL;
break;
default:
SPI_ERR("[spi-%d]: chip select not support! cs = %d\n", sspi->master->bus_num, cs_id);
break;
}
return ret;
}
/* spi device on or off control */
static void sunxi_spi_cs_control(struct spi_device *spi, bool on)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
unsigned int cs = 0;
if (sspi->cs_control) {
if (on) {
/* set active */
cs = (spi->mode & SPI_CS_HIGH) ? 1 : 0;
} else {
/* set inactive */
cs = (spi->mode & SPI_CS_HIGH) ? 0 : 1;
}
spi_ss_level(sspi->base_addr, cs);
}
}
/* change the properties of spi device with spi transfer.
* every spi transfer must call this interface to update
* the master to the excute transfer
* set clock frequecy, bits per word, mode etc...
* return: >= 0 : succeed; < 0: failed.
*/
static int sunxi_spi_xfer_setup(struct spi_device *spi, struct spi_transfer *t)
{
/* get at the setup function, the properties of spi device */
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
/* allocate in the setup,and free in the cleanup */
struct sunxi_spi_config *config = spi->controller_data;
void __iomem *base_addr = sspi->base_addr;
config->max_speed_hz = (t && t->speed_hz) ? t->speed_hz : spi->max_speed_hz;
config->bits_per_word = (t && t->bits_per_word) ? t->bits_per_word : spi->bits_per_word;
config->bits_per_word = ((config->bits_per_word + 7) / 8) * 8;
if (config->bits_per_word != 8) {
SPI_ERR("[spi-%d]: just support 8bits per word...\n", spi->master->bus_num);
return -EINVAL;
}
if (spi->chip_select >= spi->master->num_chipselect) {
SPI_ERR("[spi-%d]: spi device's chip select = %d exceeds the master supported cs_num[%d]\n",
spi->master->bus_num, spi->chip_select, spi->master->num_chipselect);
return -EINVAL;
}
/* check again board info */
if (sunxi_spi_check_cs(spi->chip_select, sspi) != SUNXI_SPI_OK) {
SPI_ERR("sunxi_spi_check_cs failed! spi_device cs =%d ...\n", spi->chip_select);
return -EINVAL;
}
/* set cs */
spi_set_cs(spi->chip_select, base_addr);
/* master: set spi module clock, set the default frequency10MHz */
spi_set_master(base_addr);
if (config->max_speed_hz > SPI_MAX_FREQUENCY)
return -EINVAL;
#ifdef CONFIG_EVB_PLATFORM
spi_set_clk(config->max_speed_hz, clk_get_rate(sspi->mclk), base_addr, sspi->cdr);
#else
spi_set_clk(config->max_speed_hz, 24000000, base_addr, sspi->cdr);
#endif
/* master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0
* set bit width-default: 8 bits
*/
spi_config_tc(1, spi->mode, base_addr);
spi_enable_tp(base_addr);
return 0;
}
static int sunxi_spi_single_config(struct sunxi_spi *sspi,
struct spi_transfer *t, unsigned long flags)
{
/* single spi mode */
SPI_DBG("in single SPI mode\n");
spin_lock_irqsave(&sspi->lock, flags);
if (t->tx_buf && t->rx_buf) {
/* full duplex */
spi_set_all_burst_received(sspi->base_addr);
spi_set_bc_tc_stc(t->len, 0, t->len, 0, sspi->base_addr);
sspi->mode_type = SINGLE_FULL_DUPLEX_RX_TX;
} else {
/* half duplex transmit(single mode) */
if (t->tx_buf) {
spi_set_bc_tc_stc(t->len, 0, t->len, 0, sspi->base_addr);
sspi->mode_type = SINGLE_HALF_DUPLEX_TX;
} /* half duplex receive(single mode) */
else if (t->rx_buf) {
spi_set_bc_tc_stc(0, t->len, 0, 0, sspi->base_addr);
sspi->mode_type = SINGLE_HALF_DUPLEX_RX;
}
}
spin_unlock_irqrestore(&sspi->lock, flags);
return 0;
}
static int sunxi_spi_dual_config(struct sunxi_spi *sspi, struct spi_transfer *t, unsigned long flags)
{
static int dual_enable;
spin_lock_irqsave(&sspi->lock, flags);
if (t->tx_buf) {
spi_disable_dual(sspi->base_addr);
spi_set_bc_tc_stc(t->len, 0, t->len, 0, sspi->base_addr);
sspi->mode_type = DUAL_HALF_DUPLEX_TX;
if (*(const u8 *)t->tx_buf == SPINOR_OP_READ_1_1_2 ||
*(const u8 *)t->tx_buf == SPINOR_OP_READ4_1_1_2)
dual_enable = 1;
else
dual_enable = 0;
} else if (t->rx_buf) {
if (dual_enable)
spi_enable_dual(sspi->base_addr);
else
spi_disable_dual(sspi->base_addr);
spi_set_bc_tc_stc(0, t->len, 0, 0, sspi->base_addr);
sspi->mode_type = DUAL_HALF_DUPLEX_RX;
}
spin_unlock_irqrestore(&sspi->lock, flags);
return 0;
}
static int sunxi_spi_quad_config(struct sunxi_spi *sspi, struct spi_transfer *t, unsigned long flags)
{
static int quad_enable;
spin_lock_irqsave(&sspi->lock, flags);
if (t->tx_buf) {
spi_disable_quad(sspi->base_addr);
spi_set_bc_tc_stc(t->len, 0, t->len, 0, sspi->base_addr);
sspi->mode_type = QUAD_HALF_DUPLEX_TX;
if (*(const u8 *)t->tx_buf == SPINOR_OP_READ_1_1_4 ||
*(const u8 *)t->tx_buf == SPINOR_OP_READ4_1_1_4)
quad_enable = 1;
else
quad_enable = 0;
} else if (t->rx_buf) { /* half duplex receive(qual mode) */
if (quad_enable)
spi_enable_quad(sspi->base_addr);
else
spi_disable_quad(sspi->base_addr);
spi_set_bc_tc_stc(0, t->len, 0, 0, sspi->base_addr);
sspi->mode_type = QUAD_HALF_DUPLEX_RX;
}
spin_unlock_irqrestore(&sspi->lock, flags);
return 0;
}
static int sunxi_spi_mode_check(struct sunxi_spi *sspi, struct spi_device *spi, struct spi_transfer *t)
{
unsigned long flags = 0;
static int mode;
if (sspi->mode_type != MODE_TYPE_NULL)
return -EINVAL;
if (spi->mode & SPI_RX_DUAL)
mode = SPI_DUAL_MODE;
else if (spi->mode & SPI_RX_QUAD)
mode = SPI_QUAD_MODE;
else
mode = SPI_SINGLE_MODE;
switch (mode) {
case SPI_DUAL_MODE:
sunxi_spi_dual_config(sspi, t, flags);
break;
case SPI_QUAD_MODE:
sunxi_spi_quad_config(sspi, t, flags);
break;
case SPI_SINGLE_MODE:
default:
sunxi_spi_single_config(sspi, t, flags);
break;
}
return 0;
}
static int sunxi_spi_cpu_readl(struct spi_device *spi, struct spi_transfer *t)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
void __iomem *base_addr = sspi->base_addr;
unsigned rx_len = t->len; /* number of bytes sent */
unsigned char *rx_buf = (unsigned char *)t->rx_buf;
unsigned int poll_time = 0x7ffffff;
while (rx_len) {
/* rxFIFO counter */
if (spi_query_rxfifo(base_addr) && (--poll_time > 0)) {
*rx_buf++ = readb(base_addr + SPI_RXDATA_REG);
--rx_len;
}
}
if (poll_time <= 0) {
SPI_ERR("cpu receive data time out!\n");
return -1;
}
return 0;
}
static int sunxi_spi_cpu_writel(struct spi_device *spi, struct spi_transfer *t)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
void __iomem *base_addr = sspi->base_addr;
unsigned long flags = 0;
#ifndef CONFIG_DMA_ENGINE
unsigned char time;
#endif
unsigned tx_len = t->len; /* number of bytes receieved */
unsigned char *tx_buf = (unsigned char *)t->tx_buf;
unsigned int poll_time = 0x7ffffff;
spin_lock_irqsave(&sspi->lock, flags);
for (; tx_len > 0; --tx_len) {
writeb(*tx_buf++, base_addr + SPI_TXDATA_REG);
#ifndef CONFIG_DMA_ENGINE
if (spi_query_txfifo(base_addr) >= MAX_FIFU)
for (time = 2; 0 < time; --time)
;
#endif
}
spin_unlock_irqrestore(&sspi->lock, flags);
while (spi_query_txfifo(base_addr) && (--poll_time > 0))
;
if (poll_time <= 0) {
SPI_ERR("cpu transfer data time out!\n");
return -1;
}
return 0;
}
#ifdef CONFIG_DMA_ENGINE
static int sunxi_spi_dma_rx_config(struct spi_device *spi, struct spi_transfer *t)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
void __iomem *base_addr = sspi->base_addr;
int ret = 0;
/* rxFIFO reday dma request enable */
spi_enable_dma_irq(SPI_FIFO_CTL_RX_DRQEN, base_addr);
ret = sunxi_spi_prepare_dma(&sspi->dma_rx, SPI_DMA_RDEV);
if (ret < 0) {
spi_disable_dma_irq(SPI_FIFO_CTL_RX_DRQEN, base_addr);
spi_disable_irq(SPI_INTEN_TC|SPI_INTEN_ERR, base_addr);
return -EINVAL;
}
sunxi_spi_config_dma(sspi, SPI_DMA_RDEV, t);
sunxi_spi_start_dma(&sspi->dma_rx);
return ret;
}
static int sunxi_spi_dma_tx_config(struct spi_device *spi, struct spi_transfer *t)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
void __iomem *base_addr = sspi->base_addr;
int ret = 0;
spi_enable_dma_irq(SPI_FIFO_CTL_TX_DRQEN, base_addr);
ret = sunxi_spi_prepare_dma(&sspi->dma_tx, SPI_DMA_WDEV);
if (ret < 0) {
spi_disable_dma_irq(SPI_FIFO_CTL_TX_DRQEN, base_addr);
spi_disable_irq(SPI_INTEN_TC|SPI_INTEN_ERR, base_addr);
return -EINVAL;
}
sunxi_spi_config_dma(sspi, SPI_DMA_WDEV, t);
sunxi_spi_start_dma(&sspi->dma_tx);
return ret;
}
static int sunxi_spi_dma_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
void __iomem *base_addr = sspi->base_addr;
unsigned tx_len = t->len; /* number of bytes receieved */
unsigned rx_len = t->len; /* number of bytes sent */
switch (sspi->mode_type) {
case SINGLE_HALF_DUPLEX_RX:
case DUAL_HALF_DUPLEX_RX:
case QUAD_HALF_DUPLEX_RX:
{
/* >64 use DMA transfer, or use cpu */
if (t->len > BULK_DATA_BOUNDARY) {
SPI_DBG(" rx -> by dma\n");
/* For Rx mode, the DMA end(not TC flag) is real end. */
spi_disable_irq(SPI_INTEN_TC, base_addr);
sunxi_spi_dma_rx_config(spi, t);
spi_start_xfer(base_addr);
} else {
SPI_DBG(" rx -> by ahb\n");
/* SMC=1,XCH trigger the transfer */
spi_start_xfer(base_addr);
sunxi_spi_cpu_readl(spi, t);
}
break;
}
case SINGLE_HALF_DUPLEX_TX:
case DUAL_HALF_DUPLEX_TX:
case QUAD_HALF_DUPLEX_TX:
{
/* >64 use DMA transfer, or use cpu */
if (t->len > BULK_DATA_BOUNDARY) {
SPI_DBG(" tx -> by dma\n");
spi_start_xfer(base_addr);
/* txFIFO empty dma request enable */
sunxi_spi_dma_tx_config(spi, t);
} else {
SPI_DBG(" tx -> by ahb\n");
spi_start_xfer(base_addr);
sunxi_spi_cpu_writel(spi, t);
}
break;
}
case SINGLE_FULL_DUPLEX_RX_TX:
{
/* >64 use DMA transfer, or use cpu */
if (t->len > BULK_DATA_BOUNDARY) {
SPI_DBG(" rx and tx -> by dma\n");
/* For Rx mode, the DMA end(not TC flag) is real end. */
spi_disable_irq(SPI_INTEN_TC, base_addr);
sunxi_spi_dma_rx_config(spi, t);
spi_start_xfer(base_addr);
sunxi_spi_dma_tx_config(spi, t);
} else {
SPI_DBG(" rx and tx -> by ahb\n");
if ((rx_len == 0) || (tx_len == 0))
return -EINVAL;
spi_start_xfer(base_addr);
sunxi_spi_cpu_writel(spi, t);
sunxi_spi_cpu_readl(spi, t);
}
break;
}
default:
return -1;
}
return 0;
}
#else
static int sunxi_spi_cpu_transfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
void __iomem *base_addr = sspi->base_addr;
unsigned tx_len = t->len; /* number of bytes receieved */
unsigned rx_len = t->len; /* number of bytes sent */
switch (sspi->mode_type) {
case SINGLE_HALF_DUPLEX_RX:
case DUAL_HALF_DUPLEX_RX:
case QUAD_HALF_DUPLEX_RX:
{
SPI_DBG(" rx -> by ahb\n");
/* SMC=1,XCH trigger the transfer */
spi_start_xfer(base_addr);
sunxi_spi_cpu_readl(spi, t);
break;
}
case SINGLE_HALF_DUPLEX_TX:
case DUAL_HALF_DUPLEX_TX:
case QUAD_HALF_DUPLEX_TX:
{
SPI_DBG(" tx -> by ahb\n");
spi_start_xfer(base_addr);
sunxi_spi_cpu_writel(spi, t);
break;
}
case SINGLE_FULL_DUPLEX_RX_TX:
{
SPI_DBG(" rx and tx -> by ahb\n");
if ((rx_len == 0) || (tx_len == 0))
return -EINVAL;
spi_start_xfer(base_addr);
sunxi_spi_cpu_writel(spi, t);
sunxi_spi_cpu_readl(spi, t);
break;
}
default:
return -1;
}
return 0;
}
#endif
/*
* < 64 : cpu ; >= 64 : dma
* wait for done completion in this function, wakup in the irq hanlder
*/
static int sunxi_spi_xfer(struct spi_device *spi, struct spi_transfer *t)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
void __iomem *base_addr = sspi->base_addr;
unsigned char *tx_buf = (unsigned char *)t->tx_buf;
unsigned char *rx_buf = (unsigned char *)t->rx_buf;
int ret = 0;
SPI_DBG("[spi-%d]: begin transfer, txbuf %p, rxbuf %p, len %d\n",
spi->master->bus_num, tx_buf, rx_buf, t->len);
if ((!t->tx_buf && !t->rx_buf) || !t->len)
return -EINVAL;
/* write 1 to clear 0 */
spi_clr_irq_pending(SPI_INT_STA_MASK, base_addr);
/* disable all DRQ */
spi_disable_dma_irq(SPI_FIFO_CTL_DRQEN_MASK, base_addr);
/* reset tx/rx fifo */
spi_reset_fifo(base_addr);
if (sunxi_spi_mode_check(sspi, spi, t))
return -EINVAL;
/* 1. Tx/Rx error irq,process in IRQ;
* 2. Transfer Complete Interrupt Enable
*/
spi_enable_irq(SPI_INTEN_TC|SPI_INTEN_ERR, base_addr);
#ifdef CONFIG_DMA_ENGINE
sunxi_spi_dma_transfer(spi, t);
#else
sunxi_spi_cpu_transfer(spi, t);
#endif
/* wait for xfer complete in the isr. */
wait_for_completion(&sspi->done);
/* get the isr return code */
if (sspi->result != 0) {
SPI_ERR("[spi-%d]: xfer failed...\n", spi->master->bus_num);
ret = -1;
}
#ifdef CONFIG_DMA_ENGINE
/* release dma resource if necessary */
sunxi_spi_release_dma(sspi, t);
#endif
if (sspi->mode_type != MODE_TYPE_NULL)
sspi->mode_type = MODE_TYPE_NULL;
return ret;
}
/* spi core xfer process */
static void sunxi_spi_work(struct work_struct *work)
{
struct sunxi_spi *sspi = container_of(work, struct sunxi_spi, work);
unsigned long flags = 0;
spin_lock_irqsave(&sspi->lock, flags);
sspi->busy = SPI_BUSY;
/*
* get from messages queue, and then do with them,
* if message queue is empty ,then return and set status to free,
* otherwise process them.
*/
while (!list_empty(&sspi->queue)) {
struct spi_message *msg = NULL;
struct spi_device *spi = NULL;
struct spi_transfer *t = NULL;
unsigned int cs_change = 0;
int status;
/* get message from message queue in sunxi_spi. */
msg = container_of(sspi->queue.next, struct spi_message, queue);
/* then delete from the message queue,now it is alone.*/
list_del_init(&msg->queue);
spin_unlock_irqrestore(&sspi->lock, flags);
/* get spi device from this message */
spi = msg->spi;
/* set default value,no need to change cs,keep select until spi transfer require to change cs. */
cs_change = 1;
/* set message status to succeed. */
status = 0;
/* search the spi transfer in this message, deal with it alone. */
list_for_each_entry(t, &msg->transfers, transfer_list) {
if (t->bits_per_word || t->speed_hz) {
status = sunxi_spi_xfer_setup(spi, t);
if (status < 0)
break;
SPI_DBG("[spi-%d]: xfer setup\n", sspi->master->bus_num);
}
/* first active the cs */
if (cs_change)
sspi->cs_control(spi, 1);
/* update the new cs value */
cs_change = t->cs_change;
/* do transfer
* > 64 : dma ; <= 64 : cpu
* wait for done completion in this function, wakup in the irq hanlder
*/
status = sunxi_spi_xfer(spi, t);
if (status)
break;/* fail quit, zero means succeed */
/* accmulate the value in the message */
msg->actual_length += t->len;
/* accmulate the value in the message */
/* may be need to delay */
if (t->delay_usecs)
udelay(t->delay_usecs);
/* if zero ,keep active,otherwise deactived. */
if (cs_change)
sspi->cs_control(spi, 0);
}
msg->status = status;
/* wakup the uplayer caller,complete one message */
msg->complete(msg->context);
/* fail or need to change cs */
if (status || !cs_change)
sspi->cs_control(spi, 0);
/* restore default value. */
sunxi_spi_xfer_setup(spi, NULL);
spin_lock_irqsave(&sspi->lock, flags);
}
/* set spi to free */
sspi->busy = SPI_FREE;
spin_unlock_irqrestore(&sspi->lock, flags);
}
/* wake up the sleep thread, and give the result code */
static irqreturn_t sunxi_spi_handler(int irq, void *dev_id)
{
struct sunxi_spi *sspi = (struct sunxi_spi *)dev_id;
void __iomem *base_addr = sspi->base_addr;
unsigned int status = 0;
unsigned long flags = 0;
spin_lock_irqsave(&sspi->lock, flags);
status = spi_qry_irq_pending(base_addr);
spi_clr_irq_pending(status, base_addr);
SPI_DBG("[spi-%d]: irq status = %x\n", sspi->master->bus_num, status);
sspi->result = 0; /* assume succeed */
/* master mode, Transfer Complete Interrupt */
if (status & SPI_INT_STA_TC) {
SPI_DBG("[spi-%d]: SPI TC comes\n", sspi->master->bus_num);
spi_disable_irq(SPI_INT_STA_TC | SPI_INT_STA_ERR, base_addr);
/*wakup uplayer, by the sem */
complete(&sspi->done);
spin_unlock_irqrestore(&sspi->lock, flags);
return IRQ_HANDLED;
} else if (status & SPI_INT_STA_ERR) { /* master mode:err */
SPI_ERR("[spi-%d]: SPI ERR %#x comes\n", sspi->master->bus_num, status);
/* error process, release dma in the workqueue,should not be here */
spi_disable_irq(SPI_INT_STA_TC | SPI_INT_STA_ERR, base_addr);
spi_soft_reset(base_addr);
sspi->result = -1;
complete(&sspi->done);
spin_unlock_irqrestore(&sspi->lock, flags);
return IRQ_HANDLED;
}
SPI_DBG("[spi-%d]: SPI NONE comes\n", sspi->master->bus_num);
spin_unlock_irqrestore(&sspi->lock, flags);
return IRQ_NONE;
}
/* interface 1 */
static int sunxi_spi_transfer(struct spi_device *spi, struct spi_message *msg)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
unsigned long flags;
msg->actual_length = 0;
msg->status = -EINPROGRESS;
spin_lock_irqsave(&sspi->lock, flags);
/* add msg to the sunxi_spi queue */
list_add_tail(&msg->queue, &sspi->queue);
/* add work to the workqueue,schedule the cpu. */
queue_work(sspi->workqueue, &sspi->work);
spin_unlock_irqrestore(&sspi->lock, flags);
return 0;
}
/* interface 2, setup the frequency and default status */
static int sunxi_spi_setup(struct spi_device *spi)
{
struct sunxi_spi *sspi = spi_master_get_devdata(spi->master);
struct sunxi_spi_config *config = spi->controller_data;/* general is null. */
unsigned long flags;
/* just support 8 bits per word */
if (spi->bits_per_word != 8)
return -EINVAL;
/* first check its valid,then set it as default select,finally set its */
if (sunxi_spi_check_cs(spi->chip_select, sspi) == SUNXI_SPI_FAIL) {
SPI_ERR("[spi-%d]: not support cs-%d\n", spi->master->bus_num, spi->chip_select);
return -EINVAL;
}
if (spi->max_speed_hz > SPI_MAX_FREQUENCY)
return -EINVAL;
if (!config) {
config = kzalloc(sizeof(*config), GFP_KERNEL);
if (!config)
return -ENOMEM;
spi->controller_data = config;
}
/* set the default value with spi device
* can change by every spi transfer
*/
config->bits_per_word = spi->bits_per_word;
config->max_speed_hz = spi->max_speed_hz;
config->mode = spi->mode;
spin_lock_irqsave(&sspi->lock, flags);
/* if spi is free, then deactived the spi device */
if (sspi->busy & SPI_FREE) {
/* set chip select number */
spi_set_cs(spi->chip_select, sspi->base_addr);
/* deactivate chip select */
sspi->cs_control(spi, 0);
}
spin_unlock_irqrestore(&sspi->lock, flags);
return 0;
}
/* interface 3 */
static void sunxi_spi_cleanup(struct spi_device *spi)
{
kfree(spi->controller_data);
spi->controller_data = NULL;
}
static int sunxi_spi_chan_is_enable(int _ch)
{
return spi_used_mask & SUNXI_SPI_CHAN_MASK(_ch);
}
static int sunxi_spi_select_gpio_state(struct pinctrl *pctrl, char *name, u32 no)
{
int ret = 0;
struct pinctrl_state *pctrl_state = NULL;
pctrl_state = pinctrl_lookup_state(pctrl, name);
if (IS_ERR(pctrl_state)) {
SPI_ERR("SPI%d pinctrl_lookup_state(%s) failed! return %p\n", no, name, pctrl_state);
return -1;
}
ret = pinctrl_select_state(pctrl, pctrl_state);
if (ret < 0)
SPI_ERR("SPI%d pinctrl_select_state(%s) failed! return %d\n", no, name, ret);
return ret;
}
static int sunxi_spi_request_gpio(struct sunxi_spi *sspi)
{
int bus_no = sspi->pdev->id;
if (sspi->pctrl != NULL)
return sunxi_spi_select_gpio_state(sspi->pctrl, PINCTRL_STATE_DEFAULT, bus_no);
if (!sunxi_spi_chan_is_enable(bus_no))
return -1;
SPI_DBG("Pinctrl init %d : [%s]\n", bus_no, sspi->pdev->dev.init_name);
sspi->pctrl = devm_pinctrl_get(&sspi->pdev->dev);
if (IS_ERR(sspi->pctrl)) {
SPI_ERR("SPI%d devm_pinctrl_get() failed! return %ld\n",
bus_no, PTR_ERR(sspi->pctrl));
return -1;
}
return sunxi_spi_select_gpio_state(sspi->pctrl, PINCTRL_STATE_DEFAULT, bus_no);
}
static void sunxi_spi_release_gpio(struct sunxi_spi *sspi)
{
devm_pinctrl_put(sspi->pctrl);
sspi->pctrl = NULL;
}
static int sunxi_spi_clk_init(struct sunxi_spi *sspi, u32 mod_clk)
{
int ret = 0;
long rate = 0;
sspi->pclk = of_clk_get(sspi->pdev->dev.of_node, 0);
if (IS_ERR_OR_NULL(sspi->pclk)) {
SPI_ERR("[spi-%d] Unable to acquire module clock '%s', return %x\n",
sspi->master->bus_num, sspi->dev_name, PTR_RET(sspi->pclk));
return -1;
}
sspi->mclk = of_clk_get(sspi->pdev->dev.of_node, 1);
if (IS_ERR_OR_NULL(sspi->mclk)) {
SPI_ERR("[spi-%d] Unable to acquire module clock '%s', return %x\n",
sspi->master->bus_num, sspi->dev_name, PTR_RET(sspi->mclk));
return -1;
}
ret = clk_set_parent(sspi->mclk, sspi->pclk);
if (ret != 0) {
SPI_ERR("[spi-%d] clk_set_parent() failed! return %d\n",
sspi->master->bus_num, ret);
return -1;
}
rate = clk_round_rate(sspi->mclk, mod_clk);
if (clk_set_rate(sspi->mclk, rate)) {
SPI_ERR("[spi-%d] spi clk_set_rate failed\n", sspi->master->bus_num);
return -1;
}
SPI_INF("[spi-%d] mclk %u\n", sspi->master->bus_num, (unsigned)clk_get_rate(sspi->mclk));
if (clk_prepare_enable(sspi->mclk)) {
SPI_ERR("[spi-%d] Couldn't enable module clock 'spi'\n", sspi->master->bus_num);
return -EBUSY;
}
return clk_get_rate(sspi->mclk);
}
static int sunxi_spi_clk_exit(struct sunxi_spi *sspi)
{
if (IS_ERR_OR_NULL(sspi->mclk)) {
SPI_ERR("[spi-%d] SPI mclk handle is invalid!\n", sspi->master->bus_num);
return -1;
}
clk_disable_unprepare(sspi->mclk);
clk_put(sspi->mclk);
clk_put(sspi->pclk);
sspi->mclk = NULL;
sspi->pclk = NULL;
return 0;
}
static int sunxi_spi_hw_init(struct sunxi_spi *sspi, struct sunxi_spi_platform_data *pdata)
{
void __iomem *base_addr = sspi->base_addr;
u32 sclk_freq_def = 0;
int sclk_freq = 0;
int ret;
if (spi_regulator_request(pdata) < 0) {
SPI_ERR("[spi-%d] request regulator failed!\n", sspi->master->bus_num);
return -1;
}
spi_regulator_enable(pdata);
if (sunxi_spi_request_gpio(sspi) < 0) {
SPI_ERR("[spi-%d] Request GPIO failed!\n", sspi->master->bus_num);
return -1;
}
ret = of_property_read_u32(sspi->pdev->dev.of_node, "clock-frequency", &sclk_freq_def);
if (ret) {
SPI_ERR("[spi-%d] Get clock-frequency property failed\n", sspi->master->bus_num);
return -1;
}
ret = of_property_read_u32(sspi->pdev->dev.of_node, "cdr", &sspi->cdr);
if (ret)
SPI_DBG("[spi-%d] Get cdr property failed\n", sspi->master->bus_num);
sclk_freq = sunxi_spi_clk_init(sspi, sclk_freq_def);
if (sclk_freq < 0) {
SPI_ERR("[spi-%d] sunxi_spi_clk_init(%s) failed!\n", sspi->master->bus_num, sspi->dev_name);
return -1;
}
/* 1. enable the spi module */
spi_enable_bus(base_addr);
/* 2. set the default chip select */
if (sunxi_spi_check_cs(0, sspi) == SUNXI_SPI_OK)
spi_set_cs(0, base_addr);
else
spi_set_cs(1, base_addr);
/* 3. master: set spi module clock;
* 4. set the default frequency 10MHz
*/
spi_set_master(base_addr);
spi_set_clk(10000000, sclk_freq, base_addr, sspi->cdr);
/* 5. master : set POL,PHA,SSOPL,LMTF,DDB,DHB; default: SSCTL=0,SMC=1,TBW=0.
* 6. set bit width-default: 8 bits
*/
spi_config_tc(1, SPI_MODE_0, base_addr);
spi_enable_tp(base_addr);
/* 7. manual control the chip select */
spi_ss_ctrl(base_addr, 1);
/* 8. reset fifo */
spi_reset_fifo(base_addr);
return 0;
}
static int sunxi_spi_hw_exit(struct sunxi_spi *sspi, struct sunxi_spi_platform_data *pdata)
{
/* disable the spi controller */
spi_disable_bus(sspi->base_addr);
/* disable module clock */
sunxi_spi_clk_exit(sspi);
sunxi_spi_release_gpio(sspi);
spi_regulator_disable(pdata);
spi_regulator_release(pdata);
return 0;
}
static ssize_t sunxi_spi_info_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct platform_device *pdev = container_of(dev, struct platform_device, dev);
struct sunxi_spi_platform_data *pdata = dev->platform_data;
return snprintf(buf, PAGE_SIZE,
"pdev->id = %d\n"
"pdev->name = %s\n"
"pdev->num_resources = %u\n"
"pdev->resource.mem = [%pa, %pa]\n"
"pdev->resource.irq = %pa\n"
"pdev->dev.platform_data.cs_bitmap = %d\n"
"pdev->dev.platform_data.cs_num = %d\n"
"pdev->dev.platform_data.regulator = 0x%p\n"
"pdev->dev.platform_data.regulator_id = %s\n",
pdev->id, pdev->name, pdev->num_resources,
&pdev->resource[0].start, &pdev->resource[0].end, &pdev->resource[1].start,
pdata->cs_bitmap, pdata->cs_num, pdata->regulator, pdata->regulator_id);
}
static struct device_attribute sunxi_spi_info_attr =
__ATTR(info, S_IRUGO, sunxi_spi_info_show, NULL);
static ssize_t sunxi_spi_status_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct spi_master *master = dev_get_drvdata(dev);
struct sunxi_spi *sspi = (struct sunxi_spi *)&master[1];
char const *spi_mode[] = {"Single mode, half duplex read",
"Single mode, half duplex write",
"Single mode, full duplex read and write",
"Dual mode, half duplex read",
"Dual mode, half duplex write",
"Null"};
char const *busy_state[] = {"Unknown", "Free", "Suspend", "Busy"};
char const *result_str[] = {"Success", "Fail"};
#ifdef CONFIG_DMA_ENGINE
char const *dma_dir[] = {"DMA NULL", "DMA read", "DMA write"};
#endif
if (master == NULL)
return snprintf(buf, PAGE_SIZE, "%s\n", "spi_master is NULL!");
return snprintf(buf, PAGE_SIZE,
"master->bus_num = %d\n"
"master->num_chipselect = %d\n"
"master->dma_alignment = %d\n"
"master->mode_bits = %d\n"
"master->flags = 0x%x, ->bus_lock_flag = 0x%x\n"
"master->busy = %d, ->running = %d, ->rt = %d\n"
"sspi->mode_type = %d [%s]\n"
"sspi->irq = %d [%s]\n"
"sspi->cs_bitmap = %d\n"
#ifdef CONFIG_DMA_ENGINE
"sspi->dma_tx.dir = %d [%s]\n"
"sspi->dma_rx.dir = %d [%s]\n"
#endif
"sspi->busy = %d [%s]\n"
"sspi->result = %d [%s]\n"
"sspi->base_addr = 0x%p, the SPI control register:\n"
"[VER] 0x%02x = 0x%08x, [GCR] 0x%02x = 0x%08x, [TCR] 0x%02x = 0x%08x\n"
"[ICR] 0x%02x = 0x%08x, [ISR] 0x%02x = 0x%08x, [FCR] 0x%02x = 0x%08x\n"
"[FSR] 0x%02x = 0x%08x, [WCR] 0x%02x = 0x%08x, [CCR] 0x%02x = 0x%08x\n"
"[BCR] 0x%02x = 0x%08x, [TCR] 0x%02x = 0x%08x, [BCC] 0x%02x = 0x%08x\n"
"[DMA] 0x%02x = 0x%08x, [TXR] 0x%02x = 0x%08x, [RXD] 0x%02x = 0x%08x\n",
master->bus_num, master->num_chipselect, master->dma_alignment,
master->mode_bits, master->flags, master->bus_lock_flag,
master->busy, master->running, master->rt,
sspi->mode_type, spi_mode[sspi->mode_type],
sspi->irq, sspi->dev_name, sspi->cs_bitmap,
#ifdef CONFIG_DMA_ENGINE
sspi->dma_tx.dir, dma_dir[sspi->dma_tx.dir],
sspi->dma_rx.dir, dma_dir[sspi->dma_rx.dir],
#endif
sspi->busy, busy_state[sspi->busy],
sspi->result, result_str[sspi->result],
sspi->base_addr,
SPI_VER_REG, readl(sspi->base_addr + SPI_VER_REG),
SPI_GC_REG, readl(sspi->base_addr + SPI_GC_REG),
SPI_TC_REG, readl(sspi->base_addr + SPI_TC_REG),
SPI_INT_CTL_REG, readl(sspi->base_addr + SPI_INT_CTL_REG),
SPI_INT_STA_REG, readl(sspi->base_addr + SPI_INT_STA_REG),
SPI_FIFO_CTL_REG, readl(sspi->base_addr + SPI_FIFO_CTL_REG),
SPI_FIFO_STA_REG, readl(sspi->base_addr + SPI_FIFO_STA_REG),
SPI_WAIT_CNT_REG, readl(sspi->base_addr + SPI_WAIT_CNT_REG),
SPI_CLK_CTL_REG, readl(sspi->base_addr + SPI_CLK_CTL_REG),
SPI_BURST_CNT_REG, readl(sspi->base_addr + SPI_BURST_CNT_REG),
SPI_TRANSMIT_CNT_REG, readl(sspi->base_addr + SPI_TRANSMIT_CNT_REG),
SPI_BCC_REG, readl(sspi->base_addr + SPI_BCC_REG),
SPI_DMA_CTL_REG, readl(sspi->base_addr + SPI_DMA_CTL_REG),
SPI_TXDATA_REG, readl(sspi->base_addr + SPI_TXDATA_REG),
SPI_RXDATA_REG, readl(sspi->base_addr + SPI_RXDATA_REG));
}
static struct device_attribute sunxi_spi_status_attr =
__ATTR(status, S_IRUGO, sunxi_spi_status_show, NULL);
static void sunxi_spi_sysfs(struct platform_device *_pdev)
{
device_create_file(&_pdev->dev, &sunxi_spi_info_attr);
device_create_file(&_pdev->dev, &sunxi_spi_status_attr);
}
static int sunxi_spi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct resource *mem_res;
struct sunxi_spi *sspi;
struct sunxi_spi_platform_data *pdata;
struct spi_master *master;
char spi_para[16] = {0};
int ret = 0, err = 0, irq;
if (np == NULL) {
SPI_ERR("SPI failed to get of_node\n");
return -ENODEV;
}
pdev->id = of_alias_get_id(np, "spi");
if (pdev->id < 0) {
SPI_ERR("SPI failed to get alias id\n");
return -EINVAL;
}
#ifdef CONFIG_DMA_ENGINE
pdev->dev.dma_mask = &sunxi_spi_dma_mask;
pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
#endif
pdata = kzalloc(sizeof(struct sunxi_spi_platform_data), GFP_KERNEL);
if (pdata == NULL) {
SPI_ERR("SPI failed to alloc mem\n");
return -ENOMEM;
}
pdev->dev.platform_data = pdata;
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res == NULL) {
SPI_ERR("Unable to get spi MEM resource\n");
ret = -ENXIO;
goto err0;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
SPI_ERR("No spi IRQ specified\n");
ret = -ENXIO;
goto err0;
}
snprintf(spi_para, sizeof(spi_para), "spi%d_cs_number", pdev->id);
ret = of_property_read_u32(np, spi_para, &pdata->cs_num);
if (ret) {
SPI_ERR("Failed to get cs_number property\n");
ret = -EINVAL;
goto err0;
}
snprintf(spi_para, sizeof(spi_para), "spi%d_cs_bitmap", pdev->id);
ret = of_property_read_u32(np, spi_para, &pdata->cs_bitmap);
if (ret) {
SPI_ERR("Failed to get cs_bitmap property\n");
ret = -EINVAL;
goto err0;
}
/* create spi master */
master = spi_alloc_master(&pdev->dev, sizeof(struct sunxi_spi));
if (master == NULL) {
SPI_ERR("Unable to allocate SPI Master\n");
ret = -ENOMEM;
goto err0;
}
platform_set_drvdata(pdev, master);
sspi = spi_master_get_devdata(master);
memset(sspi, 0, sizeof(struct sunxi_spi));
sspi->master = master;
sspi->irq = irq;
#ifdef CONFIG_DMA_ENGINE
sspi->dma_rx.dir = SPI_DMA_RWNULL;
sspi->dma_tx.dir = SPI_DMA_RWNULL;
#endif
sspi->cs_control = sunxi_spi_cs_control;
sspi->cs_bitmap = pdata->cs_bitmap; /* cs0-0x1; cs1-0x2; cs0&cs1-0x3. */
sspi->busy = SPI_FREE;
sspi->mode_type = MODE_TYPE_NULL;
master->dev.of_node = pdev->dev.of_node;
master->bus_num = pdev->id;
master->setup = sunxi_spi_setup;
master->cleanup = sunxi_spi_cleanup;
master->transfer = sunxi_spi_transfer;
master->num_chipselect = pdata->cs_num;
/* the spi->mode bits understood by this driver: */
master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST |
SPI_TX_DUAL | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD;
snprintf(sspi->dev_name, sizeof(sspi->dev_name), SUNXI_SPI_DEV_NAME"%d", pdev->id);
err = request_irq(sspi->irq, sunxi_spi_handler, 0, sspi->dev_name, sspi);
if (err) {
SPI_ERR("Cannot request IRQ\n");
goto err1;
}
if (request_mem_region(mem_res->start,
resource_size(mem_res), pdev->name) == NULL) {
SPI_ERR("Req mem region failed\n");
ret = -ENXIO;
goto err2;
}
sspi->base_addr = ioremap(mem_res->start, resource_size(mem_res));
if (sspi->base_addr == NULL) {
SPI_ERR("Unable to remap IO\n");
ret = -ENXIO;
goto err3;
}
sspi->base_addr_phy = mem_res->start;
sspi->workqueue = create_singlethread_workqueue(dev_name(master->dev.parent));
if (sspi->workqueue == NULL) {
SPI_ERR("Unable to create workqueue\n");
ret = -EPERM;
goto err5;
}
sspi->pdev = pdev;
pdev->dev.init_name = sspi->dev_name;
spi_used_mask |= SUNXI_SPI_CHAN_MASK(master->bus_num);
/* Setup Deufult Mode */
ret = sunxi_spi_hw_init(sspi, pdata);
if (ret != 0) {
SPI_ERR("spi hw init failed!\n");
goto err6;
}
spin_lock_init(&sspi->lock);
init_completion(&sspi->done);
INIT_WORK(&sspi->work, sunxi_spi_work);/* banding the process handler */
INIT_LIST_HEAD(&sspi->queue);
if (spi_register_master(master)) {
SPI_ERR("cannot register SPI master\n");
ret = -EBUSY;
goto err7;
}
sunxi_spi_sysfs(pdev);
SPI_INF("loaded for Bus SPI-%d with %d Slaves at most\n",
pdev->id, master->num_chipselect);
SPI_INF("[spi-%d]: driver probe succeed, base %p, irq %d!\n",
master->bus_num, sspi->base_addr, sspi->irq);
return 0;
err7:
sunxi_spi_hw_exit(sspi, pdata);
err6:
destroy_workqueue(sspi->workqueue);
err5:
iounmap(sspi->base_addr);
err3:
release_mem_region(mem_res->start, resource_size(mem_res));
err2:
free_irq(sspi->irq, sspi);
err1:
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
err0:
kfree(pdata);
return ret;
}
static int sunxi_spi_remove(struct platform_device *pdev)
{
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct sunxi_spi *sspi = spi_master_get_devdata(master);
struct resource *mem_res;
unsigned long flags;
spin_lock_irqsave(&sspi->lock, flags);
sspi->busy |= SPI_FREE;
spin_unlock_irqrestore(&sspi->lock, flags);
while (sspi->busy & SPI_BUSY)
msleep(10);
sunxi_spi_hw_exit(sspi, pdev->dev.platform_data);
spi_unregister_master(master);
destroy_workqueue(sspi->workqueue);
iounmap(sspi->base_addr);
mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (mem_res != NULL)
release_mem_region(mem_res->start, resource_size(mem_res));
platform_set_drvdata(pdev, NULL);
spi_master_put(master);
kfree(pdev->dev.platform_data);
return 0;
}
#ifdef CONFIG_PM
static int sunxi_spi_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct sunxi_spi *sspi = spi_master_get_devdata(master);
unsigned long flags;
spin_lock_irqsave(&sspi->lock, flags);
sspi->busy |= SPI_SUSPND;
spin_unlock_irqrestore(&sspi->lock, flags);
while (sspi->busy & SPI_BUSY)
msleep(10);
spi_disable_bus(sspi->base_addr);
sunxi_spi_clk_exit(sspi);
sunxi_spi_select_gpio_state(sspi->pctrl, PINCTRL_STATE_SLEEP, master->bus_num);
spi_regulator_disable(dev->platform_data);
SPI_INF("[spi-%d]: suspend okay..\n", master->bus_num);
return 0;
}
static int sunxi_spi_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct spi_master *master = spi_master_get(platform_get_drvdata(pdev));
struct sunxi_spi *sspi = spi_master_get_devdata(master);
unsigned long flags;
sunxi_spi_hw_init(sspi, pdev->dev.platform_data);
spin_lock_irqsave(&sspi->lock, flags);
sspi->busy = SPI_FREE;
spin_unlock_irqrestore(&sspi->lock, flags);
SPI_INF("[spi-%d]: resume okay..\n", master->bus_num);
return 0;
}
static const struct dev_pm_ops sunxi_spi_dev_pm_ops = {
.suspend = sunxi_spi_suspend,
.resume = sunxi_spi_resume,
};
#define SUNXI_SPI_DEV_PM_OPS (&sunxi_spi_dev_pm_ops)
#else
#define SUNXI_SPI_DEV_PM_OPS NULL
#endif /* CONFIG_PM */
static const struct of_device_id sunxi_spi_match[] = {
{ .compatible = "allwinner,sun8i-spi", },
{ .compatible = "allwinner,sun50i-spi", },
{},
};
MODULE_DEVICE_TABLE(of, sunxi_spi_match);
static struct platform_driver sunxi_spi_driver = {
.probe = sunxi_spi_probe,
.remove = sunxi_spi_remove,
.driver = {
.name = SUNXI_SPI_DEV_NAME,
.owner = THIS_MODULE,
.pm = SUNXI_SPI_DEV_PM_OPS,
.of_match_table = sunxi_spi_match,
},
};
static int __init sunxi_spi_init(void)
{
return platform_driver_register(&sunxi_spi_driver);
}
static void __exit sunxi_spi_exit(void)
{
platform_driver_unregister(&sunxi_spi_driver);
}
module_init(sunxi_spi_init);
module_exit(sunxi_spi_exit);
MODULE_AUTHOR("pannan");
MODULE_DESCRIPTION("SUNXI SPI BUS Driver");
MODULE_ALIAS("platform:"SUNXI_SPI_DEV_NAME);
MODULE_LICENSE("GPL");