diff -drupN a/drivers/spi/ingenic_spi.c b/drivers/spi/ingenic_spi.c --- a/drivers/spi/ingenic_spi.c 1970-01-01 03:00:00.000000000 +0300 +++ b/drivers/spi/ingenic_spi.c 2022-06-09 05:02:33.000000000 +0300 @@ -0,0 +1,1486 @@ +/* linux/drivers/spi/ingenic_spi.c + * + * SSI controller for SPI protocol,use FIFO and DMA; + * base-to: linux/drivers/spi/spi_bitbang.c + * + * Copyright (c) 2010 Ingenic + * Author:Shumb + * + * 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. + * +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ingenic_spi.h" + +/* #define SSI_DEGUG */ +#ifdef SSI_DEGUG +#define print_dbg(format,arg...) \ + printk(format,## arg) +#else +#define print_dbg(format,arg...) +#endif + +#define INGENIC_SPI_RX_BUF(type) \ + u32 ingenic_spi_rx_buf_##type(struct ingenic_spi *ingspi) \ + { \ + u32 data = spi_readl(ingspi, SSI_DR); \ + type * rx = (type *)ingspi->rx; \ + *rx++ = (type)(data); \ + ingspi->rx = (u8 *)rx; \ + return (u32)data; \ + } + +#define INGENIC_SPI_TX_BUF(type) \ + u32 ingenic_spi_tx_buf_##type(struct ingenic_spi *ingspi) \ + { \ + u32 data; \ + const type * tx = (type *)ingspi->tx; \ + data = *tx++; \ + ingspi->tx = (u8 *)tx; \ + transmit_data(ingspi, data); \ + return (u32)data; \ + } + +INGENIC_SPI_RX_BUF(u8) +INGENIC_SPI_TX_BUF(u8) + +INGENIC_SPI_RX_BUF(u16) +INGENIC_SPI_TX_BUF(u16) + +INGENIC_SPI_RX_BUF(u32) +INGENIC_SPI_TX_BUF(u32) + +/* the spi->mode bits understood by this driver: */ +#define MODEBITS (SPI_3WIRE | SPI_CPOL | SPI_CPHA | SPI_CS_HIGH | SPI_LSB_FIRST | SPI_LOOP) +#define SPI_BITS_SUPPORT (SPI_BITS_8 | SPI_BITS_16 | SPI_BITS_32) + +static void ingenic_spi_cs(struct ingenic_spi_info *spi, u8 cs, unsigned int pol) +{ +#ifdef CONFIG_INGENIC_SPI_PIO_CE + u32 pin_value = *(spi->chipselect + cs); + gpio_direction_output(pin_value, !pol ? 0 : 1); +#endif +} + +static void ingenic_spi_chipsel(struct spi_device *spi, int value) +{ + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0; + + /*printk("%s[%d]: value = %d\n",__func__,__LINE__, value);*/ + switch (value) { + case BITBANG_CS_INACTIVE: + /* chip disable selected */ + if (ingspi->set_cs && ingspi->pdata) + ingspi->set_cs(ingspi->pdata, spi->chip_select, cspol^1); + break; + case BITBANG_CS_ACTIVE: + if (spi->mode & SPI_CPHA) + set_spi_clock_phase(ingspi, 1); + else + set_spi_clock_phase(ingspi, 0); + + if (spi->mode & SPI_CPOL) + set_spi_clock_polarity(ingspi, 1); + else + set_spi_clock_polarity(ingspi, 0); + + if (!(spi->mode & SPI_LSB_FIRST)) { + set_tx_msb(ingspi); + set_rx_msb(ingspi); + } else { + set_tx_lsb(ingspi); + set_rx_lsb(ingspi); + } + + if (spi->mode & SPI_LOOP) + enable_loopback(ingspi); + else + disable_loopback(ingspi); + + /* chip enable selected */ + if (ingspi->set_cs && ingspi->pdata) + ingspi->set_cs(ingspi->pdata, spi->chip_select, cspol); + break; + default: + break; + } +} + +static void ingenic_spi_clk_enable(struct ingenic_spi *ingspi) { + if(ingspi->clk_flag == 0) + return; + + clk_set_rate(ingspi->clk_cgu, ingspi->max_clk); + clk_prepare_enable(ingspi->clk_cgu); + clk_prepare_enable(ingspi->clk_gate); + ingspi->clk_flag = 0; +} + +static void ingenic_spi_clk_disable(struct ingenic_spi *ingspi) { + if(ingspi->clk_flag) + return; + + clk_disable_unprepare(ingspi->clk_cgu); + clk_disable_unprepare(ingspi->clk_gate); + ingspi->clk_flag = 1; +} + +static unsigned long ingenic_spi_clk_get_rate(struct spi_device *spi) +{ + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + unsigned long rate; + u16 cgv; + + spin_lock(&ingspi->lock); + rate = clk_get_rate(ingspi->clk_cgu); + cgv = spi_readl(ingspi, SSI_GR); + spin_unlock(&ingspi->lock); + return (rate / (2 * (cgv + 1))); +} +static int ingenic_spi_clk_set_rate(struct spi_device *spi, unsigned long rate) +{ + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + unsigned long cur_rate; + unsigned long src_rate; + int cgv; + + cur_rate = ingenic_spi_clk_get_rate(spi); + if(cur_rate == rate) + return 0; + spin_lock(&ingspi->lock); + src_rate = clk_get_rate(ingspi->clk_cgu); + cgv = (src_rate / (rate * 2)) - 1; + if(cgv < 0) { + printk("spi clk set %ld not support, src_rate = %ld\n", rate, src_rate); + return -1; + } + /*printk("%s[%d]: clk_cgu = %ld, cur_rate = %d, rate = %ld, cgv = %d\n",__func__,__LINE__,src_rate, cur_rate, rate, cgv);*/ + spi_writel(ingspi, SSI_GR, cgv); + spin_unlock(&ingspi->lock); + return 0; +} + +static void dma_tx_callback(void *data) +{ + struct ingenic_spi *ingspi = data; + + dma_unmap_sg(ingspi->txchan->device->dev, ingspi->sg_tx, 1, DMA_TO_DEVICE); + complete(&ingspi->done_tx_dma); +} + +static void dma_rx_callback(void *data) +{ + struct ingenic_spi *ingspi = data; + + dma_unmap_sg(ingspi->txchan->device->dev, ingspi->sg_tx, 1, DMA_TO_DEVICE); + dma_unmap_sg(ingspi->rxchan->device->dev, ingspi->sg_rx, 1, DMA_FROM_DEVICE); + complete(&ingspi->done_rx_dma); +} + +/*extern void jzdma_dump(struct dma_chan *chan);*/ +static int ingenic_spi_dma_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + int ret; + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + struct dma_slave_config rx_config, tx_config; + struct dma_async_tx_descriptor *rxdesc; + struct dma_async_tx_descriptor *txdesc; + struct dma_chan *rxchan = ingspi->rxchan; + struct dma_chan *txchan = ingspi->txchan; + struct ingenic_intr_cnt *g_ingenic_intr; + int dma_ds[] = {64, 32, 16, 4, 2, 1}; + int i; + +// printk("%s[%d]: \n",__func__,__LINE__); + /* Check that the channels are available */ + if (!txchan || !rxchan) { + dev_err(&spi->dev, "no dma channel\n"); + return -ENODEV; + } + + if (t->len % ingspi->transfer_unit_size) { + pr_err("The length of tranfer data is error\n"); + return -EFAULT; + } + + ingspi->rw_mode = 0; + if(t->tx_buf) + ingspi->rw_mode |= W_MODE; + if(t->rx_buf) + ingspi->rw_mode |= R_MODE; + + /* all transfer starts with tx, ends with rx. */ + if (ingspi->rw_mode & W_MODE) + ingspi->tx = t->tx_buf; + else + ingspi->tx = ingspi->buffer; + + if (ingspi->rw_mode & R_MODE) + ingspi->rx = t->rx_buf; + else + ingspi->rx = ingspi->buffer; + + memset(ingspi->buffer, 0, BUFFER_SIZE); + + switch (ingspi->transfer_unit_size) { + case SPI_8BITS: + tx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + tx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + rx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE; + tx_config.dst_maxburst = 1; + tx_config.src_maxburst = 1; + rx_config.src_maxburst = 1; + rx_config.dst_maxburst = 1; + break; + case SPI_16BITS: + tx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + tx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + rx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES; + tx_config.dst_maxburst = 2; + tx_config.src_maxburst = 2; + rx_config.src_maxburst = 2; + rx_config.dst_maxburst = 2; + break; + case SPI_32BITS: + tx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + tx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rx_config.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + rx_config.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + tx_config.dst_maxburst = 4; + tx_config.src_maxburst = 4; + rx_config.src_maxburst = 4; + rx_config.dst_maxburst = 4; + break; + } + + tx_config.dst_addr = (dma_addr_t)(ingspi->phys + SSI_DR); + rx_config.src_addr = (dma_addr_t)(ingspi->phys + SSI_DR); + + tx_config.direction = DMA_MEM_TO_DEV; + rx_config.direction = DMA_DEV_TO_MEM; + + dmaengine_slave_config(txchan, &tx_config); + dmaengine_slave_config(rxchan, &rx_config); + + /* set tx dma trigger */ + for (i = 0; i < ARRAY_SIZE(dma_ds); i++) { + if (t->len / dma_ds[i]) + break; + } + + if (i < ARRAY_SIZE(dma_ds)) { + ingspi->dma_tx_unit = dma_ds[i]; + } else { + print_dbg("DMA tx block_size force to defaut set!!!"); + ingspi->dma_tx_unit = INGENIC_SSI_DMA_BURST_LENGTH; + } + + ingspi->tx_trigger = ingspi->dma_tx_unit / (ingspi->txfifo_width >> 3); + //set_tx_trigger(ingspi, ingspi->tx_trigger); + set_tx_trigger(ingspi, 8); //The transfer is steady if the trigger number is used + print_dbg("t->len: %d, tx fifo width: %d, set tx trigger value to %d\n", t->len, ingspi->txfifo_width, ingspi->tx_trigger); + + sg_init_one(ingspi->sg_tx, ingspi->tx, t->len); + if (dma_map_sg(ingspi->txchan->device->dev, + ingspi->sg_tx, 1, DMA_TO_DEVICE) != 1) { + dev_err(&spi->dev, "dma_map_sg tx error\n"); + printk("%s LINE %d: %s\n", __func__, __LINE__, __FILE__); + goto err_tx_sgmap; + } + + txdesc = txchan->device->device_prep_slave_sg(txchan, + ingspi->sg_tx, + 1, + DMA_TO_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK,NULL); + if (!txdesc) { + dev_err(&spi->dev, "device_prep_slave_sg error\n"); + printk("%s LINE %d: %s\n", __func__, __LINE__, __FILE__); + goto err_txdesc; + } + + // config controller + disable_tx_intr(ingspi); + disable_rx_intr(ingspi); + + //revisit + disable_tx_error_intr(ingspi); + disable_rx_error_intr(ingspi); + + start_transmit(ingspi); + //finish_transmit(ingspi); + + flush_fifo(ingspi); + +// enable_receive(ingspi); + clear_errors(ingspi); + + g_ingenic_intr = ingspi->g_ingenic_intr; + memset(g_ingenic_intr, 0, sizeof *g_ingenic_intr); + + if (!(ingspi->rw_mode & R_MODE)) { + txdesc->callback = dma_tx_callback; + txdesc->callback_param = ingspi; + enable_tx_error_intr(ingspi); + + dmaengine_submit(txdesc); + dma_async_issue_pending(txchan); + + enable_receive(ingspi); + ret = wait_for_completion_interruptible_timeout(&ingspi->done_tx_dma, 60 * HZ); + if (ret <= 0) { + printk("The tx_dma umap wait timeout\n"); + goto err_txdesc; + } + ret = wait_for_completion_interruptible_timeout(&ingspi->done, 60 * HZ); + if (ret <= 0) { + printk("The spi transfer wait timeout\n"); + goto err_txdesc; + } + + if(t->cs_change) + finish_transmit(ingspi); + flush_rxfifo(ingspi); + clear_errors(ingspi); + + return t->len; + } + /* prepare spi dma rx */ + for (i = 0; i < ARRAY_SIZE(dma_ds); i++) { + if (!(t->len % dma_ds[i])) + break; + } + + if (i < ARRAY_SIZE(dma_ds)) { + ingspi->dma_rx_unit = dma_ds[i]; + } else { + print_dbg("DMA rx block_size force to defaut set!!!"); + ingspi->dma_rx_unit = INGENIC_SSI_DMA_BURST_LENGTH; + } + + ingspi->rx_trigger = ingspi->dma_rx_unit/(ingspi->rxfifo_width >> 3); + //set_rx_trigger(ingspi, ingspi->rx_trigger); + set_rx_trigger(ingspi, 1); //the rx trigger is steady for tranfer + print_dbg("t->len: %d, rx fifo width: %d, set rx trigger value to %d\n", t->len, ingspi->rxfifo_width, ingspi->rx_trigger); + + sg_init_one(ingspi->sg_rx, ingspi->rx, t->len); + + if (dma_map_sg(ingspi->rxchan->device->dev, + ingspi->sg_rx, 1, DMA_FROM_DEVICE) != 1) { + dev_err(&spi->dev, "dma_map_sg rx error\n"); + goto err_rx_sgmap; + } + + rxdesc = rxchan->device->device_prep_slave_sg(rxchan, + ingspi->sg_rx, + 1, + DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK,NULL); + if (!rxdesc) { + dev_err(&spi->dev, "device_prep_slave_sg error\n"); + goto err_rxdesc; + } + + txdesc->callback = NULL; + txdesc->callback_param = NULL; + + rxdesc->callback = dma_rx_callback; + rxdesc->callback_param = ingspi; + enable_rx_error_intr(ingspi); + enable_tx_error_intr(ingspi); + + dmaengine_submit(txdesc); + dmaengine_submit(rxdesc); + + dma_async_issue_pending(rxchan); + dma_async_issue_pending(txchan); + + enable_receive(ingspi); +// dump_spi_reg(ingspi); + ret = wait_for_completion_interruptible_timeout(&ingspi->done_rx, 60 * HZ); + if (ret <= 0) { + dump_spi_reg(ingspi); + printk("The spi receiver wait timeout\n"); + goto err_rxdesc; + } + /*jzdma_dump(rxchan);*/ + /*printk("%s[%d]: wait dma\n",__func__,__LINE__);*/ + ret = wait_for_completion_interruptible_timeout(&ingspi->done_rx_dma, 60 * HZ); + if (ret <= 0) { + dump_spi_reg(ingspi); + printk("The spi dam_callback wait timeout\n"); + goto err_rxdesc; + } + finish_transmit(ingspi); + //flush_rxfifo(ingspi); + clear_errors(ingspi); + + return t->len; + +err_rxdesc: + dma_unmap_sg(rxchan->device->dev, ingspi->sg_rx, 1, DMA_FROM_DEVICE); +err_rx_sgmap: +err_txdesc: + dma_unmap_sg(txchan->device->dev, ingspi->sg_tx, 1, DMA_TO_DEVICE); +err_tx_sgmap: + printk("<< dma_txrx error. out of memory >>\n"); + return -ENOMEM; +} + +static irqreturn_t ingenic_spi_dma_irq_callback(struct ingenic_spi *ingspi) +{ + struct ingenic_intr_cnt *g_ingenic_intr = ingspi->g_ingenic_intr; + print_dbg("%s: status register: %08x\n", __func__, spi_readl(ingspi, SSI_SR)); + + if (ssi_underrun(ingspi) && tx_error_intr(ingspi)) { + print_dbg("UNDR:\n"); + + g_ingenic_intr->ssi_eti++; + disable_tx_error_intr(ingspi); + + clear_errors(ingspi); + complete(&ingspi->done); + complete(&ingspi->done_rx); + + goto irq_done; + } + + if (ssi_overrun(ingspi) && rx_error_intr(ingspi)) { + print_dbg(" overrun:\n"); + g_ingenic_intr->ssi_eri++; + + clear_errors(ingspi); + complete(&ingspi->done); + complete(&ingspi->done_rx); + } + +irq_done: + return IRQ_HANDLED; +} + +static inline u32 cpu_read_rxfifo(struct ingenic_spi *ingspi) +{ + u8 unit_size = ingspi->transfer_unit_size; + u32 cnt, dat; + int dummy_read = 0; + + print_dbg("The count of RxFIFO is %d \n", get_rxfifo_count(ingspi)); + if (get_rxfifo_count(ingspi) < 1) + return 0; + + cnt = ingspi->rlen; + if ((ingspi->rw_mode & RW_MODE) == W_MODE) { + print_dbg("W_MODE\n"); + dummy_read = 1; + } + + spin_lock(&ingspi->lock); + + while (!rxfifo_empty(ingspi)) { + ingspi->rlen += unit_size; + if (dummy_read) + dat = spi_readl(ingspi, SSI_DR); + else + dat = ingspi->get_rx(ingspi); + } + + spin_unlock(&ingspi->lock); + + return (ingspi->rlen - cnt); +} + +static inline u32 cpu_write_txfifo(struct ingenic_spi *ingspi, u32 entries) +{ + u8 unit_size = ingspi->transfer_unit_size; + u32 i, cnt, count; + u32 dat; + + if ((!entries ) || (!(ingspi->rw_mode & RW_MODE))) + return 0; + + cnt = entries; + count = cnt * unit_size; + + spin_lock(&ingspi->lock); + if (ingspi->rw_mode & W_MODE) { + for (i = 0; i < cnt; i++) { + ingspi->count += unit_size; + dat = (u32)(ingspi->get_tx(ingspi)); + } + } else { /* read, fill txfifo with 0 */ + for (i = 0; i < cnt; i++) { + ingspi->count += unit_size; + transmit_data(ingspi, 0); + } + } + spin_unlock(&ingspi->lock); + + print_dbg("ingspi->count:%d. %s LINE %d: %s\n", ingspi->count, __func__, __LINE__, __FILE__); + return count; +} + +static int ingenic_spi_cpu_transfer(struct ingenic_spi *ingspi, long length) +{ + unsigned char int_flag = 0, last_flag = 0; + u32 entries = 0, send_entries = 0; + u32 unit_size, trigger; + long leave_len_bytes; + u32 retlen; + + print_dbg("%s LINE %d: %s\n", __func__, __LINE__, __FILE__); + + /* calculate the left entries */ + leave_len_bytes = ingspi->len - ingspi->count; + + if (ingspi->len < ingspi->count) { + dev_err(ingspi->dev, + "Fill data len error!!!(len : count > %d : %d)\n", + ingspi->len, ingspi->count); + return -1; + } + + if (leave_len_bytes == 0) { + print_dbg("leave_len_bytes = 0\n"); + printk("leave_len_bytes = 0\n"); + return 0; + } + + if (ingspi->len % ingspi->transfer_unit_size) { + pr_err("The length of tranfer data is error\n"); + return -EFAULT; + } + + unit_size = ingspi->transfer_unit_size; + if (unit_size == SPI_8BITS) + entries = leave_len_bytes; + else if (unit_size == SPI_16BITS ) + entries = leave_len_bytes >> 1; + else if (unit_size == SPI_32BITS ) + entries = leave_len_bytes >> 2; + else { + dev_err(ingspi->dev,"transfer_unit_size error!\n"); + return -1; + } + print_dbg("%s unit_size:%d, entries:%d\n", __func__, unit_size, entries); + + /* calculate the entries which will be sent currently + * distinguish between the first and interrupt */ + if (ingspi->is_first) { + /* CPU Mode should reset SSI triggers at first */ + ingspi->tx_trigger = SSI_TX_FIFO_THRESHOLD * 8; + ingspi->rx_trigger = (SSI_RX_FIFO_THRESHOLD - SSI_SAFE_THRESHOLD) * 8; + + set_tx_trigger(ingspi, ingspi->tx_trigger); + set_rx_trigger(ingspi, ingspi->rx_trigger); + + if(entries <= INGENIC_SSI_MAX_FIFO_ENTRIES) { + send_entries = entries; + } else { + /* need enable half_intr, left entries will be sent + in SSI interrupt and receive the datas */ + send_entries = INGENIC_SSI_MAX_FIFO_ENTRIES; + int_flag = 1; + } + start_transmit(ingspi); + + ingspi->is_first = 0; + } else { /* happen in interrupts */ + trigger = INGENIC_SSI_MAX_FIFO_ENTRIES - ingspi->tx_trigger; + if (entries <= trigger) { + send_entries = entries; + /* the last part of data shouldn't disable RXI_intr + at once !!! */ + last_flag = 1; + } else { + /* need enable half_intr, left entries will be sent + in SSI interrupt and receive the datas */ + send_entries = CPU_ONCE_BLOCK_ENTRIES; + int_flag = 1; + } + } + + if (length > 0) { + length = length/ingspi->transfer_unit_size; + if (length < send_entries) + send_entries = length; + } + + /* fill the txfifo with CPU Mode */ + retlen = cpu_write_txfifo(ingspi, send_entries); + if (!retlen) { + dev_info(ingspi->dev,"cpu_write_txfifo error!\n"); + return -1; + } + print_dbg("+:(%d)\n", retlen); + + enable_tx_error_intr(ingspi); + enable_rx_error_intr(ingspi); + + /* every time should control the SSI half_intrs */ + if (int_flag) { + enable_txfifo_half_empty_intr(ingspi); + enable_rxfifo_half_full_intr(ingspi); + } else { + disable_txfifo_half_empty_intr(ingspi); + disable_rxfifo_half_full_intr(ingspi); + } + + /* to avoid RxFIFO overflow when CPU Mode at last time to fill */ + if (last_flag) { + last_flag = 0; + enable_rxfifo_half_full_intr(ingspi); + } + +#ifdef SSI_DEGUG + dump_spi_reg(ingspi); +#endif + + return 0; +} + +static int ingenic_spi_pio_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + struct ingenic_intr_cnt *g_ingenic_intr = ingspi->g_ingenic_intr; + u32 entries; + int status; + unsigned long flags; + + ingspi->tx = t->tx_buf; + ingspi->rx = t->rx_buf; + ingspi->len = t->len; + ingspi->count = 0; + ingspi->rlen = 0; + ingspi->dma_flag &= ~SPI_DMA_ACK; + + ingspi->rw_mode = 0; + if(ingspi->tx) + ingspi->rw_mode |= W_MODE; + if(ingspi->rx) + ingspi->rw_mode |= R_MODE; + + disable_tx_intr(ingspi); + disable_rx_intr(ingspi); + + start_transmit(ingspi); + flush_fifo(ingspi); + + enable_receive(ingspi); + clear_errors(ingspi); + + memset(g_ingenic_intr, 0, sizeof(struct ingenic_intr_cnt)); + /* Calculate Max IRQ numbers for SSI error out */ + entries = ingspi->len * 8 / ingspi->bits_per_word; + g_ingenic_intr->max_ssi_intr = (entries + INGENIC_SSI_MAX_FIFO_ENTRIES - 1) / + INGENIC_SSI_MAX_FIFO_ENTRIES * 2 + 2; + +#ifdef SSI_DEGUG + dump_spi_reg(ingspi); +#endif + + /* This start SSI transfer, write data or 0 to txFIFO. + * irq is locked to protect SSI config registers */ + spin_lock_irqsave(&ingspi->txrx_lock, flags); + ingspi->is_first = 1; + status = ingenic_spi_cpu_transfer(ingspi, 0); + if (status < 0) { + dev_err(ingspi->dev,"ERROR:spi_transfer error(%d)!\n", status); + disable_tx_intr(ingspi); + disable_rx_intr(ingspi); + spin_unlock_irqrestore(&ingspi->txrx_lock, flags); + + return status; + } + spin_unlock_irqrestore(&ingspi->txrx_lock, flags); + + /* wait the interrupt finish the transfer( one spi_transfer be sent ) */ + wait_for_completion_interruptible(&ingspi->done); + + if(t->cs_change) + finish_transmit(ingspi); + clear_errors(ingspi); + + if (ingspi->rlen != t->len) { + dev_info(ingspi->dev, "Length error:ingspi->rlen=%d t->len=%d\n", ingspi->rlen,t->len); + + if(ingspi->rlen > ingspi->len) + ingspi->rlen = ingspi->len; + } + + return ingspi->rlen; +} + +static irqreturn_t ingenic_spi_pio_irq_callback(struct ingenic_spi *ingspi) +{ + struct ingenic_intr_cnt *g_ingenic_intr = ingspi->g_ingenic_intr; + long left_count = ingspi->len - ingspi->count; + u8 flag = 0; + u32 cnt; + int status; + + g_ingenic_intr->ssi_intr_cnt++; + /* to avoid die in interrupt if some error occur */ + if (g_ingenic_intr->ssi_intr_cnt > g_ingenic_intr->max_ssi_intr) { + disable_tx_intr(ingspi); + disable_rx_intr(ingspi); + dev_err(ingspi->dev,"ssi interrupts too many count(%d)!\n", + g_ingenic_intr->ssi_intr_cnt); + + complete(&ingspi->done); + goto irq_done; + } + + if ( ssi_underrun(ingspi) && tx_error_intr(ingspi) ) { + print_dbg("UNDR:"); + g_ingenic_intr->ssi_eti++; + disable_tx_error_intr(ingspi); + + if(left_count == 0){ + cnt = cpu_read_rxfifo(ingspi); + print_dbg("-:(%d)\n",cnt); + + disable_tx_intr(ingspi); + disable_rx_intr(ingspi); + + complete(&ingspi->done); + } else { + clear_errors(ingspi); + enable_tx_error_intr(ingspi); + } + + flag++; + } + + if ( ssi_overrun(ingspi) && rx_error_intr(ingspi) ) { + print_dbg(" overrun:"); + g_ingenic_intr->ssi_eri++; + + cnt = cpu_read_rxfifo(ingspi); + print_dbg("-:(%d)\n",cnt); + + flag++; + } + + if ( rxfifo_half_full(ingspi) && + rxfifo_half_full_intr(ingspi)) { + + print_dbg("RXI:"); + g_ingenic_intr->ssi_rxi++; + + cnt = cpu_read_rxfifo(ingspi); + print_dbg("-:(%d)\n",cnt); + + flag++; + } + + if ( txfifo_half_empty_intr(ingspi) && + txfifo_half_empty(ingspi)) { + + print_dbg("TXI:"); + g_ingenic_intr->ssi_txi++; + + status = ingenic_spi_cpu_transfer(ingspi, 0); + if (status < 0) { + dev_err(ingspi->dev,"ingenic_spi_cpu_transfer error!!!!!\n"); + disable_tx_intr(ingspi); + disable_rx_intr(ingspi); + complete(&ingspi->done); + + goto irq_done; + } + flag++; + } + + if (!flag) { + dev_info(ingspi->dev, "\nERROR:SSI interrupt Type error\n"); + complete(&ingspi->done); + } + +irq_done: + clear_errors(ingspi); + return IRQ_HANDLED; +} + +/* every spi_transfer could call this routine to setup itself */ +static int ingenic_spi_setupxfer(struct spi_device *spi, struct spi_transfer *t) +{ + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + u8 bpw, fifo_width; + u32 hz; + int ret; + + /*printk("%s[%d]: \n",__func__,__LINE__);*/ + bpw = spi->bits_per_word; + hz = spi->max_speed_hz; + + if (t) { + if(t->bits_per_word) + bpw = t->bits_per_word; + if(t->speed_hz) + hz = t->speed_hz; + } + + if (bpw < 2 || bpw > 32) { + dev_err(&spi->dev, "invalid bits-per-word (%d)\n", bpw); + return -EINVAL; + } + + if (ingspi->use_dma) { + ingspi->txrx_bufs = &ingenic_spi_dma_txrx; + ingspi->irq_callback = &ingenic_spi_dma_irq_callback; + } else { + ingspi->txrx_bufs = &ingenic_spi_pio_txrx; + ingspi->irq_callback = &ingenic_spi_pio_irq_callback; + } + + ingspi->bits_per_word = bpw; + if (bpw <= 8) { + ingspi->transfer_unit_size = SPI_8BITS; + ingspi->get_rx = ingenic_spi_rx_buf_u8; + ingspi->get_tx = ingenic_spi_tx_buf_u8; + fifo_width = FIFO_W8; + } else if (bpw <= 16) { + ingspi->transfer_unit_size = SPI_16BITS; + ingspi->get_rx = ingenic_spi_rx_buf_u16; + ingspi->get_tx = ingenic_spi_tx_buf_u16; + fifo_width = FIFO_W16; + } else { + ingspi->transfer_unit_size = SPI_32BITS; + ingspi->get_rx = ingenic_spi_rx_buf_u32; + ingspi->get_tx = ingenic_spi_tx_buf_u32; + fifo_width = FIFO_W32; + } + + ingspi->txfifo_width = fifo_width; + ingspi->rxfifo_width = fifo_width; + set_frame_length(ingspi, fifo_width); + + if (spi->mode & SPI_LSB_FIRST) { + set_tx_lsb(ingspi); + set_rx_lsb(ingspi); + } else { + set_tx_msb(ingspi); + set_rx_msb(ingspi); + } + + if((ret = ingenic_spi_clk_set_rate(spi, hz))) + return ret; + + dev_dbg(&spi->dev, "The real SPI CLK is %ld Hz\n", ingenic_spi_clk_get_rate(spi)); + + mutex_lock(&ingspi->bitbang.lock); + if (!ingspi->bitbang.busy) { + ingspi->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); + /* need to ndelay for 0.5 clocktick ? */ + } + mutex_unlock(&ingspi->bitbang.lock); + + return 0; +} + +static int ingenic_spi_setup(struct spi_device *spi) +{ + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + unsigned long flags; + unsigned int frmhl = 0; + + spin_lock_irqsave(&ingspi->lock, flags); + if (ingspi->state & SUSPND) { + spin_unlock_irqrestore(&ingspi->lock, flags); + dev_err(&spi->dev, + "setup: SPI-%d not active!\n", spi->master->bus_num); + return -ESHUTDOWN; + } + spin_unlock_irqrestore(&ingspi->lock, flags); + + if (spi->chip_select >= spi->master->num_chipselect) { + dev_err(&spi->dev, "cs%d >= max %d\n", + spi->chip_select, + spi->master->num_chipselect); + return -EINVAL; + } + + if (spi->chip_select == 0) { + select_ce(ingspi); + frmhl = spi_readl(ingspi, SSI_CR1); + frmhl &= ~(1<<30); + frmhl |= (spi->mode & SPI_CS_HIGH ? 1 : 0) << 30; + spi_writel(ingspi, SSI_CR1, frmhl); + } else if (spi->chip_select == 1) { + select_ce2(ingspi); + frmhl = spi_readl(ingspi, SSI_CR1); + frmhl &= ~(1<<31); + frmhl |= (spi->mode & SPI_CS_HIGH ? 1 : 0) << 31; + spi_writel(ingspi, SSI_CR1, frmhl); + } else + return -EINVAL; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + if (spi->mode & ~MODEBITS) { + dev_info(&spi->dev, "Warning: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + ingspi->spi_mode = spi->mode; + + if (spi->mode & SPI_LSB_FIRST) { + set_tx_lsb(ingspi); + set_rx_lsb(ingspi); + } else { + set_tx_msb(ingspi); + set_rx_msb(ingspi); + } + + if (spi->bits_per_word & ~SPI_BITS_SUPPORT) { + dev_info(&spi->dev, "Warning: unsupported bits_per_word: %d\n", + spi->bits_per_word); + return -EINVAL; + } + + if (!spi->max_speed_hz) { + return -EINVAL; + } + + /*printk("%s[%d]: ingspi->max_clk = %ld, spi->max_speed_hz = %ld\n",__func__,__LINE__,ingspi->max_clk, spi->max_speed_hz);*/ + if (ingspi->max_clk < spi->max_speed_hz) { + dev_info(&spi->dev, "Warning:invalid clock(%d Hz) be set to source clk(%d Hz)!\n", + spi->max_speed_hz,(uint)ingspi->max_clk); + spi->max_speed_hz = ingspi->max_clk; + } + + mutex_lock(&ingspi->bitbang.lock); + if (!ingspi->bitbang.busy) { + ingspi->bitbang.chipselect(spi, BITBANG_CS_INACTIVE); + /* need to ndelay for 0.5 clocktick ? */ + } + mutex_unlock(&ingspi->bitbang.lock); + + return 0; +} + +/** + * ingenic_spi_txrx - functions which will handle transfer data + * @spi: spi device on which data transfer to be done + * @t: spi transfer in which transfer info is filled + * + * This function will put data to be transferred into data register + * of SPI controller and then wait until the completion will be marked + * by the IRQ Handler. + */ +static int ingenic_spi_txrx(struct spi_device * spi, struct spi_transfer *t) +{ + struct ingenic_spi *ingspi = spi_master_get_devdata(spi->master); + unsigned int ret; + unsigned long flags; + + spin_lock_irqsave(&ingspi->lock, flags); + if (ingspi->state & SUSPND) { + ingspi->state &= ~SPIBUSY; + spin_unlock_irqrestore(&ingspi->lock, flags); + printk("Now enter suspend, so cann't tranfer data\n"); + return -ESHUTDOWN; + } + ingspi->state |= SPIBUSY; + spin_unlock_irqrestore(&ingspi->lock, flags); + + ret = ingspi->txrx_bufs(spi, t); + + spin_lock_irqsave(&ingspi->lock, flags); + ingspi->state &= ~SPIBUSY; + spin_unlock_irqrestore(&ingspi->lock, flags); + return ret; +} + +static irqreturn_t ingenic_spi_irq(int irq, void *dev) +{ + struct ingenic_spi *ingspi = dev; + + return ingspi->irq_callback(ingspi); +} + +static int ingenic_spi_init_setup(struct ingenic_spi *ingspi) +{ + ingspi->clk_flag = 1; + ingenic_spi_clk_enable(ingspi); + + /* disable the SSI controller */ + ssi_disable(ingspi); + + /* set default half_intr trigger */ + ingspi->tx_trigger = SSI_TX_FIFO_THRESHOLD * 8; + ingspi->rx_trigger = SSI_RX_FIFO_THRESHOLD * 8; + set_tx_trigger(ingspi, ingspi->tx_trigger); + set_rx_trigger(ingspi, ingspi->rx_trigger); + + /* First,mask the interrupt, while verify the status ? */ + disable_tx_intr(ingspi); + disable_rx_intr(ingspi); + + disable_receive(ingspi); + + set_spi_clock_phase(ingspi, 0); + set_spi_clock_polarity(ingspi, 0); + set_tx_msb(ingspi); + set_rx_msb(ingspi); + + set_spi_format(ingspi); + set_frame_length(ingspi, 8); + disable_loopback(ingspi); + flush_fifo(ingspi); + + underrun_auto_clear(ingspi); + clear_errors(ingspi); + ssi_enable(ingspi); + + return 0; +} + +#ifdef CONFIG_OF +static struct ingenic_spi_info *ingenic_spi_parse_dt(struct ingenic_spi *ingspi) +{ + struct ingenic_spi_info *isi; + struct device *dev = ingspi->dev; + unsigned int value; + int i; + isi = devm_kzalloc(dev, sizeof(*isi), GFP_KERNEL); + if (!isi) + return ERR_PTR(-ENOMEM); + + if(of_property_read_u32(dev->of_node, "spi-max-frequency", &value)) { + dev_warn(dev, "spi-max-frequency not specified\n"); + isi->max_clk = 0; + } else { + isi->max_clk = value; + } + + /*printk("%s[%d]: isi->max_clk = %ld\n",__func__,__LINE__, isi->max_clk);*/ + if(of_property_read_u32(dev->of_node, "ingenic,has_dma_support", &value)) { + dev_warn(dev, "spi-max-frequency not specified\n"); + ingspi->use_dma = 0; + } else { + ingspi->use_dma = value; + } + + if (of_property_read_u32(dev->of_node, "ingenic,chnl", &value)) { + dev_warn(dev, "ingenic,channel not specified\n"); + isi->chnl = 0; + } else { + isi->chnl = value; + } + + /*printk("%s[%d]: isi->chnl = %d\n",__func__,__LINE__, isi->chnl);*/ + if (of_property_read_u32(dev->of_node, "num-cs", &value)) { + dev_warn(dev, "num_cs not specified\n"); + isi->num_chipselect = 0; + } else { + isi->num_chipselect = value; + } + + /*printk("%s[%d]: isi->num_chipselect = %d\n",__func__,__LINE__, isi->num_chipselect);*/ + if (of_property_read_u32(dev->of_node, "ingenic,allow_cs_same", &value)) { + dev_warn(dev, "ingenic,allow_cs_same not specified\n"); + isi->allow_cs_same = 0; + } else { + isi->allow_cs_same = value; + } + + /*printk("%s[%d]: isi->allow_cs_same = %d\n",__func__,__LINE__, isi->allow_cs_same);*/ + if (of_property_read_u32(dev->of_node, "ingenic,bus_num", &value)) { + dev_warn(dev, "ingenic,bus_num not specified\n"); + isi->bus_num = 0; + } else { + isi->bus_num = value; + } + + /*printk("%s[%d]: isi->bus_num = %d\n",__func__,__LINE__, isi->bus_num);*/ + for (i = 0; i < isi->num_chipselect; i++) { + int cs_gpio = of_get_named_gpio(dev->of_node, "cs-gpios", i); + if (cs_gpio == -EPROBE_DEFER) { + break; + } + isi->chipselects[i] = cs_gpio; + /*printk("%s[%d]: cs%d is gpio = %d\n",__func__,__LINE__, i ,cs_gpio);*/ + if (gpio_is_valid(cs_gpio)) { + if (devm_gpio_request(dev, cs_gpio, "INGENIC_SPI_CS")) { + if(!isi->allow_cs_same) + dev_err(dev, "could not request %d gpio\n", cs_gpio); + } else if (gpio_direction_output(cs_gpio, 1)) + dev_err(dev, "could not set gpio %d as output\n", cs_gpio); + } + } + + return isi; +} +#else +static struct ingenic_spi_info *ingenic_spi_parse_dt(struct device *dev) +{ + return dev_get_platdata(dev); +} +#endif + +static int ingenic_spi_clk_init(struct platform_device *pdev, struct ingenic_spi *ingspi) +{ + struct clk *clk; + char clkname[16]; + int err = 0; + + pdev->id = of_alias_get_id(pdev->dev.of_node, "spi"); + sprintf(clkname, "gate_ssi%d", pdev->id); + ingspi->clk_gate = devm_clk_get(&pdev->dev, clkname); + ingspi->clk_cgu = devm_clk_get(&pdev->dev, "div_ssi"); + + if (IS_ERR(ingspi->clk_cgu) || IS_ERR(ingspi->clk_gate)) { + dev_err(&pdev->dev, "Cannot get spi clock\n"); + err = PTR_ERR(ingspi->clk_cgu); + return err; + } + return 0; +} + +static int ingenic_spi_configure_dma(struct ingenic_spi *ingspi) +{ + struct device *dev = ingspi->dev; + dma_cap_mask_t mask; + int err = 0; + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + + ingspi->txchan = dma_request_slave_channel_reason(dev, "tx"); + if (IS_ERR(ingspi->txchan)) { + err = PTR_ERR(ingspi->txchan); + if (err == -EPROBE_DEFER) { + dev_warn(dev, "no DMA channel available at the moment\n"); + return err; + } + dev_err(dev, "DMA TX channel not available, SPI unable to use DMA\n"); + err = -EBUSY; + goto error; + } + + /* + * No reason to check EPROBE_DEFER here since we have already requested + * tx channel. If it fails here, it's for another reason. + */ + ingspi->rxchan = dma_request_slave_channel(dev, "rx"); + + if (!ingspi->rxchan) { + dev_err(dev, "DMA RX channel not available, SPI unable to use DMA\n"); + err = -EBUSY; + goto error; + } + + //alloc temp buffer for dma + ingspi->buffer = dma_alloc_coherent(dev, BUFFER_SIZE, + &ingspi->buffer_dma, GFP_KERNEL); + if (!ingspi->buffer) { + dev_err(dev, "SPI request temp dma buffer failed"); + goto error; + } + +#if 0 + ingspi->buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL); + if (!ingspi->buffer) { + dev_err(dev, "SPI request temp dma buffer failed"); + goto error; + } + print_dbg("<< ingspi->buffer addr:%p >>\n", ingspi->buffer); +#endif + + ingspi->sg_tx = devm_kmalloc(dev, sizeof(struct scatterlist), GFP_KERNEL); + if (!ingspi->sg_tx) { + dev_err(dev, "Failed to alloc tx scatterlist\n"); + goto error; + } + + ingspi->sg_rx = devm_kmalloc(dev, sizeof(struct scatterlist), GFP_KERNEL); + if(!ingspi->sg_rx) { + dev_err(dev, "Failed to alloc rx scatterlist\n"); + goto error; + } + + + dev_info(dev, "Using %s (tx) and %s (rx) for DMA transfers\n", + dma_chan_name(ingspi->txchan), + dma_chan_name(ingspi->rxchan)); + return 0; +error: + if (ingspi->rxchan) + dma_release_channel(ingspi->rxchan); + if (!IS_ERR(ingspi->txchan)) + dma_release_channel(ingspi->txchan); + return err; +} + +static int ingenic_spi_probe(struct platform_device *pdev) +{ + struct ingenic_spi *ingspi; + struct spi_master *master; + struct device_node *np = pdev->dev.of_node; + struct ingenic_spi_info *pdata = dev_get_platdata(&pdev->dev); + struct resource *res; + int err = 0; + + master = spi_alloc_master(&pdev->dev, sizeof(struct ingenic_spi)); + if (!master) { + dev_err(&pdev->dev, "Unable to allocate SPI Master\n"); + return -ENOMEM; + } + + /* the spi->mode bits understood by this drivers: */ + master->mode_bits = MODEBITS; + + ingspi = spi_master_get_devdata(master); + ingspi->g_ingenic_intr = devm_kzalloc(&pdev->dev, + sizeof(struct ingenic_intr_cnt),GFP_KERNEL); + if(!ingspi->g_ingenic_intr) { + dev_err(&pdev->dev, "No memory for ingenic_intr_cnt\n"); + return -ENOMEM; + } + + ingspi->master = spi_master_get(master); + ingspi->dev = &pdev->dev; + + if (!pdata && np) { + pdata = ingenic_spi_parse_dt(ingspi); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + if (!pdata) { + dev_err(&pdev->dev, "platform_data missing!\n"); + return -ENODEV; + } + + + ingspi->pdata = pdata; + ingspi->chnl= ingspi->pdata->chnl; + master->bus_num = (s16)ingspi->pdata->bus_num; + if(master->bus_num != 0 && master->bus_num != 1){ + dev_err(&pdev->dev, "No this channel, bus_num= %d.\n", master->bus_num); + err = -ENOENT; + goto err_no_pdata; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "Unable to get SPI MEM resource\n"); + return -ENXIO; + } + ingspi->phys = res->start; + ingspi->iomem = devm_ioremap_resource(&pdev->dev, res); + if (!ingspi->iomem) { + dev_err(&pdev->dev, "Cannot map IO\n"); + err = -ENXIO; + goto err_no_iomap; + } + + ingspi->irq = platform_get_irq(pdev, 0); + if (ingspi->irq <= 0) { + dev_err(&pdev->dev, "No IRQ specified\n"); + err = -ENOENT; + goto err_no_irq; + } + + ingenic_spi_clk_init(pdev, ingspi); + + ingspi->spi_clk = ingspi->pdata->src_clk; + ingspi->max_clk = ingspi->pdata->max_clk; + + platform_set_drvdata(pdev, ingspi); + init_completion(&ingspi->done); + init_completion(&ingspi->done_rx); + init_completion(&ingspi->done_tx_dma); + init_completion(&ingspi->done_rx_dma); + spin_lock_init(&ingspi->lock); + spin_lock_init(&ingspi->txrx_lock); + + master->bus_num = ingspi->pdata->bus_num; + master->num_chipselect = ingspi->pdata->num_chipselect; + master->dev.of_node = pdev->dev.of_node; + /* setup the state for the bitbang driver */ + ingspi->bitbang.master = ingspi->master; + ingspi->bitbang.setup_transfer = ingenic_spi_setupxfer; + ingspi->bitbang.chipselect = ingenic_spi_chipsel; + ingspi->bitbang.txrx_bufs = ingenic_spi_txrx; + ingspi->bitbang.master->setup = ingenic_spi_setup; + ingspi->fifodepth = INGENIC_SSI_MAX_FIFO_ENTRIES; + ingspi->set_cs = &ingenic_spi_cs; + + ingenic_spi_init_setup(ingspi); + + if (ingspi->use_dma) { + ingenic_spi_configure_dma(ingspi); + } + + /* request SSI irq */ + err = devm_request_irq(&pdev->dev, ingspi->irq, ingenic_spi_irq, 0, pdev->name, ingspi); + if (err) { + dev_err(&pdev->dev, "Cannot claim IRQ\n"); + goto err_register; + } + + dev_dbg(ingspi->dev, "bitbang at %p\n", &ingspi->bitbang); + + err = spi_bitbang_start(&ingspi->bitbang); + if (err) { + dev_err(&pdev->dev, "Failed to register SPI master ERR_NO:%d\n",err); + goto err_register; + } + + printk(KERN_INFO "INGENIC SSI Controller for SPI channel %d driver register\n",ingspi->chnl); + return 0; + +err_register: + free_irq(ingspi->irq, ingspi); +err_no_irq: + if(ingspi->clk_gate) + clk_put(ingspi->clk_gate); + if(ingspi->clk_cgu) + clk_put(ingspi->clk_cgu); + iounmap(ingspi->iomem); +err_no_iomap: + release_resource(ingspi->ioarea); + kfree(ingspi->ioarea); +#ifdef CONFIG_INGENIC_SPI_PIO_CE +err_cs_gpio: + for (i = 0; i < num_cs_got; i++) + gpio_free(ingspi->pdata->chipselect[i]); +#endif +err_no_pdata: + spi_master_put(ingspi->master); + + return err; +} + +static int ingenic_spi_remove(struct platform_device *dev) +{ + struct ingenic_spi *ingspi = platform_get_drvdata(dev); + + spi_master_put(ingspi->master); + spi_bitbang_stop(&ingspi->bitbang); + + platform_set_drvdata(dev, NULL); + + free_irq(ingspi->irq, ingspi); + iounmap(ingspi->iomem); + + ingenic_spi_clk_disable(ingspi); + clk_put(ingspi->clk_gate); + clk_put(ingspi->clk_cgu); + + release_resource(ingspi->ioarea); + kfree(ingspi->ioarea); + + /* release DMA channel */ + if (ingspi->rxchan) { + dma_release_channel(ingspi->rxchan); + } + if (ingspi->txchan) { + dma_release_channel(ingspi->txchan); + } + +#ifdef CONFIG_INGENIC_SPI_PIO_CE + /* release chipselect gpio */ + { + int i; + for (i = 0; i < ingspi->pdata->num_chipselect; i++) + gpio_free(ingspi->pdata->chipselect[i]); + } +#endif + + kfree(ingspi->g_ingenic_intr); + kfree(ingspi); + printk(KERN_INFO "INGENIC SSI Controller for SPI channel %d driver removed\n",ingspi->chnl); + + return 0; +} + +#ifdef CONFIG_PM +static int ingenic_spi_suspend(struct platform_device *pdev, pm_message_t msg) +{ + struct ingenic_spi *ingspi = platform_get_drvdata(pdev); + unsigned long flags; + + spin_lock_irqsave(&ingspi->lock, flags); + ingspi->state |= SUSPND; + spin_unlock_irqrestore(&ingspi->lock, flags); + + while (ingspi->state & SPIBUSY) + printk("Now spi is busy, waitting!\n"); + + ingenic_spi_clk_disable(ingspi); + + return 0; +} + +static int ingenic_spi_resume(struct platform_device *pdev) +{ + struct ingenic_spi *ingspi = platform_get_drvdata(pdev); + unsigned long flags; + + ingenic_spi_clk_enable(ingspi); + + spin_lock_irqsave(&ingspi->lock, flags); + ingspi->state &= ~SUSPND; + spin_unlock_irqrestore(&ingspi->lock, flags); + + return 0; +} + +#else +#define ingenic_spi_suspend NULL +#define ingenic_spi_resume NULL +#endif + +static const struct of_device_id ingenic_spi_match[] = { + { .compatible = "ingenic,spi", }, + {} +}; +MODULE_DEVICE_TABLE(of, ingenic_spi_match); + +static struct platform_driver ingenic_spidrv = { + .probe = ingenic_spi_probe, + .remove = ingenic_spi_remove, + .suspend = ingenic_spi_suspend, + .resume = ingenic_spi_resume, + .driver = { + .name = "ingenic-spi", + .of_match_table = ingenic_spi_match, + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(ingenic_spidrv); + +MODULE_ALIAS("ingenic_spi"); +MODULE_AUTHOR("Bo Liu "); +MODULE_DESCRIPTION("INGENIC SPI controller driver"); +MODULE_LICENSE("GPL");