From patchwork Mon Jul 13 16:14:12 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: s-paulraj@ti.com X-Patchwork-Id: 35395 Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n6DGHKFR017714 for ; Mon, 13 Jul 2009 16:17:21 GMT Received: from dlep36.itg.ti.com ([157.170.170.91]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n6DGFQYn004704; Mon, 13 Jul 2009 11:15:31 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep36.itg.ti.com (8.13.8/8.13.8) with ESMTP id n6DGFPFW011141; Mon, 13 Jul 2009 11:15:25 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id B6D75806A9; Mon, 13 Jul 2009 11:14:25 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dlep35.itg.ti.com (dlep35.itg.ti.com [157.170.170.118]) by linux.omap.com (Postfix) with ESMTP id 98135807AF for ; Mon, 13 Jul 2009 11:14:15 -0500 (CDT) Received: from legion.dal.design.ti.com (localhost [127.0.0.1]) by dlep35.itg.ti.com (8.13.7/8.13.7) with ESMTP id n6DGEDbA014590; Mon, 13 Jul 2009 11:14:14 -0500 (CDT) Received: from gt5d9d821.telogy.design.ti.com (gt5d9d821.telogy.design.ti.com [158.218.100.23]) by legion.dal.design.ti.com (8.11.7p1+Sun/8.11.7) with ESMTP id n6DGEC917639; Mon, 13 Jul 2009 11:14:12 -0500 (CDT) Received: from gt5d9d821.telogy.design.ti.com (localhost.localdomain [127.0.0.1]) by gt5d9d821.telogy.design.ti.com (8.13.1/8.13.1) with ESMTP id n6DGECQn002578; Mon, 13 Jul 2009 12:14:12 -0400 Received: (from a0866907@localhost) by gt5d9d821.telogy.design.ti.com (8.13.1/8.13.1/Submit) id n6DGECKk002575; Mon, 13 Jul 2009 12:14:12 -0400 From: s-paulraj@ti.com To: davinci-linux-open-source@linux.davincidsp.com Date: Mon, 13 Jul 2009 12:14:12 -0400 Message-Id: <1247501652-2549-1-git-send-email-s-paulraj@ti.com> X-Mailer: git-send-email 1.6.0.4 Cc: Subject: [PATCH 6/6] DaVinci: Adding SPI driver for DM3xx/DM6467/DA8xx X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com From: Sandeep Paulraj The patch adds support for SPI driver for DaVinci DM355/DM365/DM6467 and DA8xx Signed-off-by: Sandeep Paulraj --- drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/davinci_spi.c | 809 +++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/davinci_spi.h | 194 +++++++++++ 4 files changed, 1012 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/davinci_spi.c create mode 100644 drivers/spi/davinci_spi.h diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 2c733c2..443a879 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -77,6 +77,14 @@ config SPI_AU1550 This driver can also be built as a module. If so, the module will be called au1550_spi. +config SPI_DAVINCI + tristate "SPI controller driver for DaVinci SoC" + depends on SPI_MASTER && ARCH_DAVINCI + select SPI_BITBANG + default y + help + SPI master controller for DaVinci SPI modules. + config SPI_BITBANG tristate "Utilities for Bitbanging SPI masters" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 3de408d..592b1d5 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o obj-$(CONFIG_SPI_SH_SCI) += spi_sh_sci.o +obj-$(CONFIG_SPI_DAVINCI) += davinci_spi.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/davinci_spi.c b/drivers/spi/davinci_spi.c new file mode 100644 index 0000000..d1370c3 --- /dev/null +++ b/drivers/spi/davinci_spi.c @@ -0,0 +1,809 @@ +/* + * Copyright (C) 2009 Texas Instruments. + * + * controller driver with Interrupt. + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "davinci_spi.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static inline void davinci_spi_rx_buf_u8(u32 data, + struct davinci_spi *davinci_spi) +{ + u8 *rx = davinci_spi->rx; + + *rx++ = (u8)data; + davinci_spi->rx = rx; +} + +static inline void davinci_spi_rx_buf_u16(u32 data, + struct davinci_spi *davinci_spi) +{ + u16 *rx = davinci_spi->rx; + + *rx++ = (u16)data; + davinci_spi->rx = rx; +} + +static inline u32 davinci_spi_tx_buf_u8(struct davinci_spi *davinci_spi) +{ + u32 data; + const u8 *tx = davinci_spi->tx; + + data = *tx++; + davinci_spi->tx = tx; + return data; +} + +static inline u32 davinci_spi_tx_buf_u16(struct davinci_spi *davinci_spi) +{ + u32 data; + const u16 *tx = davinci_spi->tx; + + data = *tx++; + davinci_spi->tx = tx; + return data; +} + +static inline void set_bits(void __iomem *addr, u32 bits) +{ + u32 v = ioread32(addr); + v |= bits; + iowrite32(v, addr); +} + +static inline void clear_bits(void __iomem *addr, u32 bits) +{ + u32 v = ioread32(addr); + v &= ~bits; + iowrite32(v, addr); +} + +static inline void set_fmt_bits(void __iomem *addr, u32 bits, int bus_num) +{ + set_bits(addr + SPIFMT0 + (0x4 * bus_num), bits); +} + +static inline void clear_fmt_bits(void __iomem *addr, u32 bits, int bus_num) +{ + clear_bits(addr + SPIFMT0 + (0x4 * bus_num), bits); +} + +/* + * Interface to control the chip select signal + */ +static void davinci_spi_chipselect(struct spi_device *spi, int value) +{ + struct davinci_spi *davinci_spi; + u32 data1_reg_val = 0; + struct davinci_spi_platform_data *pdata; + int i; + + davinci_spi = spi_master_get_devdata(spi->master); + pdata = davinci_spi->pdata; + + /* + * Board specific chip select logic decides the polarity and cs + * line for the controller + */ + if (value == BITBANG_CS_INACTIVE) { + /* set all chip select high */ + if (pdata->chip_sel != NULL) { + for (i = 0; i < pdata->num_chipselect; i++) { + if (pdata->chip_sel[i] != DAVINCI_SPI_INTERN_CS) + __gpio_set_value(pdata->chip_sel[i], 1); + } + } + + set_bits(davinci_spi->base + SPIDEF, CS_DEFAULT); + + data1_reg_val |= CS_DEFAULT << DAVINCI_SPIDAT1_CSNR_SHIFT; + iowrite32(data1_reg_val, davinci_spi->base + SPIDAT1); + + while (1) + if (ioread32(davinci_spi->base + SPIBUF) + & DAVINCI_SPIBUF_RXEMPTY_MASK) + break; + } +} + +/** + * davinci_spi_setup_transfer - This functions will determine transfer method + * @spi: spi device on which data transfer to be done + * @t: spi transfer in which transfer info is filled + * + * This function determines data transfer method (8/16/32 bit transfer). + * It will also set the SPI Clock Control register according to + * SPI slave device freq. + */ +static int davinci_spi_setup_transfer(struct spi_device *spi, + struct spi_transfer *t) +{ + + struct davinci_spi *davinci_spi; + struct davinci_spi_platform_data *pdata; + u8 bits_per_word = 0; + u32 hz = 0, prescale; + + davinci_spi = spi_master_get_devdata(spi->master); + pdata = davinci_spi->pdata; + + if (t) { + bits_per_word = t->bits_per_word; + hz = t->speed_hz; + } + + /* if bits_per_word is not set then set it default */ + if (!bits_per_word) + bits_per_word = spi->bits_per_word; + + /* Assign function pointer to appropriate transfer method */ + /* 8bit/16bit or 32bit transfer */ + if (bits_per_word <= 8 && bits_per_word >= 2) { + davinci_spi->get_rx = davinci_spi_rx_buf_u8; + davinci_spi->get_tx = davinci_spi_tx_buf_u8; + davinci_spi->slave[spi->chip_select].bytes_per_word = 1; + } else if (bits_per_word <= 16 && bits_per_word >= 2) { + davinci_spi->get_rx = davinci_spi_rx_buf_u16; + davinci_spi->get_tx = davinci_spi_tx_buf_u16; + davinci_spi->slave[spi->chip_select].bytes_per_word = 2; + } else + return -1; + + if (!hz) + hz = spi->max_speed_hz; + + clear_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_CHARLEN_MASK, + spi->master->bus_num); + set_fmt_bits(davinci_spi->base, bits_per_word & 0x1f, + spi->master->bus_num); + + prescale = ((clk_get_rate(davinci_spi->clk) / hz) - 1) & 0xff; + + clear_fmt_bits(davinci_spi->base, 0x0000ff00, spi->master->bus_num); + set_fmt_bits(davinci_spi->base, prescale << 8, spi->master->bus_num); + + return 0; +} + +/** + * davinci_spi_setup - This functions will set default transfer method + * @spi: spi device on which data transfer to be done + * + * This functions sets the default transfer method. + */ + +static int davinci_spi_setup(struct spi_device *spi) +{ + int retval; + struct davinci_spi *davinci_spi; + + davinci_spi = spi_master_get_devdata(spi->master); + + /* if bits per word length is zero then set it default 8 */ + if (!spi->bits_per_word) + spi->bits_per_word = 8; + + /* + * SPI in DaVinci and DA8xx operate between + * 600 KHz and 50 MHz + */ + if (spi->max_speed_hz < 600000 || spi->max_speed_hz > 50000000) + return -EINVAL; + + retval = davinci_spi_setup_transfer(spi, NULL); + + return retval; +} + +static int davinci_spi_bufs_prep(struct spi_device *spi, + struct davinci_spi *davinci_spi) +{ + int op_mode = 0; + + /* configuraton parameter for SPI */ + if (spi->mode & SPI_LSB_FIRST) + set_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_SHIFTDIR_MASK, + spi->master->bus_num); + else + clear_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_SHIFTDIR_MASK, + spi->master->bus_num); + + if (spi->mode & SPI_CPOL) + set_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_POLARITY_MASK, + spi->master->bus_num); + else + clear_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_POLARITY_MASK, + spi->master->bus_num); + + if (spi->mode & SPI_CPHA) + set_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_PHASE_MASK, + spi->master->bus_num); + else + clear_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_PHASE_MASK, + spi->master->bus_num); + + if (davinci_spi->version == DAVINCI_SPI_VERSION_2) { + clear_fmt_bits(davinci_spi->base, DAVINCI_SPIFMT_WDELAY_MASK, + spi->master->bus_num); + set_fmt_bits(davinci_spi->base, + ((davinci_spi->pdata->wdelay << + DAVINCI_SPIFMT_WDELAY_SHIFT) & + DAVINCI_SPIFMT_WDELAY_MASK), + spi->master->bus_num); + + if (davinci_spi->pdata->odd_parity) + set_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_ODD_PARITY_MASK, + spi->master->bus_num); + else + clear_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_ODD_PARITY_MASK, + spi->master->bus_num); + + if (davinci_spi->pdata->parity_enable) + set_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_PARITYENA_MASK, + spi->master->bus_num); + else + clear_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_PARITYENA_MASK, + spi->master->bus_num); + + if (davinci_spi->pdata->wait_enable) + set_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_WAITENA_MASK, + spi->master->bus_num); + else + clear_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_WAITENA_MASK, + spi->master->bus_num); + + if (davinci_spi->pdata->timer_disable) + set_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_DISTIMER_MASK, + spi->master->bus_num); + else + clear_fmt_bits(davinci_spi->base, + DAVINCI_SPIFMT_DISTIMER_MASK, + spi->master->bus_num); + } + + /* Clock internal */ + if (davinci_spi->pdata->clk_internal) + set_bits(davinci_spi->base + SPIGCR1, + DAVINCI_SPIGCR1_CLKMOD_MASK); + else + clear_bits(davinci_spi->base + SPIGCR1, + DAVINCI_SPIGCR1_CLKMOD_MASK); + + /* master mode default */ + set_bits(davinci_spi->base + SPIGCR1, DAVINCI_SPIGCR1_MASTER_MASK); + + if (davinci_spi->pdata->intr_level) + iowrite32(DAVINCI_SPI_INTLVL_1, davinci_spi->base + SPILVL); + else + iowrite32(DAVINCI_SPI_INTLVL_0, davinci_spi->base + SPILVL); + + /* + * The driver uses new flags SPI_NO_CS and SPI_RAEDY + * These can be found in /include/linux/spi/spi.h + * Standard SPI mode is a 4 pin variant + * 3 pin SPI is actually the 4 pin variant with no CS(SPI_NO_CS) + * 4 pin with enable is (SPI_READY | SPI_NO_CS) + * 5 pin SPI variant is SPI_READY + * Operation mode(op_mode) depends on these flags + * op_mode 0 = standard 4 pin mode + * op_mode 1 = 3 pin mode + * op_mode 2 = 5 pin mode + * op_mode 3 = 4 pin mode with enable + */ + + op_mode = ((spi->mode & (SPI_NO_CS | SPI_READY)) >> 6); + + switch (op_mode) { + case 0: + op_mode = (DAVINCI_SPIPC0_DIFUN_DI << + DAVINCI_SPIPC0_DIFUN_SHIFT) + | (DAVINCI_SPIPC0_DOFUN_DO << + DAVINCI_SPIPC0_DOFUN_SHIFT) + | (DAVINCI_SPIPC0_CLKFUN_CLK << + DAVINCI_SPIPC0_CLKFUN_SHIFT) + | (1 << spi->chip_select); + break; + + case 1: + op_mode = (DAVINCI_SPIPC0_DIFUN_DI << + DAVINCI_SPIPC0_DIFUN_SHIFT) + | (DAVINCI_SPIPC0_DOFUN_DO << + DAVINCI_SPIPC0_DOFUN_SHIFT) + | (DAVINCI_SPIPC0_CLKFUN_CLK << + DAVINCI_SPIPC0_CLKFUN_SHIFT); + break; + + case 2: + op_mode = (DAVINCI_SPIPC0_DIFUN_DI << + DAVINCI_SPIPC0_DIFUN_SHIFT) + | (DAVINCI_SPIPC0_DOFUN_DO << + DAVINCI_SPIPC0_DOFUN_SHIFT) + | (DAVINCI_SPIPC0_CLKFUN_CLK < + DAVINCI_SPIPC0_CLKFUN_SHIFT) + | (DAVINCI_SPIPC0_SPIENA << + DAVINCI_SPIPC0_SPIENA_SHIFT) + | (1 << spi->chip_select); + break; + + case 3: + op_mode = (DAVINCI_SPIPC0_DIFUN_DI << + DAVINCI_SPIPC0_DIFUN_SHIFT) + | (DAVINCI_SPIPC0_DOFUN_DO << + DAVINCI_SPIPC0_DOFUN_SHIFT) + | (DAVINCI_SPIPC0_CLKFUN_CLK << + DAVINCI_SPIPC0_CLKFUN_SHIFT) + | (DAVINCI_SPIPC0_SPIENA << + DAVINCI_SPIPC0_SPIENA_SHIFT); + break; + + default: + return -1; + } + + iowrite32(op_mode, davinci_spi->base + SPIPC0); + + if (spi->mode & SPI_LOOP) + set_bits(davinci_spi->base + SPIGCR1, + DAVINCI_SPIGCR1_LOOPBACK_MASK); + else + clear_bits(davinci_spi->base + SPIGCR1, + DAVINCI_SPIGCR1_LOOPBACK_MASK); + + return 0; +} + +static int davinci_spi_check_error(struct davinci_spi *davinci_spi, + int int_status) +{ + int ret = 0; + + if (int_status & DAVINCI_SPIFLG_TIMEOUT_MASK) { + printk(KERN_ERR "SPI Time-out Error\n"); + ret = -1; + } + if (int_status & DAVINCI_SPIFLG_DESYNC_MASK) { + printk(KERN_ERR "SPI Desynchronization Error\n"); + ret = -1; + } + if (int_status & DAVINCI_SPIFLG_BITERR_MASK) { + printk(KERN_ERR "SPI Bit error\n"); + ret = -1; + } + + if (davinci_spi->version == DAVINCI_SPI_VERSION_2) { + if (int_status & DAVINCI_SPIFLG_DLEN_ERR_MASK) { + printk(KERN_ERR "SPI Data Length Error\n"); + ret = -1; + } + if (int_status & DAVINCI_SPIFLG_PARERR_MASK) { + printk(KERN_ERR "SPI Parity Error\n"); + ret = -1; + } + if (int_status & DAVINCI_SPIFLG_OVRRUN_MASK) { + printk(KERN_ERR "SPI Data Overrun error\n"); + ret = -1; + } + if (int_status & DAVINCI_SPIFLG_TX_INTR_MASK) { + printk(KERN_ERR "SPI TX intr bit set\n"); + ret = -1; + } + if (int_status & DAVINCI_SPIFLG_BUF_INIT_ACTIVE_MASK) { + printk(KERN_ERR "SPI Buffer Init Active\n"); + ret = -1; + } + } + + return ret; +} + +/** + * davinci_spi_bufs - 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 untill the completion will be marked + * by the IRQ Handler. + */ +static int davinci_spi_bufs_pio(struct spi_device *spi, struct spi_transfer *t) +{ + struct davinci_spi *davinci_spi; + int int_status, count, ret; + u8 conv, tmp; + u32 tx_data, data1_reg_val; + u32 buf_val, flg_val; + struct davinci_spi_platform_data *pdata; + + davinci_spi = spi_master_get_devdata(spi->master); + pdata = davinci_spi->pdata; + + davinci_spi->tx = t->tx_buf; + davinci_spi->rx = t->rx_buf; + + /* convert len to words bbased on bits_per_word */ + conv = davinci_spi->slave[spi->chip_select].bytes_per_word; + davinci_spi->count = t->len / conv; + + INIT_COMPLETION(davinci_spi->done); + + ret = davinci_spi_bufs_prep(spi, davinci_spi); + if (ret) + return ret; + + /* Enable SPI */ + set_bits(davinci_spi->base + SPIGCR1, DAVINCI_SPIGCR1_SPIENA_MASK); + + iowrite32(0 | (pdata->c2tdelay << DAVINCI_SPI_C2TDELAY_SHIFT) | + (pdata->t2cdelay << DAVINCI_SPI_T2CDELAY_SHIFT), + davinci_spi->base + SPIDELAY); + + count = davinci_spi->count; + data1_reg_val = pdata->cs_hold << DAVINCI_SPIDAT1_CSHOLD_SHIFT; + tmp = ~(0x1 << spi->chip_select); + + /* checking for GPIO */ + if ((pdata->chip_sel != NULL) && + (pdata->chip_sel[spi->chip_select] != DAVINCI_SPI_INTERN_CS)) + __gpio_set_value(pdata->chip_sel[spi->chip_select], 0); + else + clear_bits(davinci_spi->base + SPIDEF, ~tmp); + + data1_reg_val |= tmp << DAVINCI_SPIDAT1_CSNR_SHIFT; + + while (1) + if (ioread32(davinci_spi->base + SPIBUF) + & DAVINCI_SPIBUF_RXEMPTY_MASK) + break; + + /* Determine the command to execute READ or WRITE */ + if (t->tx_buf) { + clear_bits(davinci_spi->base + SPIINT, DAVINCI_SPIINT_MASKALL); + + while (1) { + tx_data = davinci_spi->get_tx(davinci_spi); + + data1_reg_val &= ~(0xFFFF); + data1_reg_val |= (0xFFFF & tx_data); + + buf_val = ioread32(davinci_spi->base + SPIBUF); + if ((buf_val & DAVINCI_SPIBUF_TXFULL_MASK) == 0) { + iowrite32(data1_reg_val, + davinci_spi->base + SPIDAT1); + + count--; + } + while (ioread32(davinci_spi->base + SPIBUF) + & DAVINCI_SPIBUF_RXEMPTY_MASK) + udelay(1); + /* getting the returned byte */ + if (t->rx_buf) { + buf_val = ioread32(davinci_spi->base + SPIBUF); + davinci_spi->get_rx(buf_val, davinci_spi); + } + if (count <= 0) + break; + } + } else { + if (pdata->poll_mode) { + while (1) { + /* keeps the serial clock going */ + if ((ioread32(davinci_spi->base + SPIBUF) + & DAVINCI_SPIBUF_TXFULL_MASK) == 0) + iowrite32(data1_reg_val, + davinci_spi->base + SPIDAT1); + + while ((ioread32(davinci_spi->base + SPIBUF)) & + (DAVINCI_SPIBUF_RXEMPTY_MASK)) + ; + + flg_val = ioread32(davinci_spi->base + SPIFLG); + buf_val = ioread32(davinci_spi->base + SPIBUF); + + davinci_spi->get_rx(buf_val, davinci_spi); + + count--; + if (count <= 0) + break; + } + } else { /* Receive in Interrupt mode */ + int i; + + for (i = 0; i < davinci_spi->count; i++) { + set_bits(davinci_spi->base + SPIINT, + DAVINCI_SPIINT_BITERR_INTR + | DAVINCI_SPIINT_OVRRUN_INTR + | DAVINCI_SPIINT_RX_INTR); + + iowrite32(data1_reg_val, + davinci_spi->base + SPIDAT1); + + while (ioread32(davinci_spi->base + SPIINT) & + (DAVINCI_SPIINT_RX_INTR)) + ; + } + iowrite32((data1_reg_val & 0x0ffcffff), + davinci_spi->base + SPIDAT1); + } + } + + /* + * Check for bit error, desync error,parity error,timeout error and + * receive overflow errors + */ + int_status = ioread32(davinci_spi->base + SPIFLG); + + ret = davinci_spi_check_error(davinci_spi, int_status); + if (ret != 0) + return ret; + + /* SPI Framework maintains the count only in bytes so convert back */ + davinci_spi->count *= conv; + + return t->len; +} + +/** + * davinci_spi_irq - probe function for SPI Master Controller + * @irq: IRQ number for this SPI Master + * @context_data: structure for SPI Master controller davinci_spi + * + * ISR will determine that interrupt arrives either for READ or WRITE command. + * According to command it will do the appropriate action. It will check + * transfer length and if it is not zero then dispatch transfer command again. + * If transfer length is zero then it will indicate the COMPLETION so that + * davinci_spi_bufs function can go ahead. + */ +static irqreturn_t davinci_spi_irq(s32 irq, void *context_data) +{ + struct davinci_spi *davinci_spi = context_data; + u32 int_status, rx_data = 0; + irqreturn_t ret = IRQ_NONE; + + int_status = ioread32(davinci_spi->base + SPIFLG); + while ((int_status & DAVINCI_SPIFLG_MASK) != 0) { + ret = IRQ_HANDLED; + + if (likely(int_status & DAVINCI_SPIFLG_RX_INTR_MASK)) { + rx_data = ioread32(davinci_spi->base + SPIBUF); + davinci_spi->get_rx(rx_data, davinci_spi); + + /* Disable Receive Interrupt */ + iowrite32(~DAVINCI_SPIINT_RX_INTR, + davinci_spi->base + SPIINT); + } else /* Ignore errors if have good intr */ + (void)davinci_spi_check_error(davinci_spi, int_status); + + int_status = ioread32(davinci_spi->base + SPIFLG); + } + + return ret; +} + +/* + * davinci_spi_probe - probe function for SPI Master Controller + * @pdev: platform_device structure which contains plateform specific data + * + * According to Linux Device Model this function will be invoked by Linux + * with plateform_device struct which contains the device specific info. + * This function will map the SPI controller's memory, register IRQ, + * Reset SPI controller and setting its registers to default value. + * It will invoke spi_bitbang_start to create work queue so that client driver + * can register transfer method to work queue. + */ +static int davinci_spi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct davinci_spi *davinci_spi; + struct davinci_spi_platform_data *pdata; + struct resource *r, *mem; + int i = 0, ret = 0; + + pdata = pdev->dev.platform_data; + if (pdata == NULL) { + ret = -ENODEV; + goto err; + } + + master = spi_alloc_master(&pdev->dev, sizeof(struct davinci_spi)); + if (master == NULL) { + ret = -ENOMEM; + goto err; + } + + dev_set_drvdata(&pdev->dev, master); + + davinci_spi = spi_master_get_devdata(master); + if (davinci_spi == NULL) { + ret = -ENOENT; + goto free_master; + } + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (r == NULL) { + ret = -ENOENT; + goto free_master; + } + + davinci_spi->pbase = r->start; + davinci_spi->region_size = resource_size(r); + davinci_spi->pdata = pdata; + + /* initialize gpio used as chip select */ + if (pdata->chip_sel != NULL) { + for (i = 0; i < pdata->num_chipselect; i++) { + if (pdata->chip_sel[i] != DAVINCI_SPI_INTERN_CS) { + gpio_request(pdata->chip_sel[i], "spi"); + gpio_direction_output(pdata->chip_sel[i], 1); + } + } + } + + mem = request_mem_region(r->start, davinci_spi->region_size, + pdev->name); + if (mem == NULL) { + ret = -EBUSY; + goto free_master; + } + + davinci_spi->base = (struct davinci_spi_reg __iomem *) + ioremap(r->start, davinci_spi->region_size); + if (davinci_spi->base == NULL) { + ret = -ENOMEM; + goto release_region; + } + + davinci_spi->irq = platform_get_irq(pdev, 0); + if (davinci_spi->irq <= 0) { + ret = -EINVAL; + goto unmap_io; + } + + ret = request_irq(davinci_spi->irq, davinci_spi_irq, IRQF_DISABLED, + pdev->name, davinci_spi); + if (ret != 0) { + ret = -EAGAIN; + goto unmap_io; + } + + davinci_spi->bitbang.master = spi_master_get(master); + if (davinci_spi->bitbang.master == NULL) { + ret = -ENODEV; + goto err1; + } + + davinci_spi->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(davinci_spi->clk)) { + ret = -ENODEV; + goto put_master; + } + clk_enable(davinci_spi->clk); + + + master->bus_num = pdev->id; + master->num_chipselect = pdata->num_chipselect; + master->setup = davinci_spi_setup; + + davinci_spi->bitbang.chipselect = davinci_spi_chipselect; + davinci_spi->bitbang.setup_transfer = davinci_spi_setup_transfer; + + davinci_spi->bitbang.txrx_bufs = davinci_spi_bufs_pio; + davinci_spi->version = pdata->version; + davinci_spi->get_rx = davinci_spi_rx_buf_u8; + davinci_spi->get_tx = davinci_spi_tx_buf_u8; + + init_completion(&davinci_spi->done); + + /* Reset In/OUT SPI modle */ + iowrite32(0, davinci_spi->base + SPIGCR0); + udelay(100); + iowrite32(1, davinci_spi->base + SPIGCR0); + + ret = spi_bitbang_start(&davinci_spi->bitbang); + if (ret != 0) + goto free_clk; + + printk(KERN_NOTICE "Davinci SPI Controller driver at " + "0x%p (irq = %d) \n", + davinci_spi->base, davinci_spi->irq); + + return ret; + +free_clk: + clk_disable(davinci_spi->clk); + clk_put(davinci_spi->clk); +put_master: + spi_master_put(master); +err1: + free_irq(davinci_spi->irq, davinci_spi); +unmap_io: + iounmap(davinci_spi->base); +release_region: + release_mem_region(davinci_spi->pbase, davinci_spi->region_size); +free_master: + kfree(master); +err: + return ret; +} + +/** + * davinci_spi_remove - remove function for SPI Master Controller + * @pdev: platform_device structure which contains plateform specific data + * + * This function will do the reverse action of davinci_spi_probe function + * It will free the IRQ and SPI controller's memory region. + * It will also call spi_bitbang_stop to destroy the work queue which was + * created by spi_bitbang_start. + */ +static int __exit davinci_spi_remove(struct platform_device *pdev) +{ + struct davinci_spi *davinci_spi; + struct spi_master *master; + + master = dev_get_drvdata(&pdev->dev); + davinci_spi = spi_master_get_devdata(master); + + spi_bitbang_stop(&davinci_spi->bitbang); + + clk_disable(davinci_spi->clk); + clk_put(davinci_spi->clk); + spi_master_put(master); + free_irq(davinci_spi->irq, davinci_spi); + iounmap(davinci_spi->base); + release_mem_region(davinci_spi->pbase, davinci_spi->region_size); + + spi_master_put(master); + + return 0; +} + +static struct platform_driver davinci_spi_driver = { + .driver.name = "spi_davinci", + .remove = __exit_p(davinci_spi_remove), +}; + +static int __init davinci_spi_init(void) +{ + return platform_driver_probe(&davinci_spi_driver, davinci_spi_probe); +} + +static void __exit davinci_spi_exit(void) +{ + platform_driver_unregister(&davinci_spi_driver); +} + +module_init(davinci_spi_init); +module_exit(davinci_spi_exit); + +MODULE_DESCRIPTION("TI DaVinci SPI Master Controller Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/davinci_spi.h b/drivers/spi/davinci_spi.h new file mode 100644 index 0000000..f1a6e07 --- /dev/null +++ b/drivers/spi/davinci_spi.h @@ -0,0 +1,194 @@ +/* + * Copyright (C) 2009 Texas Instruments. + * + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __DAVINCI_SPI_H +#define __DAVINCI_SPI_H + +#include +#include +#include +#include +#include +#include + +#define DAVINCI_SPI_MAX_CHIPSELECT 2 + +#define CS_DEFAULT 0xFF +#define SCS0_SELECT 0x01 +#define SCS1_SELECT 0x02 +#define SCS2_SELECT 0x04 +#define SCS3_SELECT 0x08 +#define SCS4_SELECT 0x10 +#define SCS5_SELECT 0x20 +#define SCS6_SELECT 0x40 +#define SCS7_SELECT 0x80 + +#define DAVINCI_SPIFMT_PHASE_MASK BIT(16) +#define DAVINCI_SPIFMT_POLARITY_MASK BIT(17) +#define DAVINCI_SPIFMT_DISTIMER_MASK BIT(18) +#define DAVINCI_SPIFMT_SHIFTDIR_MASK BIT(20) +#define DAVINCI_SPIFMT_WAITENA_MASK BIT(21) +#define DAVINCI_SPIFMT_PARITYENA_MASK BIT(22) +#define DAVINCI_SPIFMT_ODD_PARITY_MASK BIT(23) +#define DAVINCI_SPIFMT_WDELAY_MASK 0x3f000000u +#define DAVINCI_SPIFMT_WDELAY_SHIFT 24 +#define DAVINCI_SPIFMT_CHARLEN_MASK 0x0000001Fu + +/* SPIGCR1 */ +#define DAVINCI_SPIGCR1_SPIENA_MASK 0x01000000u + +/* SPIPC0 */ +#define DAVINCI_SPIPC0_DIFUN_MASK BIT(11) +#define DAVINCI_SPIPC0_DIFUN_SHIFT 11 + +/* DIFUN */ +#define DAVINCI_SPIPC0_DIFUN_DI BIT(0) + +/* DOFUN */ +#define DAVINCI_SPIPC0_DOFUN_MASK BIT(10) +#define DAVINCI_SPIPC0_DOFUN_SHIFT 10 +#define DAVINCI_SPIPC0_DOFUN_DO BIT(0) + +/* CLKFUN */ +#define DAVINCI_SPIPC0_CLKFUN_MASK BIT(9) +#define DAVINCI_SPIPC0_CLKFUN_SHIFT 9 +#define DAVINCI_SPIPC0_CLKFUN_CLK BIT(0) + +/* EN1FUN */ +#define DAVINCI_SPIPC0_EN1FUN_MASK BIT(1) +#define DAVINCI_SPIPC0_EN1FUN_EN1 BIT(0) + +/* EN0FUN Tokens */ +#define DAVINCI_SPIPC0_EN0FUN_MASK BIT(0) +#define DAVINCI_SPIPC0_EN0FUN_SHIFT 0 +#define DAVINCI_SPIPC0_EN0FUN_EN0 BIT(0) + +#define DAVINCI_SPIPC0_SPIENA BIT(0) +#define DAVINCI_SPIPC0_SPIENA_SHIFT 8 + +#define DAVINCI_SPIINT_MASKALL 0x0101035F +#define DAVINCI_SPI_INTLVL_1 0x000001FFu +#define DAVINCI_SPI_INTLVL_0 0x00000000u + +/* SPIDAT1 */ +#define DAVINCI_SPIDAT1_CSHOLD_MASK BIT(28) +#define DAVINCI_SPIDAT1_CSHOLD_SHIFT 28 +#define DAVINCI_SPIDAT1_CSNR_MASK (BIT(17 | BIT(16)) +#define DAVINCI_SPIDAT1_CSNR_SHIFT 16 +#define DAVINCI_SPIDAT1_DFSEL_MASK (BIT(24 | BIT(25)) +#define DAVINCI_SPIGCR1_CLKMOD_MASK BIT(1) +#define DAVINCI_SPIGCR1_MASTER_MASK BIT(0) +#define DAVINCI_SPIGCR1_LOOPBACK_MASK BIT(16) + +/* SPIBUF */ +#define DAVINCI_SPIBUF_TXFULL_MASK BIT(29) +#define DAVINCI_SPIBUF_RXEMPTY_MASK BIT(31) + +/* Error Masks */ +#define DAVINCI_SPIFLG_DLEN_ERR_MASK BIT(0) +#define DAVINCI_SPIFLG_TIMEOUT_MASK BIT(1) +#define DAVINCI_SPIFLG_PARERR_MASK BIT(2) +#define DAVINCI_SPIFLG_DESYNC_MASK BIT(3) +#define DAVINCI_SPIFLG_BITERR_MASK BIT(4) +#define DAVINCI_SPIFLG_OVRRUN_MASK BIT(6) +#define DAVINCI_SPIFLG_RX_INTR_MASK BIT(8) +#define DAVINCI_SPIFLG_TX_INTR_MASK BIT(9) +#define DAVINCI_SPIFLG_BUF_INIT_ACTIVE_MASK BIT(24) +#define DAVINCI_SPIFLG_MASK (DAVINCI_SPIFLG_DLEN_ERR_MASK \ + | DAVINCI_SPIFLG_TIMEOUT_MASK | DAVINCI_SPIFLG_PARERR_MASK \ + | DAVINCI_SPIFLG_DESYNC_MASK | DAVINCI_SPIFLG_BITERR_MASK \ + | DAVINCI_SPIFLG_OVRRUN_MASK | DAVINCI_SPIFLG_RX_INTR_MASK \ + | DAVINCI_SPIFLG_TX_INTR_MASK \ + | DAVINCI_SPIFLG_BUF_INIT_ACTIVE_MASK) + +#define DAVINCI_SPIINT_DLEN_ERR_INTR BIT(0) +#define DAVINCI_SPIINT_TIMEOUT_INTR BIT(1) +#define DAVINCI_SPIINT_PARERR_INTR BIT(2) +#define DAVINCI_SPIINT_DESYNC_INTR BIT(3) +#define DAVINCI_SPIINT_BITERR_INTR BIT(4) +#define DAVINCI_SPIINT_OVRRUN_INTR BIT(6) +#define DAVINCI_SPIINT_RX_INTR BIT(8) +#define DAVINCI_SPIINT_TX_INTR BIT(9) +#define DAVINCI_SPIINT_DMA_REQ_EN BIT(16) +#define DAVINCI_SPIINT_ENABLE_HIGHZ BIT(24) + +#define DAVINCI_BYTELENGTH 8u + +#define DAVINCI_SPI_T2CDELAY_SHIFT 16 +#define DAVINCI_SPI_C2TDELAY_SHIFT 24 + +/* SPI Controller registers */ +#define SPIGCR0 0x00 +#define SPIGCR1 0x04 +#define SPIINT 0x08 +#define SPILVL 0x0c +#define SPIFLG 0x10 +#define SPIPC0 0x14 +#define SPIPC1 0x18 +#define SPIPC2 0x1c +#define SPIPC3 0x20 +#define SPIPC4 0x24 +#define SPIPC5 0x28 +#define SPIPC6 0x2c +#define SPIPC7 0x30 +#define SPIPC8 0x34 +#define SPIDAT0 0x38 +#define SPIDAT1 0x3c +#define SPIBUF 0x40 +#define SPIEMU 0x44 +#define SPIDELAY 0x48 +#define SPIDEF 0x4c +#define SPIFMT0 0x50 +#define SPIFMT1 0x54 +#define SPIFMT2 0x58 +#define SPIFMT3 0x5c +#define TGINTVEC0 0x60 +#define TGINTVEC1 0x64 + +struct davinci_spi_slave { + u32 cmd_to_write; + u32 clk_ctrl_to_write; + u32 bytes_per_word; + u8 active_cs; +}; + +/* SPI Controller driver's private data. */ +struct davinci_spi { + /* bitbang has to be first */ + struct spi_bitbang bitbang; + struct clk *clk; + + u8 version; + resource_size_t pbase; + void __iomem *base; + size_t region_size; + u32 irq; + struct completion done; + + const void *tx; + void *rx; + int count; + struct davinci_spi_platform_data *pdata; + + void (*get_rx)(u32 rx_data, struct davinci_spi *); + u32 (*get_tx)(struct davinci_spi *); + + struct davinci_spi_slave slave[DAVINCI_SPI_MAX_CHIPSELECT]; +}; + +#endif /* __DAVINCI_SPI_H */