diff options
Diffstat (limited to 'ANDROID_3.4.5/drivers/spi/wmt_spi.c')
-rwxr-xr-x | ANDROID_3.4.5/drivers/spi/wmt_spi.c | 1347 |
1 files changed, 0 insertions, 1347 deletions
diff --git a/ANDROID_3.4.5/drivers/spi/wmt_spi.c b/ANDROID_3.4.5/drivers/spi/wmt_spi.c deleted file mode 100755 index 7b301488..00000000 --- a/ANDROID_3.4.5/drivers/spi/wmt_spi.c +++ /dev/null @@ -1,1347 +0,0 @@ -/*++ - drivers/spi/wmt_spi.c - - Copyright (c) 2008 WonderMedia Technologies, Inc. - - 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. - - This program is distributed in the hope that it will be useful, but WITHOUT - ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - PARTICULAR PURPOSE. See the GNU General Public License for more details. - You should have received a copy of the GNU General Public License along with - this program. If not, see <http://www.gnu.org/licenses/>. - - WonderMedia Technologies, Inc. - 10F, 529, Chung-Cheng Road, Hsin-Tien, Taipei 231, R.O.C. ---*/ -#include <linux/init.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#include <linux/workqueue.h> -#include <linux/ioport.h> -#include <linux/dma-mapping.h> -#include <linux/sched.h> - -#include <mach/hardware.h> -#include <mach/wmt_gpio.h> - -//#define DEBUG 1 /* debug open */ -#include <linux/platform_device.h> -#include <linux/spi/spi.h> -#include <mach/wmt-spi.h> - -#ifdef DEBUG -#define spi_dbg(fmt, args...) printk(KERN_ERR "[%s]_%d: " fmt, __func__ , __LINE__, ## args) -#define spi_trace() printk(KERN_ERR "trace in %s %d\n", __func__, __LINE__); -#else -#define spi_dbg(fmt, args...) -#define spi_trace() -#endif - -static unsigned int pllb_input_freq = 83333; -static bool g_use_ss1 = false; -static bool g_use_ss2 = false; -static bool g_use_ss3 = false; - -/* - * spi_set_reg32 - write a u32 value to spi register - * @spi: spi controller's driver data - * @reg_offset: register's offset address - * @val: value register will be set - */ -static inline void -spi_set_reg32(struct wmt_spi *spi, u32 reg_offset, u32 val) -{ - iowrite32(val, spi->regs_base + reg_offset); -} - -/* - * spi_get_reg32 - read a u32 value from spi register - * @spi: spi controller's driver data - * @reg_offset: register's offset address - */ -static inline unsigned int -spi_get_reg32(struct wmt_spi *spi, int reg_offset) -{ - return ioread32(spi->regs_base + reg_offset); -} - -/* - * spi_setbit: write bit1 to related register's bit - * @spi: spi controller's driver data - * @offset: register's offset address - * @mask: bit setting mask - */ -static void -spi_setbit(struct wmt_spi *spi, u32 reg_offset, u32 mask) -{ - u32 tmp; - tmp = spi_get_reg32(spi, reg_offset); - tmp |= mask; - spi_set_reg32(spi, reg_offset, tmp); -} - -/* - * spi_clrbit: write bit0 to related register's bit - * @spi: spi controller's driver data - * @offset: register's offset address - * @mask: bit setting mask - */ -static void -spi_clrbit(struct wmt_spi *spi, u32 reg_offset, u32 mask) -{ - u32 tmp; - tmp = spi_get_reg32(spi, reg_offset); - tmp &= ~mask; - spi_set_reg32(spi, reg_offset, tmp); -} - -/* - * spi_write_fifo: write a u8 value to spi tx fifo - * @spi: spi controller's driver data - * @fifo_reg: spi tx fifo register offset - * @val: value writen to spi tx fifo - */ -static inline void -spi_write_fifo(struct wmt_spi *spi, u32 fifo_reg, const u8 val) -{ - iowrite8(val, spi->regs_base + fifo_reg); -} - -/* - * spi_read_fifo: read a u8 value from spi rx fifo - * @spi: spi controller's driver data - * @fifo_reg: spi rx fifo register offset - */ -static inline u8 -spi_read_fifo(struct wmt_spi *spi, u32 fifo_reg) -{ - return ioread8(spi->regs_base + fifo_reg); -} - -/* - * wmt_spi_clock_enable: set GPIO to SPI mode, ten enable spi - * clock from Power management module and set default spi clock - * dividor to 0x04 as default (93Mhz) - */ -static inline void wmt_spi_clock_enable(void) -{ - int timeout = POLLING_SPI_REG_TIMEOUT; - - GPIO_CTRL_GP12_SPI_BYTE_VAL &= ~(GPIO_SPI0_CLK | - GPIO_SPI0_MOSI| - GPIO_SPI0_MISO| - GPIO_SPI0_SS0); - PULL_EN_GP12_SPI_BYTE_VAL |= (GPIO_SPI0_CLK_PULL_EN | - GPIO_SPI0_MOSI_PULL_EN| - GPIO_SPI0_MISO_PULL_EN| - GPIO_SPI0_SS0_PULL_EN); - PULL_CTRL_GP12_SPI_BYTE_VAL |= (GPIO_SPI0_CLK_PULL_EN | - GPIO_SPI0_MOSI_PULL_EN| - GPIO_SPI0_MISO_PULL_EN); - PULL_CTRL_GP12_SPI_BYTE_VAL &= ~GPIO_SPI0_CLK_PULL_UP; - if (g_use_ss1 || g_use_ss2 || g_use_ss3) - PIN_SHARING_SEL_4BYTE_VAL &= ~BIT7; -#if 0 - /*Enable SPI function and disable GPIO function for related SPI pin*/ - GPIO_CTRL_GP11_SPI_BYTE_VAL &= ~(GPIO_SPI0_CLK | - GPIO_SPI0_MOSI| - GPIO_SPI0_MISO); - - GPIO_PULL_EN_GP11_SPI_BYTE_VAL |= ( GPIO_SPI0_CLK_PULL_EN | - GPIO_SPI0_MISO_PULL_EN | - GPIO_SPI0_MOSI_PULL_EN ); - GPIO_PULL_CTRL_GP11_SPI_BYTE_VAL |= (GPIO_SPI0_MISO_PULL_UP | - GPIO_SPI0_MOSI_PULL_UP ); - - GPIO_PULL_CTRL_GP11_SPI_BYTE_VAL &=~ (GPIO_SPI0_CLK_PULL_UP); - if (g_use_ss0) { - GPIO_CTRL_GP11_SPI_BYTE_VAL &= ~(GPIO_SPI0_SS); - GPIO_PULL_EN_GP11_SPI_BYTE_VAL |= GPIO_SPI0_SS_PULL_EN; - GPIO_PULL_CTRL_GP11_SPI_BYTE_VAL |= GPIO_SPI0_SS_PULL_UP; - } - if (g_use_ss1) { - GPIO_CTRL_GP1_BYTE_VAL &= ~BIT2; - GPIO_PULL_EN_GP1_BYTE_VAL |= BIT2; - GPIO_PULL_CTRL_GP1_BYTE_VAL |= BIT2; - GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT5; - } - if (g_use_ss2) { - GPIO_CTRL_GP31_BYTE_VAL &= ~BIT1; - GPIO_PULL_EN_GP31_BYTE_VAL |= BIT1; - GPIO_PULL_CTRL_GP31_BYTE_VAL |= BIT1; - GPIO_PIN_SHARING_SEL_4BYTE_VAL |= BIT12; - } - if (g_use_ss3) { - GPIO_CTRL_GP31_BYTE_VAL &= ~BIT2; - GPIO_PULL_EN_GP31_BYTE_VAL |= BIT2; - GPIO_PULL_CTRL_GP31_BYTE_VAL |= BIT2; - GPIO_PIN_SHARING_SEL_4BYTE_VAL &= ~BIT4; - } - -#endif - /* clock open */ - auto_pll_divisor(DEV_SPI0, CLK_ENABLE, 0, 0); - pllb_input_freq = auto_pll_divisor(DEV_SPI0, SET_DIV, 1, 100000); - pllb_input_freq /= 1000; - - /* check if config successful */ - while (timeout--) { - if (!(REG8_VAL(PMC_REG_BASE) & 0x04000000)) - return ; - udelay(1); - } - printk(KERN_ERR "Enable SPI clock from PMC module failed\n!"); -} - -/* - * wmt_spi_clock_disable: reset GPIO and disable spi clock from - * Power management module - */ -static inline void wmt_spi_clock_disable(void) -{ - GPIO_CTRL_GP12_SPI_BYTE_VAL |= (GPIO_SPI0_CLK | - GPIO_SPI0_MOSI| - GPIO_SPI0_SS0| - GPIO_SPI0_MISO); -} - -/* - * spi_is_busy: check spi controller(master) is busy or not - * @spi: spi controller's driver data - */ -static int spi_is_busy(struct wmt_spi *spi) -{ - unsigned int timeout = POLLING_SPI_REG_TIMEOUT; - while (timeout--) { - if ((spi_get_reg32(spi, SPI_SR) & SPI_SR_BUSY_MASK) == 0) - return 0; - } - return -EAGAIN; -} - -/* - * spi_enable: enable spi module - * @spi: spi controller's driver data - */ -static void spi_enable(struct wmt_spi *spi) -{ - spi_setbit(spi, SPI_CR, SPI_CR_ME_MASK); -} - -/* - * spi_disable: disable spi module - * @spi: spi controller's driver data - */ -static void spi_disable(struct wmt_spi *spi) -{ - if (spi_is_busy(spi)) { - dev_warn(&spi->master->dev, "Disable spi controller failed\n"); - } - spi_clrbit(spi, SPI_CR, SPI_CR_ME_MASK); -} - -/* - * spi_tx_is_finish: polling if data in tx fifo has been sent out - * @spi: spi controller's driver data - */ -static int spi_tx_is_finish(struct wmt_spi *spi) -{ - unsigned int timeout = POLLING_SPI_REG_TIMEOUT; - while (timeout--) { - if (spi_get_reg32(spi, SPI_SR) | SPI_SR_TFEI_MASK) - return 0; - } - return -EAGAIN; -} - -/* - * spi_rx_is_finish: polling if data in rx fifo has been read out - * @spi: spi controller's driver data - */ -static int spi_rx_is_finish(struct wmt_spi *spi) -{ - unsigned int timeout = POLLING_SPI_REG_TIMEOUT; - while (timeout--) { - if ((spi_get_reg32(spi, SPI_SR) | SPI_SR_RFEI_MASK)) - return 0; - } - return -EAGAIN; -} - -/* - * wmt_spi_cs_active: enable chip select signal. SSN is driven - * low to active if spi controller use SSN_CTRL_PROGRAM mode - * @spi: spi controller's driver data - */ -static inline void wmt_spi_cs_active(struct spi_device *spi_dev, struct wmt_spi *spi) -{ - struct wmt_spi_hw *hw_info = spi->spi_hw_info; - - /* enable SSN */ - if (hw_info->ssn_ctrl == SSN_CTRL_PROGRAM) - spi_clrbit(spi, SPI_DFCR, SPI_DFCR_DSV_MASK | SPI_DFCR_DSE_MASK); -} - -/* - * wmt_spi_cs_inactive: disable chip select signal. A SSN is driven - * high to inactive if spi controller use SSN_CTRL_PROGRAM mode - * @spi: spi controller's driver data - */ -static inline void wmt_spi_cs_inactive(struct spi_device *spi_dev, struct wmt_spi *spi) -{ - struct wmt_spi_hw *hw_info = spi->spi_hw_info; - - if (hw_info->ssn_ctrl == SSN_CTRL_PROGRAM) - spi_setbit(spi, SPI_DFCR, SPI_DFCR_DSV_MASK | SPI_DFCR_DSE_MASK); -} - -/* - * get_spi_input_freq: get spi original input frequence from power - * management module - */ -static unsigned int get_spi_input_freq(void) -{ - return pllb_input_freq; -} - -/* - * spi_set_clock_div: set clock divider in spi control register, - * if the divisor is too low or too high, set spi working - * frequency 5Mhz as default - * @spi: spi controller's driver data - * @speed_hz: spi working clock frequency, unit is hz - */ -static void spi_set_clock_div(struct wmt_spi *spi, int speed_hz) -{ - unsigned int divisor; - unsigned int hw_freq = get_spi_input_freq()*1000; /* KHz to Hz */ - - spi_clrbit(spi, SPI_CR, SPI_CR_TCD_MASK); - if (!speed_hz) - goto err; - - divisor = hw_freq/(2*speed_hz); - if (divisor < 0 || divisor > 0x7ff) /* spi cr bit(21,31), max:0x7ff */ - goto err; - - spi_setbit(spi, SPI_CR, divisor << SPI_CR_TCD_SHIFT); - return ; - -err: - divisor = hw_freq/(2*SPI_DFLT_FREQ); - spi_setbit(spi, SPI_CR, divisor << SPI_CR_TCD_SHIFT); - dev_err(&spi->master->dev, "SPI frequency %dhz not support, " \ - "set %dhz as default\n", speed_hz, SPI_DFLT_FREQ); - return ; -} - -/* - * spi_set_bit_order: set spi redeive/transmit significant bit order - * @spi: spi controller's driver data - * @mode: spi device working mode - */ -static void spi_set_bit_order(struct wmt_spi *spi, u8 mode) -{ - if (mode & SPI_LSB_FIRST) - spi_setbit(spi, SPI_DFCR, SPI_DFCR_RSBO_MASK | SPI_DFCR_TSBO_MASK); -} - -/* - * spi_set_clock_mode: set spi clock polarity and phase (spi clock mode) - * @spi: spi controller's driver data - * @clk_mode: spi clock mode - */ -static void spi_set_clock_mode(struct wmt_spi *spi, u8 clk_mode) -{ - spi_clrbit(spi, SPI_CR, SPI_CR_CPHS_MASK); - if (clk_mode > SPI_CLK_MODE3) - goto err; - spi_setbit(spi, SPI_CR, clk_mode << SPI_CR_CPHS_SHIFT); - return ; -err: - spi_setbit(spi, SPI_CR, SPI_CLK_MODE3 << SPI_CR_CPHS_SHIFT); - dev_err(&spi->master->dev, "clock mode err, set clock mode 3 as default\n"); - return ; -} - -/* - * spi_as_master: spi master/slave select - * @spi: spi controller's driver data - * @is_master: if spi configured as master, is_master = 1, else is_master = 0 - */ -static void spi_as_master(struct wmt_spi *spi, u8 is_master) -{ - if (is_master) - spi_clrbit(spi, SPI_CR, SPI_CR_MSMS_MASK); - else - spi_setbit(spi, SPI_CR, SPI_CR_MSMS_MASK); -} - -/* - * spi_reset_tx_fifo: reset spi transmit fifo - * @spi: spi controller's driver data - */ -static int spi_reset_tx_fifo(struct wmt_spi *spi) -{ - unsigned int timeout = POLLING_SPI_REG_TIMEOUT; - spi_setbit(spi, SPI_CR, SPI_CR_TFR_MASK); - while (timeout--) { - if ((spi_get_reg32(spi, SPI_CR) & SPI_CR_TFR_MASK) == 0) - return 0; - } - return -1; -} - -/* - * spi_reset_rx_fifo: reset spi receive fifo - * @spi: spi controller's driver data - */ -static int spi_reset_rx_fifo(struct wmt_spi *spi) -{ - unsigned int timeout = POLLING_SPI_REG_TIMEOUT; - spi_setbit(spi, SPI_CR, SPI_CR_RFR_MASK); - while (timeout--) { - if ((spi_get_reg32(spi, SPI_CR) & SPI_CR_RFR_MASK) == 0) - return 0; - } - return -EAGAIN; -} - -/* - * spi_reset_fifo: reset both spi transmit fifo and receive fifo - * @spi: spi controller's driver data - */ -static int spi_reset_fifo(struct wmt_spi *spi) -{ - if (spi_reset_tx_fifo(spi)) - return -EAGAIN; - if (spi_reset_rx_fifo(spi)) - return -EAGAIN; - return 0; -} - -/* - * spi_reset: reset spi status register and reset spi tx and rx fifo - * @spi: spi controller's driver data - */ -static int spi_reset(struct wmt_spi *spi) -{ - spi_set_reg32(spi, SPI_SR, ~0UL); - return spi_reset_fifo(spi); -} - -/* - * spi_reset_transfer_speed: reset spi work frequency according - * to spi_transfer - * @spi: spi controller's driver data - * @t: spi transfer - */ -static void -spi_reset_transfer_speed(struct wmt_spi *spi, struct spi_transfer *t) -{ - if (t && t->speed_hz) - spi_set_clock_div(spi, t->speed_hz); -} - -/* - * wmt_spi_regs_config: config spi registers according to the - * information get from spi_device - @spi_dev: Master side proxy for an SPI slave device - */ -static int wmt_spi_regs_config(struct spi_device *spi_dev) -{ - int ret = 0; - u8 clk_mode; - struct wmt_spi *wmt_spi; - struct wmt_spi_slave *slave_info; - struct wmt_spi_hw *hw_info; - - wmt_spi = spi_master_get_devdata(spi_dev->master); - slave_info = spi_dev->controller_data; - hw_info = wmt_spi->spi_hw_info; - - /* clear spi control register */ - spi_set_reg32(wmt_spi, SPI_CR, 0x00UL); - /* clear spi status register */ - spi_set_reg32(wmt_spi, SPI_SR, ~0UL); - /* spi cre register */ - spi_set_reg32(wmt_spi, SPI_CRE, 0x20); - - /* reset tx and rx fifo */ - ret = spi_reset_fifo(wmt_spi); - if (ret) - goto err; - /* setting spi controller register */ - /* 1. set spi clock divider */ - spi_set_clock_div(wmt_spi, spi_dev->max_speed_hz); - /* 2 set clock mode */ - clk_mode = (spi_dev->mode & (BIT0 | BIT1)); - spi_set_clock_mode(wmt_spi, clk_mode); - /* 3. spi as master */ - spi_as_master(wmt_spi, 1); - /* 4. slave selection */ - /* - spi_setbit(wmt_spi, SPI_CR, spi_dev->chip_select << SPI_CR_SS_SHIFT); - */ - /*for wm3465 A0*/ - spi_setbit(wmt_spi, SPI_CR, 0 << SPI_CR_SS_SHIFT); - - /* setting spi data format control register */ - /* 1. port mode setting */ - if (PORT_MODE_PTP == hw_info->port_mode) - spi_setbit(wmt_spi, SPI_DFCR, SPI_DFCR_SPM_MASK); - /* 2. spi tx/rx significant bit order */ - spi_set_bit_order(wmt_spi, spi_dev->mode); - /* 3. ssn control setting */ - if (SSN_CTRL_PROGRAM == hw_info->ssn_ctrl) { - if ((clk_mode & SPI_CR_CPHS_MASK) == 0) { - dev_warn(&spi_dev->dev, "SSN_ctrl conflict with clock mode\n"); - /* do not abort now, the conflict is not a serious problem, - driver can handle this well, so we work on */ - goto err; - } - spi_setbit(wmt_spi, SPI_DFCR, SPI_DFCR_SC_MASK); - } - return 0; - -err: - dev_err(&wmt_spi->pdev->dev, "SPI config register error\n"); - return ret; -} - -/* - * wmt_spi_fifo_tx: transmit data by spi TX_FIFO. - * @spi: spi controller's driver data - * @tx_buf: data transmited by spi tx fifo - * @len: tx buffers length. Note: len is not checked in this function, - * and wmt spi fifo size is 32bytes, so caller must be sure - * len <= 32bytes - */ -static void -wmt_spi_fifo_tx(struct wmt_spi *spi, const u8 *tx_buf, int len) -{ - int i; - /* load data to tx fifo */ - if (tx_buf) { - for (i = 0; i < len; i++) - spi_write_fifo(spi, SPI_TXFIFO, tx_buf[i]); - } /* load idle data to tx fifo */else { - for (i = 0; i < len; i++) - spi_write_fifo(spi, SPI_TXFIFO, 0x00); - } -} - -/* - * wmt_spi_write_read: spi transfer routine - * @spi_dev: Master side proxy for an SPI slave device - * @t: spi transfer - * wmt spi transmit fifo is SPI_FIFO_SIZE(32bytes), - * so if transmit data more than 32bytes, we need transmit - * those data more than one time - **/ -static int -wmt_spi_write_read(struct spi_device *spi, struct spi_transfer *t) -{ - int i = 0; - const u8 *tx = t->tx_buf; - u8 *rx = t->rx_buf; - unsigned int cnt = t->len; - struct wmt_spi *wmt_spi = spi_master_get_devdata(spi->master); - - /* reset spi first */ - if (spi_reset(wmt_spi)) - goto out; - /* if data length more than SPI_FIFO_SIZE(32bytes) */ - while (cnt >= SPI_FIFO_SIZE) { - /* load data form tx_buf to tx_fifo */ - wmt_spi_fifo_tx(wmt_spi, tx, SPI_FIFO_SIZE); - /* send data */ - spi_enable(wmt_spi); - /* waitting for send finish */ - if (spi_tx_is_finish(wmt_spi)) - goto out; - /* data send has finished */ - spi_disable(wmt_spi); - /* read data and stroe in rx_buf */ - if (rx) { - for (i = 0; i < SPI_FIFO_SIZE; i++) - *rx++ = spi_read_fifo(wmt_spi, SPI_RXFIFO); - } - /* check if rx finish */ - if (spi_rx_is_finish(wmt_spi)) - goto out; - if (tx) - tx += SPI_FIFO_SIZE; - cnt -= SPI_FIFO_SIZE; - if (t->speed_hz < 5*1000*1000) - udelay(2000); - /* reset for the next transfer */ - if (spi_reset(wmt_spi)) - goto out; - } - /* remain data transfer */ - if (cnt && cnt < SPI_FIFO_SIZE) { - wmt_spi_fifo_tx(wmt_spi, tx, cnt); - spi_enable(wmt_spi); - if (spi_tx_is_finish(wmt_spi)) - goto out; - spi_disable(wmt_spi); - if (rx) { - for (; cnt > 0; cnt--) - *rx++ = spi_read_fifo(wmt_spi, SPI_RXFIFO); - } else { - for (; cnt > 0; cnt--) - spi_read_fifo(wmt_spi, SPI_RXFIFO); - } - } -out: - return (t->len - cnt); -} - -/* - * spi_dsr_w: spi dma transmit callback function - * @arg: point to wmt_spi - **/ -static void spi_dsr_w(void *arg) -{ - struct wmt_spi *spi = (struct wmt_spi *)arg; - struct wmt_spi_dma *spi_dma = spi->spi_dma_info; - spi_dma->tx_ack = 1; - wake_up_interruptible(&spi_dma->tx_event); -} - -/* - * spi_dsr_r: spi dma receive callback function - * @arg: point to wmt_spi - **/ -static void spi_dsr_r(void *arg) -{ - struct wmt_spi *spi = (struct wmt_spi *)arg; - struct wmt_spi_dma *spi_dma = spi->spi_dma_info; - spi_dma->rx_ack = 1; - wake_up_interruptible(&spi_dma->rx_event); -} - -/* - * wmt_spi_dma_write_read: spi transfer (use DMA mode) - * @spi_dev: Master side proxy for an SPI slave device - * @t: spi transfer1 - **/ -static int -wmt_spi_dma_write_read(struct spi_device *spi_dev, struct spi_transfer *t) -{ - struct wmt_spi *wmt_spi = spi_master_get_devdata(spi_dev->master); - struct wmt_spi_dma *spi_dma = wmt_spi->spi_dma_info; - wait_queue_head_t *event = &spi_dma->tx_event; - volatile int *ack = &spi_dma->tx_ack; - unsigned int transfered_cnt = 0; - u32 ctrl; - - /* spi dma transfer need cs inactive first*/ - wmt_spi_cs_inactive(spi_dev, wmt_spi); - if (t->speed_hz > 15000000) - spi_set_clock_div(wmt_spi, 15000000); - ctrl = spi_get_reg32(wmt_spi, SPI_CR); - ctrl |= SPI_CR_DRC_MASK | SPI_CR_RFTS_MASK | SPI_CR_TFTS_MASK; - spi_set_reg32(wmt_spi, SPI_CR, ctrl); - /* reset spi fifo */ - if (spi_reset(wmt_spi)) - goto out; - /* tx dma buffer prepare */ - if (t->tx_buf) - memcpy(spi_dma->io_waddr, t->tx_buf, t->len); - /* tx dma request */ - if (wmt_request_dma(&spi_dma->tx_ch, "wmt_spi_tx", - spi_dma->tx_config.DeviceReqType, - spi_dsr_w, wmt_spi)) { - dev_err(&spi_dev->dev, "SPI request TX DMA failed\n"); - goto out; - } - /* rx dma request and start */ - if (t->rx_buf) { - if (wmt_request_dma(&spi_dma->rx_ch, "wmt_spi_rx", - spi_dma->rx_config.DeviceReqType, - spi_dsr_r, wmt_spi)) { - dev_err(&spi_dev->dev, "SPI request RX DMA failed\n"); - goto free_tx_dma; - } - wmt_setup_dma(spi_dma->rx_ch, spi_dma->rx_config); - wmt_start_dma(spi_dma->rx_ch, spi_dma->phys_raddr, 0x00, t->len); - event = &spi_dma->rx_event; - ack = &spi_dma->rx_ack; - } - /* transmit dma setup and start */ - wmt_setup_dma(spi_dma->tx_ch, spi_dma->tx_config); - wmt_start_dma(spi_dma->tx_ch, spi_dma->phys_waddr, 0x00, t->len + 7); - /* enable spi and active chipselect signal */ - msleep(2); - spi_enable(wmt_spi); - /* waitting for transmit finish */ - msleep(2); - wmt_spi_cs_active(spi_dev, wmt_spi); - /* waitting transfer finish */ - if (!wait_event_interruptible_timeout(*event, *ack, 100)) { - dev_err(&spi_dev->dev, "SPI DMA transfer failed\n"); - goto out; - } - transfered_cnt = t->len; - /* if RX buf is not empty, copy received data from dma to t->rx_buf */ - if (t->rx_buf) { - memcpy(t->rx_buf, spi_dma->io_raddr, t->len); - wmt_free_dma(spi_dma->rx_ch); - memset(spi_dma->io_raddr, 0x00, t->len); - } -free_tx_dma: - wmt_free_dma(spi_dma->tx_ch); - memset(spi_dma->io_waddr, 0x00, t->len); -out: - spi_disable(wmt_spi); - spi_dma->rx_ack = spi_dma->tx_ack = 0; - return transfered_cnt; -} - -/* - * wmt_spi_work: wmt spi controller work queue routine function. - * Check every message which queued in workqueue (wmt_spi->queue), - * if all things correct, message dequeued and begin transfer routine - * @work: work which queued in spi controller's work queue - */ -static void wmt_spi_work(struct work_struct *work) -{ - struct wmt_spi *wmt_spi; - struct wmt_spi_slave *spi_ctrl; - - wmt_spi = container_of(work, struct wmt_spi, work); - spin_lock_irq(&wmt_spi->spinlock); - - while (!list_empty(&wmt_spi->queue)) { - int status = 0; - u8 cs_active = 0; - struct spi_message *m = NULL; - struct spi_transfer *t = NULL; - struct spi_device *spi; - - m = container_of(wmt_spi->queue.next, struct spi_message, queue); - spi_ctrl = m->spi->controller_data; - - list_del_init(&m->queue); - spin_unlock_irq(&wmt_spi->spinlock); - - spi = m->spi; - status = wmt_spi_regs_config(m->spi); - if (status < 0) - goto msg_done; - /* check every transfer which queued in message, - * if all things right, begin data transfer */ - list_for_each_entry(t, &m->transfers, transfer_list) { - spi_reset_transfer_speed(wmt_spi, t); - - if (!cs_active) { - wmt_spi_cs_active(spi, wmt_spi); - cs_active = 1; - } - - /* data transfer begins here */ - if (t->len) { - if (wmt_spi->spi_hw_info->dma_support && m->is_dma_mapped - && spi_ctrl->dma_en && t->len > SPI_FIFO_SIZE) { - m->actual_length += wmt_spi_dma_write_read(m->spi, t); - } else { - m->actual_length += wmt_spi_write_read(m->spi, t); - } - } - /* some device need this feature support */ - if (t->delay_usecs) - udelay(t->delay_usecs); - - /* if cs need change in next transfer, just inactive it */ - if (t->cs_change) { - wmt_spi_cs_inactive(spi, wmt_spi); - cs_active = 0; - } - } - -msg_done: - if (status < 0) - dev_err(&wmt_spi->master->dev, "SPI transfer error!\n"); - if (cs_active) - wmt_spi_cs_inactive(spi, wmt_spi); - m->status = status; - if (m->complete && m->context) - m->complete(m->context); - spin_lock_irq(&wmt_spi->spinlock); - } - spin_unlock(&wmt_spi->spinlock); -} - -/* - * bits_per_word_is_support: check if spi controller can support - * this bits_per_word type or not - * @spi: spi controller's driver data - * @bit_per_word: bits_per_word type will be check - */ -static int -bits_per_word_is_support(struct wmt_spi *spi, u8 bit_per_word) -{ - int ret = 0; - struct wmt_spi_hw *hw_info = spi->spi_hw_info; - - if ((bit_per_word != 8) && (bit_per_word != 16 )) { - ret = -EINVAL; - goto err; - } - - if ((bit_per_word == 8) && - !(hw_info->bits_per_word_en & BITS8_PER_WORD_EN)) { - ret = -EINVAL; - goto err; - } - - if ((bit_per_word == 16) && - !(hw_info->bits_per_word_en & BITS16_PER_WORD_EN)) { - ret = -EINVAL; - goto err; - } -err: - return ret; -} - -/* - * speed_hz_is_support: check spi controller can support this - * frequency or not - * @spi: spi controller's driver data - * @speed_hz: frequency spi controller will working with - */ -static int speed_hz_is_support(struct wmt_spi *spi, u32 speed_hz) -{ - int ret = 0; - struct wmt_spi_hw *hw_info = spi->spi_hw_info; - - if (speed_hz < hw_info->min_freq_hz) { - ret = -EINVAL; - goto err; - } - - if (speed_hz > hw_info->max_freq_hz) { - ret = -EINVAL; - } - -err: - return ret; -} - -/* - * wmt_spi_transfer: check transfers queued in spi message, - * then queued spi message in workqueue - *@spi_dev: Master side proxy for an SPI slave device - * @m: spi message which will be added to the queue - */ -/* bidirectional bulk transfers - * - * + The transfer() method may not sleep; its main role is - * just to add the message to the queue. - * + For now there's no remove-from-queue operation, or - * any other request management - * + To a given spi_device, message queueing is pure fifo - * - * + The master's main job is to process its message queue, - * selecting a chip then transferring data - * + If there are multiple spi_device children, the i/o queue - * arbitration algorithm is unspecified (round robin, fifo, - * priority, reservations, preemption, etc) - * - * + Chipselect stays active during the entire message - * (unless modified by spi_transfer.cs_change != 0). - * + The message transfers use clock and SPI mode parameters - * previously established by setup() for this device - */ -static int -wmt_spi_transfer(struct spi_device *spi_dev, struct spi_message *m) -{ - struct wmt_spi *wmt_spi = spi_master_get_devdata(spi_dev->master); - struct spi_transfer *t = NULL; - unsigned long flags = 0; - - m->actual_length = 0; - m->status = 0; - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers) || !m->complete) { - //if (list_empty(&m->transfers)) { // for none sleep - dev_err(&spi_dev->dev, "msg rejected: invalid message of transfer\n"); - goto msg_reject; - } - /* transfers members bits_per_word and speed_hz checking and setting */ - list_for_each_entry(t, &m->transfers, transfer_list) { - if ((!t->tx_buf) && (!t->rx_buf) && t->len) { - dev_err(&spi_dev->dev, "msg rejected: invalid message data\n"); - goto msg_reject; - } - - /* checking transfer length */ - if (t->len > wmt_spi->spi_hw_info->max_transfer_length) { - dev_err(&spi_dev->dev, "msg rejected: transfer lenth can not be"\ - "bigger than %d, please split it into smaller chunks"\ - "and submit them separately\n", - wmt_spi->spi_hw_info->max_transfer_length); - goto msg_reject; - } - - /* checking and resetting transfer bits_per_word */ - if (!t->bits_per_word - || bits_per_word_is_support(wmt_spi, t->bits_per_word)) - t->bits_per_word = spi_dev->bits_per_word; - - /* checking and resetting transfer speed_hz */ - if (!t->speed_hz || speed_hz_is_support(wmt_spi, t->speed_hz)) - t->speed_hz = spi_dev->max_speed_hz; - } - spin_lock_irqsave(&wmt_spi->spinlock, flags); - /* add this message into spi's queue */ - list_add_tail(&m->queue, &wmt_spi->queue); - /* doing message transfer routine in spi work */ - queue_work(wmt_spi->workqueue, &wmt_spi->work); - spin_unlock_irqrestore(&wmt_spi->spinlock, flags); - return 0; - -msg_reject: - m->status = -EINVAL; - if (m->complete) - m->complete(m->context); - return -EINVAL; -} - -/* - * wmt_spi_setup: updates the device mode, bits_per_word and clocking - * records used by a device's SPI controller; protocol code may call - * this. This must fail if an unrecognized or unsupported mode is - * requested.It's always safe to call this unless transfers are pending - * on the device whose settings are being modified. - * @spi_dev: Master side proxy for an SPI slave device - */ -#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_LOOP | SPI_LSB_FIRST) -static int wmt_spi_setup(struct spi_device *spi_dev) -{ - int ret = 0; - struct wmt_spi *wmt_spi; - struct wmt_spi_slave *slave_info; - struct wmt_spi_hw *hw_info; - - wmt_spi = spi_master_get_devdata(spi_dev->master); - slave_info = spi_dev->controller_data; - hw_info = wmt_spi->spi_hw_info; - - /* mode checking */ - if (spi_dev->mode & ~MODEBITS) { - dev_err(&spi_dev->dev, "SPI unsupported this mode 0x%08x\n", - spi_dev->mode & ~MODEBITS); - ret = -EINVAL; - goto err; - } - - /* bits_per_word checking */ - if (!spi_dev->bits_per_word) - spi_dev->bits_per_word = slave_info->bits_per_word; - - if (spi_dev->bits_per_word) { - if (bits_per_word_is_support(wmt_spi, spi_dev->bits_per_word)) { - if (bits_per_word_is_support(wmt_spi, - slave_info->bits_per_word)) { - dev_err(&spi_dev->dev, "SPI unsupport %d and %dbits_per_word\n", - spi_dev->bits_per_word, slave_info->bits_per_word); - ret = -EINVAL; - goto err; - } - spi_dev->bits_per_word = slave_info->bits_per_word; - } - } - - /* max_speed_hz checking */ - if ((spi_dev->max_speed_hz == 0) || - (spi_dev->max_speed_hz > hw_info->max_freq_hz)) - spi_dev->max_speed_hz = hw_info->max_freq_hz; - - if (spi_dev->max_speed_hz < hw_info->min_freq_hz) { - dev_err(&spi_dev->dev, "SPI unspport speed lower than %dhz\n", - hw_info->min_freq_hz); - ret = -EINVAL; - goto err; - } - return 0; - -err: - dev_err(&spi_dev->dev, "SPI setup spi device failed!\n"); - return ret; -} - -/* - * wmt_spi_cleanup: called on module_exit to free memory - * provided by spi_master - * @wmt_spi: spi controller driver data - */ -static void wmt_spi_cleanup(struct wmt_spi *wmt_spi) -{ - if (wmt_spi->spi_hw_info) - kfree(wmt_spi->spi_hw_info); - wmt_spi->spi_hw_info = NULL; - - if (wmt_spi->spi_dma_info) - kfree(wmt_spi->spi_dma_info); - wmt_spi->spi_dma_info = NULL; -} - -/* - * spi_get_hw_info: get spi controller's configuration infomation - * form platform device - * @pdev: spi platform device structure - */ -static inline void *spi_get_hw_info(struct platform_device *pdev) -{ - return pdev->dev.platform_data; -} - -/* - * wmt_spi_dma_init: request spi dma memory and initialize spi dma info - if dma supported - * @spi: spi controller's driver data - */ -static int wmt_spi_dma_init(struct wmt_spi *spi) -{ - int ret = 0; - unsigned int dma_size; - - if (!spi->spi_hw_info) - goto out; - if (!spi->spi_hw_info->dma_support) - goto out; - spi->spi_dma_info = kmalloc(sizeof(struct wmt_spi_dma), GFP_KERNEL); - if (!spi->spi_dma_info) { - ret = -ENOMEM; - dev_err(&spi->pdev->dev, "SPI allocating dma info memory failed\n"); - goto out; - } - dma_size = spi->spi_hw_info->max_transfer_length; - /* dma read config */ - spi->spi_dma_info->rx_ch = ~0UL; - spi->spi_dma_info->rx_config.ChunkSize = SPI_DMA_CHUNK_SIZE; - spi->spi_dma_info->rx_config.DefaultCCR = SPI_RX_DMA_CFG; - spi->spi_dma_info->rx_config.DeviceReqType = SPI0_DMA_RX_REQ; - spi->spi_dma_info->rx_config.MIF1addr = SPI0_BASE_ADDR + SPI_RXFIFO; - spi->spi_dma_info->io_raddr = dma_alloc_coherent(&spi->pdev->dev, - dma_size, - &spi->spi_dma_info->phys_raddr, - GFP_KERNEL | GFP_DMA); - if (!spi->spi_dma_info->io_raddr) { - ret = -ENOMEM; - dev_err(&spi->pdev->dev, "SPI allocate rdma failed\n"); - goto out; - } - memset(spi->spi_dma_info->io_raddr, 0x00, dma_size); - spi->spi_dma_info->rx_config.MIF0addr = (ulong)spi->spi_dma_info->io_raddr; - init_waitqueue_head(&spi->spi_dma_info->rx_event); - spi->spi_dma_info->rx_ack = 0; - /* dma write config */ - spi->spi_dma_info->tx_ch = ~0UL; - spi->spi_dma_info->tx_config.ChunkSize = SPI_DMA_CHUNK_SIZE; - spi->spi_dma_info->tx_config.DefaultCCR = SPI_TX_DMA_CFG; - spi->spi_dma_info->tx_config.DeviceReqType = SPI0_DMA_TX_REQ; - spi->spi_dma_info->tx_config.MIF1addr = SPI0_BASE_ADDR + SPI_TXFIFO; - spi->spi_dma_info->io_waddr = dma_alloc_coherent(&spi->pdev->dev, - dma_size + 7, - &spi->spi_dma_info->phys_waddr, - GFP_KERNEL | GFP_DMA); - if (!spi->spi_dma_info->io_waddr) { - ret = -ENOMEM; - dev_err(&spi->pdev->dev, "SPI allocate wdma failed\n"); - goto free_spi_rx_dma; - } - memset(spi->spi_dma_info->io_waddr, 0x00, dma_size + 7); - spi->spi_dma_info->tx_config.MIF0addr = (ulong)spi->spi_dma_info->io_waddr; - init_waitqueue_head(&spi->spi_dma_info->tx_event); - spi->spi_dma_info->tx_ack = 0; - return 0; - -free_spi_rx_dma: - dma_free_coherent(&spi->pdev->dev,dma_size,spi->spi_dma_info->io_raddr, - spi->spi_dma_info->phys_raddr); -out: - return ret; -} - -/* - * wmt_spi_dma_release: release spi dma memory if dma supported - * @spi: spi controller's driver data - */ -static void wmt_spi_dma_release(struct wmt_spi *spi) -{ - if (!spi->spi_hw_info) - goto out; - if (!spi->spi_hw_info->dma_support) - goto out; - - dma_free_coherent(&spi->pdev->dev, - spi->spi_hw_info->max_transfer_length, - spi->spi_dma_info->io_raddr, - spi->spi_dma_info->phys_raddr); - dma_free_coherent(&spi->pdev->dev, - spi->spi_hw_info->max_transfer_length + 7, - spi->spi_dma_info->io_waddr, - spi->spi_dma_info->phys_waddr); -out: - return ; -} - -static int __devinit wmt_spi_probe(struct platform_device *pdev) -{ - int ret; - struct device *dev = &pdev->dev; - struct spi_master *master; - struct wmt_spi *wmt_spi; - struct wmt_spi_hw *wmt_dev_info; - struct resource *res; - - /* SPI master allocation */ - master = spi_alloc_master(dev, sizeof(struct wmt_spi)); - if (!master) { - ret = -ENOMEM; - dev_err(dev, "SPI master allocation failed!\n"); - goto the_end; - } - - /* spi controller structure initialization */ - wmt_spi = (struct wmt_spi *)spi_master_get_devdata(master); - memset(wmt_spi, 0x00, sizeof(struct wmt_spi)); - wmt_spi->master = spi_master_get(master); - wmt_spi->pdev = pdev; - wmt_dev_info = spi_get_hw_info(pdev); - wmt_spi->spi_hw_info = kmalloc(sizeof(struct wmt_spi_hw), GFP_KERNEL); - if (!wmt_spi->spi_hw_info) { - dev_err(dev, "SPI allocating hardware info memory failed\n"); - ret = -ENOMEM; - goto release_master; - } - memset(wmt_spi->spi_hw_info, 0x00, sizeof(struct wmt_spi_hw)); - memcpy(wmt_spi->spi_hw_info, wmt_dev_info, - sizeof(struct wmt_spi_hw)); - - /* spi master initialization */ - master->bus_num = pdev->id; - master->num_chipselect = wmt_dev_info->num_chipselect; - master->setup = wmt_spi_setup; - master->transfer = wmt_spi_transfer; - - /* the spi->mode bits understood by this driver: */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - - platform_set_drvdata(pdev, wmt_spi); - - /* device resource request */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - ret = -ENXIO; - dev_err(dev, "SPI getting platform resource failed\n"); - goto release_hw_info; - } -#if 0 - if (!request_mem_region(res->start, res->end-res->start+1, pdev->name)) { - ret = -ENXIO; - dev_err(dev, "SPI request mem region failed\n"); - goto release_hw_info; - } - wmt_spi->regs_base = ioremap(res->start, res->end - res->start + 1); -#endif - wmt_spi->regs_base = (void __iomem *)SPI0_BASE_ADDR; - if (!wmt_spi->regs_base) { - ret = -ENXIO; - dev_err(dev, "SPI ioremap failed!\n"); - goto release_region; - } - - if (wmt_dev_info->dma_support) { - if (wmt_spi_dma_init(wmt_spi)) { - ret = -ENXIO; - wmt_dev_info->dma_support = 0; - dev_err(dev, "SPI dma init failed\n"); - goto release_ioremap; - } - } - - /* work queue create */ - spin_lock_init(&wmt_spi->spinlock); - INIT_LIST_HEAD(&wmt_spi->queue); - INIT_WORK(&wmt_spi->work, wmt_spi_work); - init_waitqueue_head(&wmt_spi->waitq); - wmt_spi->workqueue = create_singlethread_workqueue("wmt_spi"); - if (NULL == wmt_spi->workqueue) { - ret = -ENXIO; - dev_err(dev, "SPI create workqueue failed!\n"); - goto release_dma; - } - /* power and clock open */ - wmt_spi_clock_enable(); - - /* registe spi master at last */ - ret = spi_register_master(master); - if (ret) { - dev_err(dev, "SPI register master failed\n"); - goto release_workqueue; - } - - printk(KERN_INFO "WMT EVB SPI Controlor Driver OK!\n"); - return 0; - -release_workqueue: - wmt_spi_clock_disable(); - destroy_workqueue(wmt_spi->workqueue); -release_dma: - wmt_spi_dma_release(wmt_spi); -release_ioremap: - iounmap(wmt_spi->regs_base); -release_region: - release_mem_region(res->start, res->end - res->start + 1); -release_hw_info: - kfree(wmt_spi->spi_hw_info); - platform_set_drvdata(pdev, NULL); -release_master: - spi_master_put(master); -the_end: - dev_err(dev, "WMT EVB SPI Controlor Probe Failed!\n"); - return ret; -} - -static int wmt_spi_stop_queue(struct wmt_spi *spi) -{ - unsigned long flags; - unsigned limit = 500; - int status = 0; - - spin_lock_irqsave(&spi->spinlock, flags); - - while (!list_empty(&spi->queue) && limit--) { - spin_unlock_irqrestore(&spi->spinlock, flags); - msleep(10); - spin_lock_irqsave(&spi->spinlock, flags); - } - - if (!list_empty(&spi->queue)) - status = -EBUSY; - - spin_unlock_irqrestore(&spi->spinlock, flags); - - return status; -} - -static int __devexit wmt_spi_remove(struct platform_device *pdev) -{ - struct wmt_spi *spi; - struct wmt_spi_hw *spi_hw_info; - struct resource *res; - - spi = (struct wmt_spi *)platform_get_drvdata(pdev); - spi_hw_info = spi_get_hw_info(pdev); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - - /* stop spi message queue */ - wmt_spi_stop_queue(spi); - /* SSN inactive */ - /* - wmt_spi_cs_inactive(spi); - */ - spi_disable(spi); - /* clock and power off */ - wmt_spi_clock_disable(); - /* work queue flush and destory */ - flush_workqueue(spi->workqueue); - destroy_workqueue(spi->workqueue); - /* dma free if dma support and enable */ - wmt_spi_dma_release(spi); - /* irq free */ - if (spi->irq) - free_irq(spi->irq, spi); - /* release requested resource */ - iounmap(spi->regs_base); - release_mem_region(res->start, res->end - res->start + 1); - /* driver data entry reset */ - platform_set_drvdata(pdev, NULL); - /* free memory provide by spi master */ - wmt_spi_cleanup(spi); - /* spi master unregister and free */ - spi_unregister_master(spi->master); - spi_master_put(spi->master); - - return 0; -} - -#ifdef CONFIG_PM -static int -wmt_spi_suspend(struct platform_device *pdev, pm_message_t state) -{ - int ret = 0; - struct wmt_spi *spi = platform_get_drvdata(pdev); - state = state; - - spi_dbg("Enter\n"); - ret = wmt_spi_stop_queue(spi); - if (ret) { - dev_warn(&pdev->dev, "suspend wmt spi failed\n"); - return ret; - } - /* - wmt_spi_cs_inactive(spi); - */ - spi_disable(spi); - - spi_dbg("Exit\n"); - return 0; -} - -static int wmt_spi_resume(struct platform_device *pdev) -{ - struct wmt_spi *spi = platform_get_drvdata(pdev); - - spi_dbg("Enter\n"); - /* hardware reset */ - wmt_spi_clock_enable(); - /* start msg queue */ - queue_work(spi->workqueue, &spi->work); - - spi_dbg("Exit\n"); - return 0; -} -#else -#define wmt_spi_suspend NULL -#define wmt_spi_resume NULL -#endif /* CONFIG_PM */ - -static struct platform_driver wmt_spi_driver = { - .driver = { - .name = "wmt_spi_0", - .owner = THIS_MODULE, - }, - .suspend = wmt_spi_suspend, - .resume = wmt_spi_resume, - .remove = __devexit_p(wmt_spi_remove), -}; - -static int __init wmt_spi_init(void) -{ - return platform_driver_probe(&wmt_spi_driver, wmt_spi_probe); -} -module_init(wmt_spi_init); - -static void __exit wmt_spi_exit(void) -{ - platform_driver_unregister(&wmt_spi_driver); -} -module_exit(wmt_spi_exit); - -MODULE_AUTHOR("WonderMedia Technologies, Inc."); -MODULE_DESCRIPTION("WMT SPI Controller Driver"); -MODULE_LICENSE("GPL"); |