uboot/drivers/spi/omap3_spi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com>
   3 *
   4 * Driver for McSPI controller on OMAP3. Based on davinci_spi.c
   5 * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
   6 *
   7 * Copyright (C) 2007 Atmel Corporation
   8 *
   9 * Parts taken from linux/drivers/spi/omap2_mcspi.c
  10 * Copyright (C) 2005, 2006 Nokia Corporation
  11 *
  12 * Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.com>
  13 *
  14 * See file CREDITS for list of people who contributed to this
  15 * project.
  16 *
  17 * This program is free software; you can redistribute it and/or
  18 * modify it under the terms of the GNU General Public License as
  19 * published by the Free Software Foundation; either version 2 of
  20 * the License, or (at your option) any later version.
  21 *
  22 * This program is distributed in the hope that it will be useful,
  23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  25 * GNU General Public License for more details.
  26 *
  27 * You should have received a copy of the GNU General Public License
  28 * along with this program; if not, write to the Free Software
  29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
  30 * MA 02111-1307 USA
  31 *
  32 */
  33
  34#include <common.h>
  35#include <spi.h>
  36#include <malloc.h>
  37#include <asm/io.h>
  38#include "omap3_spi.h"
  39
  40#define WORD_LEN        8
  41#define SPI_WAIT_TIMEOUT 3000000;
  42
  43static void spi_reset(struct omap3_spi_slave *ds)
  44{
  45        unsigned int tmp;
  46
  47        writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig);
  48        do {
  49                tmp = readl(&ds->regs->sysstatus);
  50        } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE));
  51
  52        writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE |
  53                                 OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP |
  54                                 OMAP3_MCSPI_SYSCONFIG_SMARTIDLE,
  55                                 &ds->regs->sysconfig);
  56
  57        writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable);
  58}
  59
  60void spi_init()
  61{
  62        /* do nothing */
  63}
  64
  65struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
  66                                  unsigned int max_hz, unsigned int mode)
  67{
  68        struct omap3_spi_slave  *ds;
  69
  70        ds = malloc(sizeof(struct omap3_spi_slave));
  71        if (!ds) {
  72                printf("SPI error: malloc of SPI structure failed\n");
  73                return NULL;
  74        }
  75
  76        /*
  77         * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
  78         * with different number of chip selects (CS, channels):
  79         * McSPI1 has 4 CS (bus 0, cs 0 - 3)
  80         * McSPI2 has 2 CS (bus 1, cs 0 - 1)
  81         * McSPI3 has 2 CS (bus 2, cs 0 - 1)
  82         * McSPI4 has 1 CS (bus 3, cs 0)
  83         */
  84
  85        switch (bus) {
  86        case 0:
  87                ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
  88                break;
  89        case 1:
  90                ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
  91                break;
  92        case 2:
  93                ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
  94                break;
  95        case 3:
  96                ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
  97                break;
  98        default:
  99                printf("SPI error: unsupported bus %i. \
 100                        Supported busses 0 - 3\n", bus);
 101                return NULL;
 102        }
 103        ds->slave.bus = bus;
 104
 105        if (((bus == 0) && (cs > 3)) ||
 106                        ((bus == 1) && (cs > 1)) ||
 107                        ((bus == 2) && (cs > 1)) ||
 108                        ((bus == 3) && (cs > 0))) {
 109                printf("SPI error: unsupported chip select %i \
 110                        on bus %i\n", cs, bus);
 111                return NULL;
 112        }
 113        ds->slave.cs = cs;
 114
 115        if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
 116                printf("SPI error: unsupported frequency %i Hz. \
 117                        Max frequency is 48 Mhz\n", max_hz);
 118                return NULL;
 119        }
 120        ds->freq = max_hz;
 121
 122        if (mode > SPI_MODE_3) {
 123                printf("SPI error: unsupported SPI mode %i\n", mode);
 124                return NULL;
 125        }
 126        ds->mode = mode;
 127
 128        return &ds->slave;
 129}
 130
 131void spi_free_slave(struct spi_slave *slave)
 132{
 133        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 134
 135        free(ds);
 136}
 137
 138int spi_claim_bus(struct spi_slave *slave)
 139{
 140        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 141        unsigned int conf, div = 0;
 142
 143        /* McSPI global module configuration */
 144
 145        /*
 146         * setup when switching from (reset default) slave mode
 147         * to single-channel master mode
 148         */
 149        spi_reset(ds);
 150        conf = readl(&ds->regs->modulctrl);
 151        conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS);
 152        conf |= OMAP3_MCSPI_MODULCTRL_SINGLE;
 153        writel(conf, &ds->regs->modulctrl);
 154
 155        /* McSPI individual channel configuration */
 156
 157        /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */
 158        if (ds->freq) {
 159                while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div))
 160                                         > ds->freq)
 161                        div++;
 162        } else
 163                div = 0xC;
 164
 165        conf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 166
 167        /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
 168         * REVISIT: this controller could support SPI_3WIRE mode.
 169         */
 170        conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
 171        conf |= OMAP3_MCSPI_CHCONF_DPE0;
 172
 173        /* wordlength */
 174        conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
 175        conf |= (WORD_LEN - 1) << 7;
 176
 177        /* set chipselect polarity; manage with FORCE */
 178        if (!(ds->mode & SPI_CS_HIGH))
 179                conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
 180        else
 181                conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
 182
 183        /* set clock divisor */
 184        conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK;
 185        conf |= div << 2;
 186
 187        /* set SPI mode 0..3 */
 188        if (ds->mode & SPI_CPOL)
 189                conf |= OMAP3_MCSPI_CHCONF_POL;
 190        else
 191                conf &= ~OMAP3_MCSPI_CHCONF_POL;
 192        if (ds->mode & SPI_CPHA)
 193                conf |= OMAP3_MCSPI_CHCONF_PHA;
 194        else
 195                conf &= ~OMAP3_MCSPI_CHCONF_PHA;
 196
 197        /* Transmit & receive mode */
 198        conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
 199
 200        writel(conf, &ds->regs->channel[ds->slave.cs].chconf);
 201
 202        return 0;
 203}
 204
 205void spi_release_bus(struct spi_slave *slave)
 206{
 207        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 208
 209        /* Reset the SPI hardware */
 210        spi_reset(ds);
 211}
 212
 213int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
 214                    unsigned long flags)
 215{
 216        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 217        int i;
 218        int timeout = SPI_WAIT_TIMEOUT;
 219        int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 220
 221        if (flags & SPI_XFER_BEGIN)
 222                writel(OMAP3_MCSPI_CHCTRL_EN,
 223                       &ds->regs->channel[ds->slave.cs].chctrl);
 224
 225        chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
 226        chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY;
 227        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 228        writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
 229
 230        for (i = 0; i < len; i++) {
 231                /* wait till TX register is empty (TXS == 1) */
 232                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 233                         OMAP3_MCSPI_CHSTAT_TXS)) {
 234                        if (--timeout <= 0) {
 235                                printf("SPI TXS timed out, status=0x%08x\n",
 236                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 237                                return -1;
 238                        }
 239                }
 240                /* Write the data */
 241                writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
 242        }
 243
 244        if (flags & SPI_XFER_END) {
 245                /* wait to finish of transfer */
 246                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 247                         OMAP3_MCSPI_CHSTAT_EOT));
 248
 249                chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 250                writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
 251
 252                writel(0, &ds->regs->channel[ds->slave.cs].chctrl);
 253        }
 254        return 0;
 255}
 256
 257int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
 258                   unsigned long flags)
 259{
 260        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 261        int i;
 262        int timeout = SPI_WAIT_TIMEOUT;
 263        int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 264
 265        if (flags & SPI_XFER_BEGIN)
 266                writel(OMAP3_MCSPI_CHCTRL_EN,
 267                       &ds->regs->channel[ds->slave.cs].chctrl);
 268
 269        chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
 270        chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY;
 271        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 272        writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
 273
 274        writel(0, &ds->regs->channel[ds->slave.cs].tx);
 275
 276        for (i = 0; i < len; i++) {
 277                /* Wait till RX register contains data (RXS == 1) */
 278                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 279                         OMAP3_MCSPI_CHSTAT_RXS)) {
 280                        if (--timeout <= 0) {
 281                                printf("SPI RXS timed out, status=0x%08x\n",
 282                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 283                                return -1;
 284                        }
 285                }
 286                /* Read the data */
 287                rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
 288        }
 289
 290        if (flags & SPI_XFER_END) {
 291                chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 292                writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
 293
 294                writel(0, &ds->regs->channel[ds->slave.cs].chctrl);
 295        }
 296
 297        return 0;
 298}
 299
 300/*McSPI Transmit Receive Mode*/
 301int omap3_spi_txrx(struct spi_slave *slave,
 302                unsigned int len, const u8 *txp, u8 *rxp, unsigned long flags)
 303{
 304        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 305        int timeout = SPI_WAIT_TIMEOUT;
 306        int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 307        int irqstatus = readl(&ds->regs->irqstatus);
 308        int i=0;
 309
 310        /*Enable SPI channel*/
 311        if (flags & SPI_XFER_BEGIN)
 312                writel(OMAP3_MCSPI_CHCTRL_EN,
 313                       &ds->regs->channel[ds->slave.cs].chctrl);
 314
 315        /*set TRANSMIT-RECEIVE Mode*/
 316        chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
 317        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 318        writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
 319
 320        /*Shift in and out 1 byte at time*/
 321        for (i=0; i < len; i++){
 322                /* Write: wait for TX empty (TXS == 1)*/
 323                irqstatus |= (1<< (4*(ds->slave.bus)));
 324                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 325                         OMAP3_MCSPI_CHSTAT_TXS)) {
 326                        if (--timeout <= 0) {
 327                                printf("SPI TXS timed out, status=0x%08x\n",
 328                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 329                                return -1;
 330                        }
 331                }
 332                /* Write the data */
 333                writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
 334
 335                /*Read: wait for RX containing data (RXS == 1)*/
 336                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 337                         OMAP3_MCSPI_CHSTAT_RXS)) {
 338                        if (--timeout <= 0) {
 339                                printf("SPI RXS timed out, status=0x%08x\n",
 340                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 341                                return -1;
 342                        }
 343                }
 344                /* Read the data */
 345                rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
 346        }
 347
 348        /*if transfer must be terminated disable the channel*/
 349        if (flags & SPI_XFER_END) {
 350                chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 351                writel(chconf, &ds->regs->channel[ds->slave.cs].chconf);
 352
 353                writel(0, &ds->regs->channel[ds->slave.cs].chctrl);
 354        }
 355
 356        return 0;
 357}
 358
 359int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 360             const void *dout, void *din, unsigned long flags)
 361{
 362        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 363        unsigned int    len;
 364        const u8        *txp = dout;
 365        u8              *rxp = din;
 366        int ret = -1;
 367
 368        if (bitlen % 8)
 369                return -1;
 370
 371        len = bitlen / 8;
 372
 373        if (bitlen == 0) {       /* only change CS */
 374                int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 375
 376                if (flags & SPI_XFER_BEGIN) {
 377                        writel(OMAP3_MCSPI_CHCTRL_EN,
 378                               &ds->regs->channel[ds->slave.cs].chctrl);
 379                        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 380                        writel(chconf,
 381                               &ds->regs->channel[ds->slave.cs].chconf);
 382                }
 383                if (flags & SPI_XFER_END) {
 384                        chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 385                        writel(chconf,
 386                               &ds->regs->channel[ds->slave.cs].chconf);
 387                        writel(0, &ds->regs->channel[ds->slave.cs].chctrl);
 388                }
 389                ret = 0;
 390        } else {
 391                if (dout != NULL && din != NULL)
 392                        ret = omap3_spi_txrx(slave, len, txp, rxp, flags);
 393                else if (dout != NULL)
 394                        ret = omap3_spi_write(slave, len, txp, flags);
 395                else if (din != NULL)
 396                        ret = omap3_spi_read(slave, len, rxp, flags);
 397        }
 398        return ret;
 399}
 400
 401int spi_cs_is_valid(unsigned int bus, unsigned int cs)
 402{
 403        return 1;
 404}
 405
 406void spi_cs_activate(struct spi_slave *slave)
 407{
 408}
 409
 410void spi_cs_deactivate(struct spi_slave *slave)
 411{
 412}
 413