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 | |
| parent | 3e664f6d50ea7f5d6ea96a028fc8f099236e99be (diff) | |
| download | olio-uboot-2014.01-53736baaff80fc0cde36661296dffdedc7226bd2.tar.xz olio-uboot-2014.01-53736baaff80fc0cde36661296dffdedc7226bd2.zip | |
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>
| -rw-r--r-- | drivers/spi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/spi/omap3_spi.c | 352 | ||||
| -rw-r--r-- | drivers/spi/omap3_spi.h | 117 | ||||
| -rw-r--r-- | include/configs/omap3_beagle.h | 2 | 
4 files changed, 472 insertions, 0 deletions
| diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 117ab1988..e34a12423 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -35,6 +35,7 @@ COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o  COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o  COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o  COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o +COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o  COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o  COBJS	:= $(COBJS-y) diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c new file mode 100644 index 000000000..af12c0e59 --- /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) +{ +} diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h new file mode 100644 index 000000000..b8e3a4c44 --- /dev/null +++ b/drivers/spi/omap3_spi.h @@ -0,0 +1,117 @@ +/* + * Register definitions for the OMAP3 McSPI Controller + * + * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com> + * + * 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 + */ + +#ifndef _OMAP3_SPI_H_ +#define _OMAP3_SPI_H_ + +#define OMAP3_MCSPI1_BASE	0x48098000 +#define OMAP3_MCSPI2_BASE	0x4809A000 +#define OMAP3_MCSPI3_BASE	0x480B8000 +#define OMAP3_MCSPI4_BASE	0x480BA000 + +#define OMAP3_MCSPI_MAX_FREQ	48000000 + +/* OMAP3 McSPI registers */ +struct mcspi_channel { +	unsigned int chconf;		/* 0x2C, 0x40, 0x54, 0x68 */ +	unsigned int chstat;		/* 0x30, 0x44, 0x58, 0x6C */ +	unsigned int chctrl;		/* 0x34, 0x48, 0x5C, 0x70 */ +	unsigned int tx;		/* 0x38, 0x4C, 0x60, 0x74 */ +	unsigned int rx;		/* 0x3C, 0x50, 0x64, 0x78 */ +}; + +struct mcspi { +	unsigned char res1[0x10]; +	unsigned int sysconfig;		/* 0x10 */ +	unsigned int sysstatus;		/* 0x14 */ +	unsigned int irqstatus;		/* 0x18 */ +	unsigned int irqenable;		/* 0x1C */ +	unsigned int wakeupenable;	/* 0x20 */ +	unsigned int syst;		/* 0x24 */ +	unsigned int modulctrl;		/* 0x28 */ +	struct mcspi_channel channel[4]; /* channel0: 0x2C - 0x3C, bus 0 & 1 & 2 & 3 */ +					/* channel1: 0x40 - 0x50, bus 0 & 1 */ +					/* channel2: 0x54 - 0x64, bus 0 & 1 */ +					/* channel3: 0x68 - 0x78, bus 0 */ +}; + +/* per-register bitmasks */ +#define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3) +#define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP (1 << 2) +#define OMAP3_MCSPI_SYSCONFIG_AUTOIDLE	(1 << 0) +#define OMAP3_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) + +#define OMAP3_MCSPI_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP3_MCSPI_MODULCTRL_SINGLE	(1 << 0) +#define OMAP3_MCSPI_MODULCTRL_MS	(1 << 2) +#define OMAP3_MCSPI_MODULCTRL_STEST	(1 << 3) + +#define OMAP3_MCSPI_CHCONF_PHA		(1 << 0) +#define OMAP3_MCSPI_CHCONF_POL		(1 << 1) +#define OMAP3_MCSPI_CHCONF_CLKD_MASK	(0x0f << 2) +#define OMAP3_MCSPI_CHCONF_EPOL		(1 << 6) +#define OMAP3_MCSPI_CHCONF_WL_MASK	(0x1f << 7) +#define OMAP3_MCSPI_CHCONF_TRM_RX_ONLY	(0x01 << 12) +#define OMAP3_MCSPI_CHCONF_TRM_TX_ONLY	(0x02 << 12) +#define OMAP3_MCSPI_CHCONF_TRM_MASK	(0x03 << 12) +#define OMAP3_MCSPI_CHCONF_DMAW		(1 << 14) +#define OMAP3_MCSPI_CHCONF_DMAR		(1 << 15) +#define OMAP3_MCSPI_CHCONF_DPE0		(1 << 16) +#define OMAP3_MCSPI_CHCONF_DPE1		(1 << 17) +#define OMAP3_MCSPI_CHCONF_IS		(1 << 18) +#define OMAP3_MCSPI_CHCONF_TURBO	(1 << 19) +#define OMAP3_MCSPI_CHCONF_FORCE	(1 << 20) + +#define OMAP3_MCSPI_CHSTAT_RXS		(1 << 0) +#define OMAP3_MCSPI_CHSTAT_TXS		(1 << 1) +#define OMAP3_MCSPI_CHSTAT_EOT		(1 << 2) + +#define OMAP3_MCSPI_CHCTRL_EN		(1 << 0) + +#define OMAP3_MCSPI_WAKEUPENABLE_WKEN	(1 << 0) + +struct omap3_spi_slave { +	struct spi_slave slave; +	struct mcspi *regs; +	unsigned int freq; +	unsigned int mode; +}; + +static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave) +{ +	return container_of(slave, struct omap3_spi_slave, slave); +} + +int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp, +		    unsigned long flags); +int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp, +		   unsigned long flags); + +#endif /* _OMAP3_SPI_H_ */ diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h index 56363f762..5cfa4cb69 100644 --- a/include/configs/omap3_beagle.h +++ b/include/configs/omap3_beagle.h @@ -335,4 +335,6 @@ extern unsigned int boot_flash_type;  					 CONFIG_SYS_INIT_RAM_SIZE - \  					 GENERATED_GBL_DATA_SIZE) +#define CONFIG_OMAP3_SPI +  #endif /* __CONFIG_H */ |