diff options
author | Dirk Behme <dirk.behme@googlemail.com> | 2010-12-11 11:01:00 -0500 |
---|---|---|
committer | Sandeep Paulraj <s-paulraj@ti.com> | 2010-12-11 11:01:00 -0500 |
commit | 53736baaff80fc0cde36661296dffdedc7226bd2 (patch) | |
tree | e8e8e28b54388e506a0000da5e50c6dae7ddaf18 /drivers/spi/omap3_spi.c | |
parent | 3e664f6d50ea7f5d6ea96a028fc8f099236e99be (diff) | |
download | u-boot-imx-53736baaff80fc0cde36661296dffdedc7226bd2.zip u-boot-imx-53736baaff80fc0cde36661296dffdedc7226bd2.tar.gz u-boot-imx-53736baaff80fc0cde36661296dffdedc7226bd2.tar.bz2 |
OMAP3: SPI driver
CC: Ruslan N. Araslanov <byaaka@yandex.ru>
Signed-off-by: Ruslan Araslanov <ruslan.araslanov@vitecmm.com>
Signed-off-by: Sandeep Paulraj <s-paulraj@ti.com>
Diffstat (limited to 'drivers/spi/omap3_spi.c')
-rw-r--r-- | drivers/spi/omap3_spi.c | 352 |
1 files changed, 352 insertions, 0 deletions
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c new file mode 100644 index 0000000..af12c0e --- /dev/null +++ b/drivers/spi/omap3_spi.c @@ -0,0 +1,352 @@ +/* + * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> + * + * Driver for McSPI controller on OMAP3. Based on davinci_spi.c + * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * + * Copyright (C) 2007 Atmel Corporation + * + * Parts taken from linux/drivers/spi/omap2_mcspi.c + * Copyright (C) 2005, 2006 Nokia Corporation + * + * Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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 <common.h> +#include <spi.h> +#include <malloc.h> +#include <asm/io.h> +#include "omap3_spi.h" + +#define WORD_LEN 8 +#define SPI_WAIT_TIMEOUT 3000000; + +static void spi_reset(struct omap3_spi_slave *ds) +{ + unsigned int tmp; + + writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig); + do { + tmp = readl(&ds->regs->sysstatus); + } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE)); + + writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE | + OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP | + OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, + &ds->regs->sysconfig); + + writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable); +} + +void spi_init() +{ + /* do nothing */ +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct omap3_spi_slave *ds; + + ds = malloc(sizeof(struct omap3_spi_slave)); + if (!ds) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + + /* + * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules) + * with different number of chip selects (CS, channels): + * McSPI1 has 4 CS (bus 0, cs 0 - 3) + * McSPI2 has 2 CS (bus 1, cs 0 - 1) + * McSPI3 has 2 CS (bus 2, cs 0 - 1) + * McSPI4 has 1 CS (bus 3, cs 0) + */ + + switch (bus) { + case 0: + ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE; + break; + case 1: + ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE; + break; + case 2: + ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE; + break; + case 3: + ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE; + break; + default: + printf("SPI error: unsupported bus %i. \ + Supported busses 0 - 3\n", bus); + return NULL; + } + ds->slave.bus = bus; + + if (((bus == 0) && (cs > 3)) || + ((bus == 1) && (cs > 1)) || + ((bus == 2) && (cs > 1)) || + ((bus == 3) && (cs > 0))) { + printf("SPI error: unsupported chip select %i \ + on bus %i\n", cs, bus); + return NULL; + } + ds->slave.cs = cs; + + if (max_hz > OMAP3_MCSPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %i Hz. \ + Max frequency is 48 Mhz\n", max_hz); + return NULL; + } + ds->freq = max_hz; + + if (mode > SPI_MODE_3) { + printf("SPI error: unsupported SPI mode %i\n", mode); + return NULL; + } + ds->mode = mode; + + return &ds->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + + free(ds); +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + unsigned int conf, div = 0; + + /* McSPI global module configuration */ + + /* + * setup when switching from (reset default) slave mode + * to single-channel master mode + */ + spi_reset(ds); + conf = readl(&ds->regs->modulctrl); + conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS); + conf |= OMAP3_MCSPI_MODULCTRL_SINGLE; + writel(conf, &ds->regs->modulctrl); + + /* McSPI individual channel configuration */ + + /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */ + if (ds->freq) { + while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div)) + > ds->freq) + div++; + } else + div = 0xC; + + conf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS + * REVISIT: this controller could support SPI_3WIRE mode. + */ + conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1); + conf |= OMAP3_MCSPI_CHCONF_DPE0; + + /* wordlength */ + conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK; + conf |= (WORD_LEN - 1) << 7; + + /* set chipselect polarity; manage with FORCE */ + if (!(ds->mode & SPI_CS_HIGH)) + conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */ + else + conf &= ~OMAP3_MCSPI_CHCONF_EPOL; + + /* set clock divisor */ + conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK; + conf |= div << 2; + + /* set SPI mode 0..3 */ + if (ds->mode & SPI_CPOL) + conf |= OMAP3_MCSPI_CHCONF_POL; + else + conf &= ~OMAP3_MCSPI_CHCONF_POL; + if (ds->mode & SPI_CPHA) + conf |= OMAP3_MCSPI_CHCONF_PHA; + else + conf &= ~OMAP3_MCSPI_CHCONF_PHA; + + /* Transmit & receive mode */ + conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + + writel(conf, &ds->regs->channel[ds->slave.cs].chconf); + + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + + /* Reset the SPI hardware */ + spi_reset(ds); +} + +int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp, + unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int i; + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + if (flags & SPI_XFER_BEGIN) + writel(OMAP3_MCSPI_CHCTRL_EN, + &ds->regs->channel[ds->slave.cs].chctrl); + + chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + for (i = 0; i < len; i++) { + /* wait till TX register is empty (TXS == 1) */ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_TXS)) { + if (--timeout <= 0) { + printf("SPI TXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + /* Write the data */ + writel(txp[i], &ds->regs->channel[ds->slave.cs].tx); + } + + if (flags & SPI_XFER_END) { + /* wait to finish of transfer */ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_EOT)); + + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + } + return 0; +} + +int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp, + unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int i; + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + if (flags & SPI_XFER_BEGIN) + writel(OMAP3_MCSPI_CHCTRL_EN, + &ds->regs->channel[ds->slave.cs].chctrl); + + chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + writel(0, &ds->regs->channel[ds->slave.cs].tx); + + for (i = 0; i < len; i++) { + /* Wait till RX register contains data (RXS == 1) */ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_RXS)) { + if (--timeout <= 0) { + printf("SPI RXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + /* Read the data */ + rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx); + } + + if (flags & SPI_XFER_END) { + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + } + + return 0; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + unsigned int len; + const u8 *txp = dout; + u8 *rxp = din; + int ret = -1; + + if (bitlen % 8) + return -1; + + len = bitlen / 8; + + if (bitlen == 0) { /* only change CS */ + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + + if (flags & SPI_XFER_BEGIN) { + writel(OMAP3_MCSPI_CHCTRL_EN, + &ds->regs->channel[ds->slave.cs].chctrl); + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, + &ds->regs->channel[ds->slave.cs].chconf); + } + if (flags & SPI_XFER_END) { + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, + &ds->regs->channel[ds->slave.cs].chconf); + writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + } + ret = 0; + } else { + if (dout != NULL) + ret = omap3_spi_write(slave, len, txp, flags); + + if (din != NULL) + ret = omap3_spi_read(slave, len, rxp, flags); + } + return ret; +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return 1; +} + +void spi_cs_activate(struct spi_slave *slave) +{ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ +} |