1132 lines
31 KiB
C
1132 lines
31 KiB
C
/*
|
|
* GSC3280 SoC SPI controller driver
|
|
*
|
|
* Copyright (C) 2013 BLX IC Design Corp.,Ltd.
|
|
* Author: Davied, apple_guet@126.com
|
|
* Modify by: lufei, lufei@china-cpu.com
|
|
*
|
|
*/
|
|
#include <linux/platform_device.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/highmem.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/spi/spi.h>
|
|
#include <linux/spi/gsc3280_spi.h>
|
|
#include <linux/io.h>
|
|
#include <linux/scatterlist.h>
|
|
#include <gsc3280/gpio.h>
|
|
#include <asm/clock.h>
|
|
#include <gsc3280/gsc3280_regs.h>
|
|
|
|
#ifdef CONFIG_GSC3280_SPI_DEBUG
|
|
#define DBG(msg...) do { \
|
|
printk(KERN_INFO msg); \
|
|
} while (0)
|
|
#else
|
|
#define DBG(msg...) do { } while(0)
|
|
#endif
|
|
|
|
#define START_STATE ((void *)0)
|
|
#define RUNNING_STATE ((void *)1)
|
|
#define DONE_STATE ((void *)2)
|
|
#define ERROR_STATE ((void *)-1)
|
|
|
|
#define MRST_SPI_DEASSERT 0
|
|
#define MRST_SPI_ASSERT 1
|
|
|
|
#define GSC_SPI_ENABLE 0x01
|
|
#define GSC_SPI_DISABLE 0x00
|
|
|
|
/* Slave spi_dev related */
|
|
struct chip_data {
|
|
u8 n_bytes; /* current is a 1/2/4 byte op */
|
|
u8 cs_type; /* 0: chip cs, 1: gpio cs */
|
|
u8 cs_value; /* chip select value */
|
|
u8 lsb_flg; /* 1:LSB, 0: MSB */
|
|
u8 bits_per_word;
|
|
|
|
u16 cr;
|
|
u16 clk_div; /* baud rate divider, reg value */
|
|
u32 speed_hz; /* baud rate */
|
|
u32 pin_cs[3]; /* chip select pin */
|
|
u32 pin_cnt;
|
|
|
|
u8 poll_mode; /* 1 means use poll mode */
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
u8 enable_dma;
|
|
u32 dma_width;
|
|
#endif
|
|
};
|
|
|
|
struct gsc3280_spi;
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
|
|
//#include <linux/gsc_dmac.h>
|
|
#include <linux/gsc3280_dma.h>
|
|
|
|
struct gsc3280_spi_dma_ops {
|
|
int (*dma_init)(struct gsc3280_spi *gscs);
|
|
void (*dma_exit)(struct gsc3280_spi *gscs);
|
|
int (*dma_transfer)(struct gsc3280_spi *gscs, int cs_change);
|
|
};
|
|
|
|
#if 0
|
|
struct gsc3280_spi_dma_platform_data {
|
|
struct gsc3280_dma_slave dmas_tx;
|
|
struct gsc3280_dma_slave dmas_rx;
|
|
};
|
|
#endif
|
|
#endif
|
|
|
|
typedef enum {
|
|
GSC_SPI_QUEUE_STOP = 0x00,
|
|
GSC_SPI_QUEUE_RUN,
|
|
} spi_queue_state_e;
|
|
|
|
struct gsc3280_spi {
|
|
u8 n_bytes; //current is a 1 or 2 bytes op
|
|
u8 max_bits_per_word; //maxim is 16b
|
|
u8 bus_num;
|
|
u8 num_cs; //supported slave numbers
|
|
u8 busy;
|
|
int cs_change;
|
|
int irq;
|
|
u32 fifo_len; //depth of the FIFO buffer
|
|
u32 max_freq; //max bus freq supported
|
|
size_t len;
|
|
|
|
void *tx; //current tx point
|
|
void *tx_end;
|
|
void *rx; //current rx point
|
|
void *rx_end;
|
|
|
|
struct clk *clk;
|
|
spinlock_t slock;
|
|
void __iomem *regs;
|
|
struct list_head queue; //work queue
|
|
struct spi_master *master;
|
|
struct spi_message *cur_msg;
|
|
struct spi_transfer *cur_transfer;
|
|
struct chip_data *cur_chip;
|
|
struct chip_data *prev_chip;
|
|
struct workqueue_struct *workqueue; /* Driver message queue */
|
|
struct work_struct pump_messages;
|
|
spi_queue_state_e queue_state;
|
|
struct tasklet_struct pump_transfers; /* Message Transfer pump */
|
|
irqreturn_t (*transfer_handler)(struct gsc3280_spi *gscs);
|
|
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
u32 dma_width;
|
|
int dma_mapped;
|
|
dma_addr_t rx_dma;
|
|
dma_addr_t tx_dma;
|
|
size_t rx_map_len;
|
|
size_t tx_map_len;
|
|
int dma_inited;
|
|
unsigned long paddr;
|
|
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 gsc3280_spi_dma_ops *dma_ops;
|
|
void *dma_priv; /* platform relate info */
|
|
struct pci_dev *dmac;
|
|
#endif
|
|
|
|
#ifdef CONFIG_DEBUG_FS
|
|
struct dentry *debugfs;
|
|
#endif
|
|
};
|
|
|
|
static inline void gsc3280_spi_chip_sel(struct gsc3280_spi *gscs, u16 cs)
|
|
{
|
|
writel(cs, gscs->regs + GSC_SPI_CS);
|
|
}
|
|
|
|
/* Disable IRQ bits */
|
|
static inline void gsc3280_spi_mask_intr(struct gsc3280_spi *gscs, u32 mask)
|
|
{
|
|
u32 new_mask = 0;
|
|
|
|
if(gscs->irq == 14)
|
|
new_mask = readl(gscs->regs + GSC_SPI_ISR) | mask;
|
|
else
|
|
new_mask = readl(gscs->regs + GSC_SPI_IMSR) | mask;
|
|
writel(new_mask, gscs->regs + GSC_SPI_IMSR);
|
|
}
|
|
|
|
/* Enable IRQ bits */
|
|
static inline void gsc3280_spi_umask_intr(struct gsc3280_spi *gscs, u32 mask)
|
|
{
|
|
u32 new_mask = 0;
|
|
|
|
if(gscs->irq == 14)
|
|
new_mask = readl(gscs->regs + GSC_SPI_ISR) & ~mask;
|
|
else
|
|
new_mask = readl(gscs->regs + GSC_SPI_IMSR) & ~mask;
|
|
writel(new_mask, gscs->regs + GSC_SPI_IMSR);
|
|
}
|
|
|
|
static inline void gsc3280_enable_spi(struct gsc3280_spi *gscs, int enable)
|
|
{
|
|
u32 cr = 0;
|
|
|
|
if(enable)
|
|
cr = readl(gscs->regs + GSC_SPI_CTRL) | (GSC_SPI_CTL_EN);
|
|
else
|
|
cr = readl(gscs->regs + GSC_SPI_CTRL) & ~(GSC_SPI_CTL_EN);
|
|
writel(cr, gscs->regs + GSC_SPI_CTRL);
|
|
}
|
|
|
|
static inline void gsc3280_spi_clr_fifo(struct gsc3280_spi *gscs)
|
|
{
|
|
u32 cr = GSC_SPI_CTL_CLR;
|
|
cr |= readl(gscs->regs + GSC_SPI_CTRL);
|
|
writel(cr, gscs->regs + GSC_SPI_CTRL);
|
|
}
|
|
|
|
static void *gsc3280_spi_next_transfer(struct gsc3280_spi *gscs)
|
|
{
|
|
struct spi_message *msg = gscs->cur_msg;
|
|
struct spi_transfer *trans = gscs->cur_transfer;
|
|
|
|
if (trans->transfer_list.next != &msg->transfers) {
|
|
gscs->cur_transfer = list_entry(trans->transfer_list.next, struct spi_transfer, transfer_list);
|
|
return RUNNING_STATE;
|
|
} else
|
|
return DONE_STATE;
|
|
}
|
|
|
|
/* Caller already set message->status; dma and pio irqs are blocked */
|
|
static void gsc3280_spi_giveback(struct gsc3280_spi *gscs)
|
|
{
|
|
struct spi_message *msg;
|
|
unsigned long flags = 0;
|
|
int i;
|
|
int cs_type = gscs->cur_chip->cs_type;
|
|
int cs_value = gscs->cur_chip->cs_value;
|
|
int pin_cnt = gscs->cur_chip->pin_cnt;
|
|
int *pin_cs = gscs->cur_chip->pin_cs;
|
|
|
|
spin_lock_irqsave(&gscs->slock, flags);
|
|
msg = gscs->cur_msg;
|
|
gscs->cur_msg = NULL;
|
|
gscs->cur_transfer = NULL;
|
|
gscs->prev_chip = gscs->cur_chip;
|
|
gscs->cur_chip = NULL;
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
gscs->dma_mapped = 0;
|
|
#endif
|
|
queue_work(gscs->workqueue, &gscs->pump_messages);
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
|
|
DBG("gsc3280_spi_giveback\n");
|
|
if (cs_type == 0) //chip cs
|
|
writel(1, gscs->regs + GSC_SPI_CS);
|
|
else{ //io cs
|
|
for(i=0;i<pin_cnt;i++)
|
|
gpio_set_value(pin_cs[i],~((cs_value>>(i+1))&0x1));
|
|
}
|
|
msg->state = NULL;
|
|
if (msg->complete)
|
|
msg->complete(msg->context);
|
|
}
|
|
|
|
static void gsc3280_spi_xfer_done(struct gsc3280_spi *gscs)
|
|
{
|
|
/* Update total byte transferred return count actual bytes read */
|
|
gscs->cur_msg->actual_length += gscs->len;
|
|
/* Move to next transfer */
|
|
gscs->cur_msg->state = gsc3280_spi_next_transfer(gscs);
|
|
if (gscs->cur_msg->state == DONE_STATE) {
|
|
/* Handle end of message */
|
|
gscs->cur_msg->status = 0;
|
|
gsc3280_spi_giveback(gscs);
|
|
} else {
|
|
tasklet_schedule(&gscs->pump_transfers);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
#if 0
|
|
static struct gsc3280_spi_dma_platform_data spi_platform_data = {
|
|
.dmas_tx.dma_dev = &(gsc3280_dmac_device.dev),
|
|
.dmas_rx.dma_dev = &(gsc3280_dmac_device.dev),
|
|
};
|
|
#endif
|
|
/* Note: first step is the protocol driver prepares a dma-capable memory, and this func just need translate
|
|
the virt addr to physical */
|
|
static int map_dma_buffers(struct gsc3280_spi *gscs)
|
|
{
|
|
if (!gscs->cur_msg->is_dma_mapped || !gscs->dma_inited || !gscs->cur_chip->enable_dma
|
|
|| !gscs->dma_ops) {
|
|
return 0;
|
|
}
|
|
if (gscs->cur_transfer->tx_dma)
|
|
gscs->tx_dma = gscs->cur_transfer->tx_dma;
|
|
if (gscs->cur_transfer->rx_dma)
|
|
gscs->rx_dma = gscs->cur_transfer->rx_dma;
|
|
return 1;
|
|
}
|
|
|
|
static bool gsc3280_spi_dma_chan_filter(struct dma_chan *chan, void *param)
|
|
{
|
|
struct gsc3280_dma_slave *gscs = param;
|
|
return (gscs->dma_dev == chan->device->dev);
|
|
}
|
|
|
|
static int gscs_dma_init(struct gsc3280_spi *gscs)
|
|
{
|
|
struct gsc3280_spi_dma_platform_data *hsd = gscs->dma_priv;
|
|
//struct gsc_dma_slave *rxs, *txs;
|
|
struct gsc3280_dma_slave *rxs, *txs;
|
|
dma_cap_mask_t mask;
|
|
|
|
dma_cap_zero(mask);
|
|
dma_cap_set(DMA_SLAVE, mask);
|
|
/* 1. Init rx channel */
|
|
rxs = &hsd->dmas_rx;
|
|
rxs->rx_reg = gscs->dma_addr;
|
|
rxs->reg_width = 0;
|
|
rxs->cfg_hi = 0x584;
|
|
rxs->cfg_lo = 0x640;
|
|
rxs->dst_msize = 0;
|
|
rxs->src_msize = 0;
|
|
rxs->dst_master = 1;
|
|
rxs->src_master = 0;
|
|
rxs->fc = 2;
|
|
gscs->rxchan = dma_request_channel(mask, gsc3280_spi_dma_chan_filter, rxs);
|
|
if (!gscs->rxchan)
|
|
goto err_exit;
|
|
gscs->rxchan->private = rxs;
|
|
/* 2. Init tx channel */
|
|
txs = &hsd->dmas_tx;
|
|
txs->tx_reg = gscs->dma_addr;
|
|
txs->reg_width = 0;
|
|
txs->cfg_hi = 0x5004;
|
|
txs->cfg_lo = 0xa20;
|
|
txs->src_msize = 0;
|
|
txs->dst_msize = 0;
|
|
txs->dst_master = 0;
|
|
txs->src_master = 1;
|
|
txs->fc = 1;
|
|
gscs->txchan = dma_request_channel(mask, gsc3280_spi_dma_chan_filter, txs);
|
|
if (!gscs->txchan)
|
|
goto free_rxchan;
|
|
gscs->txchan->private = txs;
|
|
gscs->dma_inited = 1;
|
|
return 0;
|
|
free_rxchan:
|
|
gsc3280_dma_single_free(gscs->rxchan);
|
|
err_exit:
|
|
return -1;
|
|
}
|
|
static void gscs_dma_exit(struct gsc3280_spi *gscs)
|
|
{
|
|
gsc3280_dma_single_free(gscs->txchan);
|
|
gsc3280_dma_single_free(gscs->rxchan);
|
|
}
|
|
|
|
static void gsc3280_spi_dma_done(void *arg)
|
|
{
|
|
struct gsc3280_spi *gscs = arg;
|
|
|
|
if (++gscs->dma_chan_done != 2)
|
|
return;
|
|
if (gscs->dma_mapped)
|
|
gscs->dma_ops->dma_exit(gscs);
|
|
gsc3280_spi_xfer_done(gscs);
|
|
}
|
|
|
|
static int gscs_dma_transfer(struct gsc3280_spi *gscs, int cs_change)
|
|
{
|
|
//struct he_cyclic_desc *txdesc = NULL,*rxdesc = NULL;
|
|
struct gsc3280_cyclic_desc *txdesc = NULL,*rxdesc = NULL;
|
|
struct dma_chan *txchan, *rxchan;
|
|
u16 dma_ctrl = 0;
|
|
|
|
/* 1. setup DMA related registers */
|
|
// if (cs_change) {
|
|
gsc3280_spi_chip_sel(gscs, 0x1);
|
|
// gsc3280_enable_spi(gscs, 0);
|
|
if (gscs->tx_dma)
|
|
dma_ctrl |= 1<<7;
|
|
if (gscs->rx_dma)
|
|
dma_ctrl |= 1<<6;
|
|
dma_ctrl |= readw(gscs->regs + GSC_SPI_CTRL);
|
|
writew(dma_ctrl, gscs->regs + GSC_SPI_CTRL);
|
|
// gsc3280_enable_spi(gscs, 1);
|
|
gsc3280_spi_chip_sel(gscs, 0x0);
|
|
// }
|
|
gscs->dma_chan_done = 0;
|
|
txchan = gscs->txchan;
|
|
rxchan = gscs->rxchan;
|
|
/* 2. Prepare the TX dma transfer */
|
|
//txdesc = gsc_dma_single_prep(txchan, gscs->tx_dma, gscs->len, DMA_TO_DEVICE);
|
|
txdesc = gsc3280_dma_single_prep(txchan, gscs->tx_dma, gscs->len, DMA_TO_DEVICE);
|
|
txdesc->period_callback = &gsc3280_spi_dma_done;
|
|
txdesc->period_callback_param = gscs;
|
|
/* 3. Prepare the RX dma transfer */
|
|
//rxdesc = gsc_dma_single_prep(rxchan, gscs->rx_dma, gscs->len, DMA_FROM_DEVICE);
|
|
rxdesc = gsc3280_dma_single_prep(rxchan, gscs->rx_dma, gscs->len, DMA_FROM_DEVICE);
|
|
rxdesc->period_callback = &gsc3280_spi_dma_done;
|
|
rxdesc->period_callback_param = gscs;
|
|
/* 4. submit */
|
|
/* rx must be started before tx due to spi instinct */
|
|
//gsc_dma_single_start(rxchan);
|
|
gsc3280_dma_single_start(rxchan);
|
|
//gsc_dma_single_start(txchan);
|
|
gsc3280_dma_single_start(txchan);
|
|
return 0;
|
|
}
|
|
|
|
static struct gsc3280_spi_dma_ops gscs_dma_ops = {
|
|
.dma_init = gscs_dma_init,
|
|
.dma_exit = gscs_dma_exit,
|
|
.dma_transfer = gscs_dma_transfer,
|
|
};
|
|
|
|
#endif
|
|
|
|
/* Return the max entries we should read out of rx fifo */
|
|
static inline u32 gsc3280_spi_rx_max(struct gsc3280_spi *gscs)
|
|
{
|
|
u32 rx_left = (gscs->rx_end - gscs->rx) / gscs->n_bytes;
|
|
return min(rx_left, (u32)readw(gscs->regs + GSC_SPI_RXFLR));
|
|
}
|
|
/* Return the max entries we can fill into tx fifo */
|
|
static inline u32 gsc3280_spi_tx_max(struct gsc3280_spi *gscs)
|
|
{
|
|
u32 tx_left,tx_room,rxtx_gap;
|
|
|
|
tx_left = (gscs->tx_end - gscs->tx) / gscs->n_bytes;
|
|
tx_room = gscs->fifo_len - readw(gscs->regs + GSC_SPI_TXFLR);
|
|
rxtx_gap = ((gscs->rx_end - gscs->rx) - (gscs->tx_end - gscs->tx))
|
|
/ gscs->n_bytes;
|
|
return min3(tx_left, tx_room,(u32)(gscs->fifo_len - rxtx_gap));
|
|
}
|
|
|
|
static void gsc3280_reader(struct gsc3280_spi *gscs)
|
|
{
|
|
u32 max = gsc3280_spi_rx_max(gscs);
|
|
u16 rxw;
|
|
|
|
while (max--) {
|
|
rxw = readl(gscs->regs + GSC_SPI_DA_S);
|
|
DBG("rxw = 0x%x\n\n", rxw);
|
|
/* Care rx only if the transfer's original "rx" is not null */
|
|
if (gscs->rx_end - gscs->len) {
|
|
if (gscs->n_bytes == 1)
|
|
*(u8 *)(gscs->rx) = rxw;
|
|
else
|
|
*(u16 *)(gscs->rx) = rxw;
|
|
}
|
|
gscs->rx += gscs->n_bytes;
|
|
}
|
|
}
|
|
|
|
static void gsc3280_writer(struct gsc3280_spi *gscs)
|
|
{
|
|
u32 max = gsc3280_spi_tx_max(gscs);
|
|
u16 txw = 0;
|
|
|
|
while (max--) {
|
|
/* Set the tx word if the transfer's original "tx" is not null */
|
|
if (gscs->tx_end - gscs->len) {
|
|
if (gscs->n_bytes == 1)
|
|
txw = *(u8 *)(gscs->tx);
|
|
else
|
|
txw = *(u16 *)(gscs->tx);
|
|
}
|
|
if(gscs->irq == 14)
|
|
udelay(1);
|
|
DBG("txw = 0x%x\n\n", txw);
|
|
writel(txw, gscs->regs + GSC_SPI_DA_S);
|
|
gscs->tx += gscs->n_bytes;
|
|
}
|
|
}
|
|
|
|
/* Must be called inside pump_transfers() */
|
|
static void gsc3280_spi_poll_transfer(struct gsc3280_spi *gscs)
|
|
{
|
|
do {
|
|
gsc3280_writer(gscs);
|
|
gsc3280_reader(gscs);
|
|
cpu_relax();
|
|
} while (gscs->rx_end > gscs->rx);
|
|
gsc3280_spi_xfer_done(gscs);
|
|
}
|
|
|
|
static void int_error_stop(struct gsc3280_spi *gscs, const char *msg)
|
|
{
|
|
/* Stop the hw */
|
|
gsc3280_enable_spi(gscs, GSC_SPI_DISABLE);
|
|
gsc3280_spi_clr_fifo(gscs);
|
|
dev_err(&gscs->master->dev, "%s\n", msg);
|
|
gscs->cur_msg->state = ERROR_STATE;
|
|
tasklet_schedule(&gscs->pump_transfers);
|
|
}
|
|
|
|
static irqreturn_t interrupt_transfer(struct gsc3280_spi *gscs)
|
|
{
|
|
u16 irq_status ;
|
|
if(gscs->irq == 14)
|
|
irq_status = readw(gscs->regs + GSC_SPI_IMSR);
|
|
else
|
|
irq_status = readw(gscs->regs + GSC_SPI_ISR);
|
|
/* Error handling */
|
|
if (irq_status & (SPI_INT_TX_H_OVER | SPI_INT_RX_L_OVER | SPI_INT_RX_H_OVER)){
|
|
printk(KERN_INFO "irq_status=0x%x\n",irq_status);
|
|
writew(0xe, gscs->regs + GSC_SPI_ISR);
|
|
int_error_stop(gscs, "interrupt_transfer: fifo overrun/underrun");
|
|
return IRQ_HANDLED;
|
|
}
|
|
gsc3280_reader(gscs);
|
|
if (gscs->rx_end == gscs->rx) {
|
|
gsc3280_spi_mask_intr(gscs, SPI_INT_TX_EMPTY);
|
|
gsc3280_spi_xfer_done(gscs);
|
|
return IRQ_HANDLED;
|
|
}
|
|
if (irq_status & SPI_INT_TX_EMPTY) {
|
|
gsc3280_spi_mask_intr(gscs, SPI_INT_TX_EMPTY);
|
|
gsc3280_writer(gscs);
|
|
/* Enable TX irq always, it will be disabled when RX finished */
|
|
gsc3280_spi_umask_intr(gscs, SPI_INT_TX_EMPTY);
|
|
}
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
/* this is transfer message function */
|
|
static irqreturn_t gsc3280_spi_irq(int irq, void *dev_id)
|
|
{
|
|
struct gsc3280_spi *gscs = dev_id;
|
|
u16 irq_status;
|
|
if(irq == 14)
|
|
irq_status = readw(gscs->regs + GSC_SPI_IMSR);
|
|
else
|
|
irq_status = readw(gscs->regs + GSC_SPI_ISR);
|
|
|
|
if (!irq_status)
|
|
return IRQ_NONE;
|
|
if (!gscs->cur_msg) {
|
|
gsc3280_spi_mask_intr(gscs, SPI_INT_TX_EMPTY);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
return gscs->transfer_handler(gscs);
|
|
}
|
|
|
|
/* when call this function,the cur_msg is the new msg */
|
|
static void gsc3280_spi_pump_transfers(unsigned long data)
|
|
{
|
|
struct gsc3280_spi *gscs = (struct gsc3280_spi *)data;
|
|
int clk_div = 0;
|
|
u32 imask = 0, cr = 0;
|
|
struct spi_message *message = NULL;
|
|
struct spi_transfer *transfer = NULL;
|
|
struct spi_transfer *previous = NULL;
|
|
struct spi_device *spi = NULL;
|
|
struct chip_data *chip = NULL;
|
|
|
|
/* Get current state information */
|
|
message = gscs->cur_msg;
|
|
transfer = gscs->cur_transfer;
|
|
chip = gscs->cur_chip;
|
|
spi = message->spi;
|
|
|
|
DBG("gsc3280_spi_pump_transfers\n");
|
|
if (message->state == ERROR_STATE) {
|
|
DBG("!!!!pump_transfers:cur msg state error!\n");
|
|
message->status = -EIO;
|
|
goto early_exit;
|
|
}
|
|
/* Handle end of message */
|
|
if (message->state == DONE_STATE) {
|
|
message->status = 0;
|
|
goto early_exit;
|
|
}
|
|
/* Delay if requested at end of transfer*/
|
|
if (message->state == RUNNING_STATE) {
|
|
previous = list_entry(transfer->transfer_list.prev, struct spi_transfer, transfer_list);
|
|
if (previous->delay_usecs)
|
|
udelay(previous->delay_usecs);
|
|
}
|
|
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
gscs->dma_width = chip->dma_width;
|
|
gscs->rx_dma = transfer->rx_dma;
|
|
gscs->tx_dma = transfer->tx_dma;
|
|
#endif
|
|
|
|
gscs->tx = (void *)transfer->tx_buf;
|
|
gscs->tx_end = gscs->tx + transfer->len;
|
|
gscs->rx = transfer->rx_buf;
|
|
gscs->rx_end = gscs->rx + transfer->len;
|
|
gscs->cs_change = transfer->cs_change;
|
|
gscs->len = transfer->len;
|
|
|
|
/* Handle per transfer options for bpw and speed */
|
|
if (transfer->speed_hz) {
|
|
if (transfer->speed_hz != chip->speed_hz) {
|
|
if (transfer->speed_hz > (gscs->max_freq/2)) {
|
|
printk(KERN_ERR "SPI: unsupported freq: %dHz\n", transfer->speed_hz);
|
|
message->status = -EIO;
|
|
return;
|
|
} else
|
|
chip->speed_hz = transfer->speed_hz;
|
|
}
|
|
|
|
clk_div = gscs->max_freq / chip->speed_hz;
|
|
if((gscs->max_freq%chip->speed_hz)>0)
|
|
clk_div +=1;
|
|
if(clk_div%2)
|
|
clk_div = clk_div/2;
|
|
else
|
|
clk_div = (clk_div/2) -1;
|
|
chip->clk_div = (u16)clk_div;
|
|
writel(chip->clk_div, gscs->regs + GSC_SPI_SEABAUR);
|
|
}
|
|
if (transfer->bits_per_word) {
|
|
switch (transfer->bits_per_word) {
|
|
case 8:
|
|
case 16:
|
|
gscs->n_bytes = transfer->bits_per_word >> 3;
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
gscs->dma_width = gscs->n_bytes;
|
|
#endif
|
|
break;
|
|
default:
|
|
printk(KERN_ERR "SPI: unsupported bits:" "%db\n", transfer->bits_per_word);
|
|
message->status = -EIO;
|
|
return;
|
|
}
|
|
}
|
|
|
|
message->state = RUNNING_STATE;
|
|
|
|
/* Interrupt mode we only need set the TXEI IRQ, as TX/RX always happen syncronizely */
|
|
if (!chip->poll_mode) {
|
|
gscs->transfer_handler = interrupt_transfer;
|
|
imask |= SPI_INT_TX_H_OVER | SPI_INT_RX_L_OVER | SPI_INT_RX_H_OVER | SPI_INT_RX_FULL ;
|
|
|
|
if (gscs->tx != NULL) {
|
|
imask |= SPI_INT_TX_EMPTY;
|
|
}
|
|
gsc3280_spi_umask_intr(gscs, imask);
|
|
}
|
|
cr = chip->cr | GSC_SPI_CTL_EN;
|
|
writel(cr, gscs->regs + GSC_SPI_CTRL); /* enable spi */
|
|
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
/* Check if current transfer is a DMA transaction */
|
|
gscs->dma_mapped = map_dma_buffers(gscs);
|
|
if (gscs->dma_mapped)
|
|
//gscs->dma_ops->dma_transfer(gscs, cs_change);
|
|
gscs->dma_ops->dma_transfer(gscs, gscs->cs_change);
|
|
#endif
|
|
if (chip->poll_mode)
|
|
gsc3280_spi_poll_transfer(gscs);
|
|
|
|
return;
|
|
|
|
early_exit:
|
|
gsc3280_spi_giveback(gscs);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* when call this function, no msg transfering
|
|
* deal one msg when call this funciton once.
|
|
*
|
|
*/
|
|
static void gsc3280_spi_pump_messages(struct work_struct *work)
|
|
{
|
|
unsigned long flags = 0;
|
|
int i;
|
|
struct gsc3280_spi *gscs = container_of(work, struct gsc3280_spi, pump_messages);
|
|
|
|
DBG("####gsc3280_spi_pump_messages####\n");
|
|
spin_lock_irqsave(&gscs->slock, flags);
|
|
if (list_empty(&gscs->queue) || (gscs->queue_state == GSC_SPI_QUEUE_STOP)) {
|
|
DBG("msg is finished!\n");
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
gscs->busy = 0;
|
|
return;
|
|
}
|
|
/* Make sure we are not already running a message */
|
|
if (gscs->cur_msg) {
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
return;
|
|
}
|
|
gscs->cur_msg = list_entry(gscs->queue.next, struct spi_message, queue);
|
|
if (!gscs->cur_msg) {
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
DBG("!!!!gsc3280_spi_pump_messages: current no msg!\n");
|
|
return;
|
|
}
|
|
list_del_init(&gscs->cur_msg->queue);
|
|
gscs->cur_msg->state = START_STATE;
|
|
gscs->cur_chip = spi_get_ctldata(gscs->cur_msg->spi);
|
|
gscs->n_bytes = gscs->cur_chip->n_bytes;
|
|
if (gscs->cur_chip->cs_type == 0) //chip cs
|
|
writel(0, gscs->regs + GSC_SPI_CS);
|
|
else{ //io cs
|
|
writel(1, gscs->regs + GSC_SPI_CS);
|
|
for(i=0;i<gscs->cur_chip->pin_cnt;i++)
|
|
gpio_set_value(gscs->cur_chip->pin_cs[i],((gscs->cur_chip->cs_value>>(i+1))&0x1));
|
|
}
|
|
|
|
/* get first transfer */
|
|
gscs->cur_transfer = list_entry(gscs->cur_msg->transfers.next, struct spi_transfer, transfer_list);
|
|
if (!gscs->cur_transfer) {
|
|
DBG("!!!!gsc3280_spi_pump_transfers: current no transfer!\n");
|
|
return;
|
|
}
|
|
tasklet_schedule(&gscs->pump_transfers);
|
|
gscs->busy = 1;
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
return;
|
|
}
|
|
|
|
/* spi driver call this function transfer data */
|
|
static int gsc3280_spi_transfer(struct spi_device *spi, struct spi_message *msg)
|
|
{
|
|
unsigned long flags = 0;
|
|
struct gsc3280_spi *gscs = spi_master_get_devdata(spi->master);
|
|
|
|
spin_lock_irqsave(&gscs->slock, flags);
|
|
if (gscs->queue_state == GSC_SPI_QUEUE_STOP) {
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
return -ESHUTDOWN;
|
|
}
|
|
msg->actual_length = 0;
|
|
msg->status = -EINPROGRESS;
|
|
msg->state = START_STATE;
|
|
|
|
list_add_tail(&msg->queue, &gscs->queue);
|
|
|
|
if (gscs->queue_state == GSC_SPI_QUEUE_RUN && !gscs->busy) {
|
|
DBG("####gsc3280 spi transfer start####\n");
|
|
|
|
if (gscs->cur_transfer || gscs->cur_msg) {
|
|
queue_work(gscs->workqueue,
|
|
&gscs->pump_messages);
|
|
printk(KERN_CRIT "queue message\n");
|
|
}
|
|
else {
|
|
/* If no other data transaction in air, just go */
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
gsc3280_spi_pump_messages(&gscs->pump_messages);
|
|
// printk(KERN_CRIT "transfer ok.\n");
|
|
return 0;
|
|
}
|
|
}
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
DBG("####gsc3280 spi transfer success####\n");
|
|
return 0;
|
|
}
|
|
|
|
/* This may be called twice for each spi dev */
|
|
static int gsc3280_spi_setup(struct spi_device *spi)
|
|
{
|
|
int i,clk_div,ret = 0;
|
|
struct chip_data *chip = NULL;
|
|
struct gsc3280_spi_info *chip_info = NULL;
|
|
struct gsc3280_spi *gscs = spi_master_get_devdata(spi->master);
|
|
|
|
DBG("######gsc3280 spi bus setup start######\n");
|
|
chip = spi_get_ctldata(spi); /* Only alloc on first setup */
|
|
if (!chip) {
|
|
chip = kzalloc(sizeof(struct chip_data), GFP_KERNEL);
|
|
if (!chip) {
|
|
DBG("!!!!kzalloc error!\n");
|
|
ret = -ENOMEM;
|
|
goto exit;
|
|
}
|
|
}
|
|
chip_info = spi->controller_data;
|
|
/* chip_info doesn't always exist */
|
|
if (chip_info) {
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
chip->enable_dma = chip_info->enable_dma;
|
|
#endif
|
|
chip->poll_mode = chip_info->poll_mode;
|
|
chip->cs_type = chip_info->cs_type;
|
|
chip->cs_value = chip_info->cs_value;
|
|
chip->bits_per_word = chip_info->bits_per_word;
|
|
chip->lsb_flg = chip_info->lsb_flg;
|
|
chip->pin_cnt = chip_info->pin_cnt;
|
|
//init cancel cs
|
|
if (chip->cs_type == 0) //chip cs
|
|
writel(1, gscs->regs + GSC_SPI_CS);
|
|
else{ //io cs
|
|
for(i=0;i<chip->pin_cnt;i++){
|
|
chip->pin_cs[i]=chip_info->pin_cs[i];
|
|
if(gpio_request(chip->pin_cs[i], spi->modalias)==0)
|
|
gpio_direction_output(chip->pin_cs[i],~((chip->cs_value>>(i+1))&0x1));
|
|
}
|
|
}
|
|
}
|
|
if (spi->bits_per_word == 8) {
|
|
chip->n_bytes = 1;
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
chip->dma_width = 1;
|
|
#endif
|
|
} else if (spi->bits_per_word == 16) {
|
|
chip->n_bytes = 2;
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
chip->dma_width = 2;
|
|
#endif
|
|
} else {
|
|
DBG("!!!!spi->bits_per_word = %d error!\n", spi->bits_per_word);
|
|
ret = -EINVAL;
|
|
goto exit;
|
|
}
|
|
|
|
if(spi->max_speed_hz > gscs->max_freq/2)
|
|
chip->speed_hz = gscs->max_freq/2;
|
|
else
|
|
chip->speed_hz = spi->max_speed_hz;
|
|
|
|
clk_div = gscs->max_freq / chip->speed_hz;
|
|
// if((gscs->max_freq%chip->speed_hz)>0)
|
|
// clk_div +=1;
|
|
if(clk_div%2)
|
|
clk_div = clk_div/2;
|
|
else
|
|
clk_div = (clk_div/2) -1;
|
|
|
|
writel(clk_div , gscs->regs + GSC_SPI_SEABAUR);
|
|
printk(KERN_INFO "SPI: max freq = %dHz\n", gscs->max_freq);
|
|
printk(KERN_INFO "SPI: max speed = %dHz\n", spi->max_speed_hz);
|
|
printk(KERN_INFO "SPI: rel speed = %dHz\n", gscs->max_freq/((clk_div+1)*2));
|
|
|
|
chip->cr = (chip->lsb_flg << GSC_SPI_CTL_LSB) | (spi->mode << GSC_SPI_CTL_MOD)
|
|
| ((chip->bits_per_word - 1) << GSC_SPI_CTL_DSS);
|
|
spi_set_ctldata(spi, chip);
|
|
|
|
exit:
|
|
if (ret != 0)
|
|
DBG("!!!!gsc3280 spi bus setup error!\n");
|
|
else
|
|
DBG("######gsc3280 spi bus setup success######\n");
|
|
return ret;
|
|
}
|
|
|
|
static void gsc3280_spi_cleanup(struct spi_device *spi)
|
|
{
|
|
struct chip_data *chip = spi_get_ctldata(spi);
|
|
|
|
kfree(chip);
|
|
}
|
|
|
|
static int __devinit gsc3280_init_queue(struct gsc3280_spi *gscs)
|
|
{
|
|
INIT_LIST_HEAD(&gscs->queue);
|
|
spin_lock_init(&gscs->slock);
|
|
gscs->queue_state = GSC_SPI_QUEUE_STOP;
|
|
gscs->busy = 0;
|
|
tasklet_init(&gscs->pump_transfers, gsc3280_spi_pump_transfers, (unsigned long)gscs);
|
|
INIT_WORK(&gscs->pump_messages, gsc3280_spi_pump_messages);
|
|
gscs->workqueue = create_singlethread_workqueue(dev_name(gscs->master->dev.parent));
|
|
if (gscs->workqueue == NULL) {
|
|
return -EBUSY;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int gsc3280_start_queue(struct gsc3280_spi *gscs)
|
|
{
|
|
unsigned long flags = 0;
|
|
|
|
spin_lock_irqsave(&gscs->slock, flags);
|
|
if ((gscs->queue_state == GSC_SPI_QUEUE_RUN) || gscs->busy) {
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
return -EBUSY;
|
|
}
|
|
gscs->queue_state = GSC_SPI_QUEUE_RUN;
|
|
gscs->cur_msg = NULL;
|
|
gscs->cur_transfer = NULL;
|
|
gscs->cur_chip = NULL;
|
|
gscs->prev_chip = NULL;
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
queue_work(gscs->workqueue, &gscs->pump_messages);
|
|
return 0;
|
|
}
|
|
|
|
static int gsc3280_spi_stop_queue(struct gsc3280_spi *gscs)
|
|
{
|
|
int ret = 0;
|
|
unsigned long flags;
|
|
unsigned limit = 50;
|
|
|
|
spin_lock_irqsave(&gscs->slock, flags);
|
|
while ((!list_empty(&gscs->queue) || gscs->busy) && limit--) {
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
msleep(10);
|
|
spin_lock_irqsave(&gscs->slock, flags);
|
|
}
|
|
if (!list_empty(&gscs->queue) || gscs->busy)
|
|
ret = -EBUSY;
|
|
else
|
|
gscs->queue_state = GSC_SPI_QUEUE_STOP;
|
|
spin_unlock_irqrestore(&gscs->slock, flags);
|
|
return ret;
|
|
}
|
|
|
|
static int gsc3280_spi_destroy_queue(struct gsc3280_spi *gscs)
|
|
{
|
|
int ret = 0;
|
|
|
|
ret = gsc3280_spi_stop_queue(gscs);
|
|
if (ret != 0)
|
|
return ret;
|
|
destroy_workqueue(gscs->workqueue);
|
|
return ret;
|
|
}
|
|
|
|
/* Restart the controller, disable all interrupts, clean fifo */
|
|
static void gsc3280_spi_hw_init(struct gsc3280_spi *gscs)
|
|
{
|
|
gsc3280_enable_spi(gscs, GSC_SPI_DISABLE);
|
|
gsc3280_spi_mask_intr(gscs, GSC_SPI_SR_MASK);
|
|
if (!gscs->fifo_len) {
|
|
if(gscs->irq == 14) //spi0
|
|
gscs->fifo_len = 0x8;
|
|
else //spi1
|
|
gscs->fifo_len = 0x10;
|
|
}
|
|
writew(0x00, gscs->regs + GSC_SPI_TXFTLR);
|
|
writew(0x00, gscs->regs + GSC_SPI_RXFTLR);
|
|
gsc3280_spi_clr_fifo(gscs);
|
|
gsc3280_enable_spi(gscs, GSC_SPI_ENABLE);
|
|
}
|
|
|
|
static int __init gsc3280_spi_probe(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct gsc3280_spi *gscs;
|
|
struct spi_master *master;
|
|
struct resource *mem, *ioarea;
|
|
|
|
/*When used high speed SPI, then must reconfig the SPI clock */
|
|
/*SPI0 ReInit*/
|
|
__raw_writel(0x1,(__iomem void *)GSC3280_REGADDR_SYSCTL_CLKDIV_SPI0);
|
|
/*SPI1 ReInit*/
|
|
__raw_writel(0x1,(__iomem void *)GSC3280_REGADDR_SYSCTL_CLKDIV_SPI1);
|
|
|
|
DBG("############\n");
|
|
DBG("gsc3280 spi probe start\n");
|
|
master = spi_alloc_master(&pdev->dev, sizeof(struct gsc3280_spi));
|
|
if (!master) {
|
|
ret = -ENOMEM;
|
|
DBG("!!!!spi_alloc_master error\n");
|
|
goto exit;
|
|
}
|
|
gscs = spi_master_get_devdata(master);
|
|
memset(gscs, 0, sizeof(struct gsc3280_spi));
|
|
gscs->master = spi_master_get(master);
|
|
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
if (!mem) {
|
|
DBG("!!!!no mem resource!\n");
|
|
ret = -EINVAL;
|
|
goto err_kfree;
|
|
}
|
|
ioarea = request_mem_region(mem->start, resource_size(mem), pdev->name);
|
|
if (!ioarea) {
|
|
DBG("!!!!SPI region already claimed!\n");
|
|
ret = -EBUSY;
|
|
goto err_kfree;
|
|
}
|
|
gscs->regs = ioremap_nocache(mem->start, resource_size(mem));
|
|
if (!gscs->regs) {
|
|
DBG("!!!!SPI ioremap error!\n");
|
|
ret = -ENOMEM;
|
|
goto err_release_reg;
|
|
}
|
|
DBG("gscs->regs = 0x%p\n", gscs->regs);
|
|
gscs->irq = platform_get_irq(pdev, 0);
|
|
if (gscs->irq < 0) {
|
|
DBG("!!!!no irq resource!\n");
|
|
ret = gscs->irq;
|
|
goto err_unmap;
|
|
}
|
|
ret = request_irq(gscs->irq, gsc3280_spi_irq, IRQF_DISABLED, dev_name(&pdev->dev), gscs);
|
|
if (ret < 0) {
|
|
DBG("!!!!can not get IRQ!\n");
|
|
goto err_irq;
|
|
}
|
|
gscs->clk = clk_get(NULL, pdev->dev.platform_data);
|
|
if (IS_ERR(gscs->clk)) {
|
|
DBG("!!!!failed to find spi1 clock source!\n");
|
|
ret = PTR_ERR(gscs->clk);
|
|
goto err_irq;
|
|
}
|
|
gscs->max_freq = clk_get_rate(gscs->clk);
|
|
clk_enable(gscs->clk);
|
|
gscs->bus_num = pdev->id;
|
|
gscs->num_cs = 4;
|
|
gscs->prev_chip = NULL;
|
|
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
// gscs->dma_priv = pdev->dev.platform_data = &spi_platform_data;
|
|
gscs->dma_priv = pdev->dev.platform_data;
|
|
pdata = pdev->dev.platform_data;
|
|
|
|
if (!gscs->dma_priv)
|
|
goto err_clk;
|
|
gscs->dma_ops = &gscs_dma_ops;
|
|
gscs->dma_inited = 0;
|
|
gscs->dma_addr = (dma_addr_t)(gscs->regs + 0x24) & 0x1fffffff;
|
|
#endif
|
|
platform_set_drvdata(pdev, master);
|
|
master->mode_bits = SPI_CPOL | SPI_CPHA |SPI_CS_HIGH;
|
|
master->bus_num = gscs->bus_num;
|
|
master->num_chipselect = gscs->num_cs;
|
|
master->cleanup = gsc3280_spi_cleanup;
|
|
master->setup = gsc3280_spi_setup;
|
|
master->transfer = gsc3280_spi_transfer;
|
|
gsc3280_spi_hw_init(gscs);
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
if (gscs->dma_ops && gscs->dma_ops->dma_init) {
|
|
ret = gscs->dma_ops->dma_init(gscs);
|
|
if (ret) {
|
|
dev_warn(&master->dev, "DMA init failed\n");
|
|
gscs->dma_inited = 0;
|
|
}
|
|
}
|
|
#endif
|
|
ret = gsc3280_init_queue(gscs);
|
|
if (ret != 0) {
|
|
DBG("!!!!problem initializing queue!\n");
|
|
goto err_diable_hw;
|
|
}
|
|
ret = gsc3280_start_queue(gscs);
|
|
if (ret != 0) {
|
|
DBG("!!!!problem starting queue!\n");
|
|
goto err_queue_alloc;
|
|
}
|
|
ret = spi_register_master(master);
|
|
if (ret != 0) {
|
|
DBG("!!!!register spi master error!\n");
|
|
goto err_queue_alloc;
|
|
}
|
|
|
|
DBG("gsc3280 spi probe success\n");
|
|
DBG("############\n");
|
|
return 0;
|
|
|
|
//err_free_master:
|
|
//spi_master_put(master);
|
|
err_queue_alloc:
|
|
gsc3280_spi_destroy_queue(gscs);
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
if (gscs->dma_ops && gscs->dma_ops->dma_exit)
|
|
gscs->dma_ops->dma_exit(gscs);
|
|
#endif
|
|
err_diable_hw:
|
|
gsc3280_enable_spi(gscs, GSC_SPI_DISABLE);
|
|
err_clk:
|
|
clk_disable(gscs->clk);
|
|
clk_put(gscs->clk);
|
|
err_irq:
|
|
free_irq(gscs->irq, gscs);
|
|
err_unmap:
|
|
iounmap(gscs->regs);
|
|
err_release_reg:
|
|
release_mem_region(mem->start, resource_size(mem));
|
|
err_kfree:
|
|
kfree(gscs);
|
|
kfree(master);
|
|
exit:
|
|
printk(KERN_ERR "!!!!!!gsc3280 probe error!!!!!!\n");
|
|
return ret;
|
|
}
|
|
|
|
void __exit gsc3280_spi_remove(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct spi_master *master = platform_get_drvdata(pdev);
|
|
struct gsc3280_spi *gscs = spi_master_get_devdata(master);
|
|
|
|
if (!gscs)
|
|
return;
|
|
ret = gsc3280_spi_destroy_queue(gscs);
|
|
if (ret != 0)
|
|
dev_err(&gscs->master->dev, "gsc3280_spi_remove: workqueue will not "
|
|
"complete, message memory not freed\n");
|
|
|
|
#ifdef CONFIG_GSC3280_SPI_DMA
|
|
if (gscs->dma_ops && gscs->dma_ops->dma_exit)
|
|
gscs->dma_ops->dma_exit(gscs);
|
|
#endif
|
|
|
|
gsc3280_enable_spi(gscs, GSC_SPI_DISABLE);
|
|
free_irq(gscs->irq, gscs);
|
|
iounmap(gscs->regs);
|
|
clk_disable(gscs->clk);
|
|
clk_put(gscs->clk);
|
|
gscs->clk = NULL;
|
|
spi_unregister_master(gscs->master);
|
|
}
|
|
|
|
static int gsc3280_spi_suspend(struct platform_device *pdev, pm_message_t mesg)
|
|
{
|
|
int ret = 0;
|
|
struct spi_master *master = platform_get_drvdata(pdev);
|
|
struct gsc3280_spi *gscs = spi_master_get_devdata(master);
|
|
|
|
ret = gsc3280_spi_stop_queue(gscs);
|
|
if (ret != 0)
|
|
return ret;
|
|
gsc3280_enable_spi(gscs, GSC_SPI_DISABLE);
|
|
return ret;
|
|
}
|
|
|
|
static int gsc3280_spi_resume(struct platform_device *pdev)
|
|
{
|
|
int ret = 0;
|
|
struct spi_master *master = platform_get_drvdata(pdev);
|
|
struct gsc3280_spi *gscs = spi_master_get_devdata(master);
|
|
|
|
gsc3280_spi_hw_init(gscs);
|
|
ret = gsc3280_start_queue(gscs);
|
|
if (ret != 0)
|
|
dev_err(&gscs->master->dev, "fail to start queue (%d)\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
static struct platform_driver gsc3280_spi_driver = {
|
|
.driver = {
|
|
.name = "gsc3280-spi",
|
|
.owner = THIS_MODULE,
|
|
},
|
|
.suspend = gsc3280_spi_suspend,
|
|
.resume = gsc3280_spi_resume,
|
|
.remove = __exit_p(gsc3280_spi_remove),
|
|
};
|
|
|
|
static int __init gsc3280_spi_init(void)
|
|
{
|
|
platform_driver_probe(&gsc3280_spi_driver, gsc3280_spi_probe);
|
|
return 0;
|
|
}
|
|
static void __exit gsc3280_spi_exit(void)
|
|
{
|
|
platform_driver_unregister(&gsc3280_spi_driver);
|
|
}
|
|
module_init(gsc3280_spi_init);
|
|
module_exit(gsc3280_spi_exit);
|
|
|
|
MODULE_DESCRIPTION("BLX GSC3280 SoC SPI Controller driver");
|
|
MODULE_AUTHOR("Davied<apple_guet@126.com>");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:gsc3280-spi");
|
|
|