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 * SPDX-License-Identifier:     GPL-2.0+
  15 */
  16
  17#include <common.h>
  18#include <spi.h>
  19#include <malloc.h>
  20#include <asm/io.h>
  21#include "omap3_spi.h"
  22
  23#define SPI_WAIT_TIMEOUT 3000000
  24
  25static void spi_reset(struct omap3_spi_slave *ds)
  26{
  27        unsigned int tmp;
  28
  29        writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig);
  30        do {
  31                tmp = readl(&ds->regs->sysstatus);
  32        } while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE));
  33
  34        writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE |
  35                                 OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP |
  36                                 OMAP3_MCSPI_SYSCONFIG_SMARTIDLE,
  37                                 &ds->regs->sysconfig);
  38
  39        writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable);
  40}
  41
  42static void omap3_spi_write_chconf(struct omap3_spi_slave *ds, int val)
  43{
  44        writel(val, &ds->regs->channel[ds->slave.cs].chconf);
  45        /* Flash post writes to make immediate effect */
  46        readl(&ds->regs->channel[ds->slave.cs].chconf);
  47}
  48
  49static void omap3_spi_set_enable(struct omap3_spi_slave *ds, int enable)
  50{
  51        writel(enable, &ds->regs->channel[ds->slave.cs].chctrl);
  52        /* Flash post writes to make immediate effect */
  53        readl(&ds->regs->channel[ds->slave.cs].chctrl);
  54}
  55
  56void spi_init()
  57{
  58        /* do nothing */
  59}
  60
  61struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
  62                                  unsigned int max_hz, unsigned int mode)
  63{
  64        struct omap3_spi_slave  *ds;
  65        struct mcspi *regs;
  66
  67        /*
  68         * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
  69         * with different number of chip selects (CS, channels):
  70         * McSPI1 has 4 CS (bus 0, cs 0 - 3)
  71         * McSPI2 has 2 CS (bus 1, cs 0 - 1)
  72         * McSPI3 has 2 CS (bus 2, cs 0 - 1)
  73         * McSPI4 has 1 CS (bus 3, cs 0)
  74         */
  75
  76        switch (bus) {
  77        case 0:
  78                regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
  79                break;
  80#ifdef OMAP3_MCSPI2_BASE
  81        case 1:
  82                regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
  83                break;
  84#endif
  85#ifdef OMAP3_MCSPI3_BASE
  86        case 2:
  87                regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
  88                break;
  89#endif
  90#ifdef OMAP3_MCSPI4_BASE
  91        case 3:
  92                regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
  93                break;
  94#endif
  95        default:
  96                printf("SPI error: unsupported bus %i. \
  97                        Supported busses 0 - 3\n", bus);
  98                return NULL;
  99        }
 100
 101        if (((bus == 0) && (cs > 3)) ||
 102                        ((bus == 1) && (cs > 1)) ||
 103                        ((bus == 2) && (cs > 1)) ||
 104                        ((bus == 3) && (cs > 0))) {
 105                printf("SPI error: unsupported chip select %i \
 106                        on bus %i\n", cs, bus);
 107                return NULL;
 108        }
 109
 110        if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
 111                printf("SPI error: unsupported frequency %i Hz. \
 112                        Max frequency is 48 Mhz\n", max_hz);
 113                return NULL;
 114        }
 115
 116        if (mode > SPI_MODE_3) {
 117                printf("SPI error: unsupported SPI mode %i\n", mode);
 118                return NULL;
 119        }
 120
 121        ds = spi_alloc_slave(struct omap3_spi_slave, bus, cs);
 122        if (!ds) {
 123                printf("SPI error: malloc of SPI structure failed\n");
 124                return NULL;
 125        }
 126
 127        ds->regs = regs;
 128        ds->freq = max_hz;
 129        ds->mode = mode;
 130
 131        return &ds->slave;
 132}
 133
 134void spi_free_slave(struct spi_slave *slave)
 135{
 136        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 137
 138        free(ds);
 139}
 140
 141int spi_claim_bus(struct spi_slave *slave)
 142{
 143        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 144        unsigned int conf, div = 0;
 145
 146        /* McSPI global module configuration */
 147
 148        /*
 149         * setup when switching from (reset default) slave mode
 150         * to single-channel master mode
 151         */
 152        spi_reset(ds);
 153        conf = readl(&ds->regs->modulctrl);
 154        conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS);
 155        conf |= OMAP3_MCSPI_MODULCTRL_SINGLE;
 156        writel(conf, &ds->regs->modulctrl);
 157
 158        /* McSPI individual channel configuration */
 159
 160        /* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */
 161        if (ds->freq) {
 162                while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div))
 163                                         > ds->freq)
 164                        div++;
 165        } else
 166                div = 0xC;
 167
 168        conf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 169
 170        /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
 171         * REVISIT: this controller could support SPI_3WIRE mode.
 172         */
 173#ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED
 174        /*
 175         * Some boards have D0 wired as MOSI / D1 as MISO instead of
 176         * The normal D0 as MISO / D1 as MOSI.
 177         */
 178        conf &= ~OMAP3_MCSPI_CHCONF_DPE0;
 179        conf |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1;
 180#else
 181        conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
 182        conf |= OMAP3_MCSPI_CHCONF_DPE0;
 183#endif
 184
 185        /* wordlength */
 186        conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
 187        conf |= (ds->slave.wordlen - 1) << 7;
 188
 189        /* set chipselect polarity; manage with FORCE */
 190        if (!(ds->mode & SPI_CS_HIGH))
 191                conf |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
 192        else
 193                conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
 194
 195        /* set clock divisor */
 196        conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK;
 197        conf |= div << 2;
 198
 199        /* set SPI mode 0..3 */
 200        if (ds->mode & SPI_CPOL)
 201                conf |= OMAP3_MCSPI_CHCONF_POL;
 202        else
 203                conf &= ~OMAP3_MCSPI_CHCONF_POL;
 204        if (ds->mode & SPI_CPHA)
 205                conf |= OMAP3_MCSPI_CHCONF_PHA;
 206        else
 207                conf &= ~OMAP3_MCSPI_CHCONF_PHA;
 208
 209        /* Transmit & receive mode */
 210        conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
 211
 212        omap3_spi_write_chconf(ds,conf);
 213
 214        return 0;
 215}
 216
 217void spi_release_bus(struct spi_slave *slave)
 218{
 219        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 220
 221        /* Reset the SPI hardware */
 222        spi_reset(ds);
 223}
 224
 225int omap3_spi_write(struct spi_slave *slave, unsigned int len, const void *txp,
 226                    unsigned long flags)
 227{
 228        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 229        int i;
 230        int timeout = SPI_WAIT_TIMEOUT;
 231        int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 232
 233        /* Enable the channel */
 234        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
 235
 236        chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
 237        chconf |= (ds->slave.wordlen - 1) << 7;
 238        chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY;
 239        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 240        omap3_spi_write_chconf(ds,chconf);
 241
 242        for (i = 0; i < len; i++) {
 243                /* wait till TX register is empty (TXS == 1) */
 244                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 245                         OMAP3_MCSPI_CHSTAT_TXS)) {
 246                        if (--timeout <= 0) {
 247                                printf("SPI TXS timed out, status=0x%08x\n",
 248                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 249                                return -1;
 250                        }
 251                }
 252                /* Write the data */
 253                unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx;
 254                if (ds->slave.wordlen > 16)
 255                        writel(((u32 *)txp)[i], tx);
 256                else if (ds->slave.wordlen > 8)
 257                        writel(((u16 *)txp)[i], tx);
 258                else
 259                        writel(((u8 *)txp)[i], tx);
 260        }
 261
 262        /* wait to finish of transfer */
 263        while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 264                         OMAP3_MCSPI_CHSTAT_EOT));
 265
 266        /* Disable the channel otherwise the next immediate RX will get affected */
 267        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
 268
 269        if (flags & SPI_XFER_END) {
 270
 271                chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 272                omap3_spi_write_chconf(ds,chconf);
 273        }
 274        return 0;
 275}
 276
 277int omap3_spi_read(struct spi_slave *slave, unsigned int len, void *rxp,
 278                   unsigned long flags)
 279{
 280        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 281        int i;
 282        int timeout = SPI_WAIT_TIMEOUT;
 283        int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 284
 285        /* Enable the channel */
 286        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
 287
 288        chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
 289        chconf |= (ds->slave.wordlen - 1) << 7;
 290        chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY;
 291        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 292        omap3_spi_write_chconf(ds,chconf);
 293
 294        writel(0, &ds->regs->channel[ds->slave.cs].tx);
 295
 296        for (i = 0; i < len; i++) {
 297                /* Wait till RX register contains data (RXS == 1) */
 298                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 299                         OMAP3_MCSPI_CHSTAT_RXS)) {
 300                        if (--timeout <= 0) {
 301                                printf("SPI RXS timed out, status=0x%08x\n",
 302                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 303                                return -1;
 304                        }
 305                }
 306
 307                /* Disable the channel to prevent furher receiving */
 308                if(i == (len - 1))
 309                        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
 310
 311                /* Read the data */
 312                unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx;
 313                if (ds->slave.wordlen > 16)
 314                        ((u32 *)rxp)[i] = readl(rx);
 315                else if (ds->slave.wordlen > 8)
 316                        ((u16 *)rxp)[i] = (u16)readl(rx);
 317                else
 318                        ((u8 *)rxp)[i] = (u8)readl(rx);
 319        }
 320
 321        if (flags & SPI_XFER_END) {
 322                chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 323                omap3_spi_write_chconf(ds,chconf);
 324        }
 325
 326        return 0;
 327}
 328
 329/*McSPI Transmit Receive Mode*/
 330int omap3_spi_txrx(struct spi_slave *slave, unsigned int len,
 331                   const void *txp, void *rxp, unsigned long flags)
 332{
 333        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 334        int timeout = SPI_WAIT_TIMEOUT;
 335        int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 336        int irqstatus = readl(&ds->regs->irqstatus);
 337        int i=0;
 338
 339        /*Enable SPI channel*/
 340        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
 341
 342        /*set TRANSMIT-RECEIVE Mode*/
 343        chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
 344        chconf |= (ds->slave.wordlen - 1) << 7;
 345        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 346        omap3_spi_write_chconf(ds,chconf);
 347
 348        /*Shift in and out 1 byte at time*/
 349        for (i=0; i < len; i++){
 350                /* Write: wait for TX empty (TXS == 1)*/
 351                irqstatus |= (1<< (4*(ds->slave.bus)));
 352                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 353                         OMAP3_MCSPI_CHSTAT_TXS)) {
 354                        if (--timeout <= 0) {
 355                                printf("SPI TXS timed out, status=0x%08x\n",
 356                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 357                                return -1;
 358                        }
 359                }
 360                /* Write the data */
 361                unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx;
 362                if (ds->slave.wordlen > 16)
 363                        writel(((u32 *)txp)[i], tx);
 364                else if (ds->slave.wordlen > 8)
 365                        writel(((u16 *)txp)[i], tx);
 366                else
 367                        writel(((u8 *)txp)[i], tx);
 368
 369                /*Read: wait for RX containing data (RXS == 1)*/
 370                while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
 371                         OMAP3_MCSPI_CHSTAT_RXS)) {
 372                        if (--timeout <= 0) {
 373                                printf("SPI RXS timed out, status=0x%08x\n",
 374                                       readl(&ds->regs->channel[ds->slave.cs].chstat));
 375                                return -1;
 376                        }
 377                }
 378                /* Read the data */
 379                unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx;
 380                if (ds->slave.wordlen > 16)
 381                        ((u32 *)rxp)[i] = readl(rx);
 382                else if (ds->slave.wordlen > 8)
 383                        ((u16 *)rxp)[i] = (u16)readl(rx);
 384                else
 385                        ((u8 *)rxp)[i] = (u8)readl(rx);
 386        }
 387        /* Disable the channel */
 388        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
 389
 390        /*if transfer must be terminated disable the channel*/
 391        if (flags & SPI_XFER_END) {
 392                chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 393                omap3_spi_write_chconf(ds,chconf);
 394        }
 395
 396        return 0;
 397}
 398
 399int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 400             const void *dout, void *din, unsigned long flags)
 401{
 402        struct omap3_spi_slave *ds = to_omap3_spi(slave);
 403        unsigned int    len;
 404        int ret = -1;
 405
 406        if (ds->slave.wordlen < 4 || ds->slave.wordlen > 32) {
 407                printf("omap3_spi: invalid wordlen %d\n", ds->slave.wordlen);
 408                return -1;
 409        }
 410
 411        if (bitlen % ds->slave.wordlen)
 412                return -1;
 413
 414        len = bitlen / ds->slave.wordlen;
 415
 416        if (bitlen == 0) {       /* only change CS */
 417                int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
 418
 419                if (flags & SPI_XFER_BEGIN) {
 420                        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
 421                        chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 422                        omap3_spi_write_chconf(ds,chconf);
 423                }
 424                if (flags & SPI_XFER_END) {
 425                        chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
 426                        omap3_spi_write_chconf(ds,chconf);
 427                        omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
 428                }
 429                ret = 0;
 430        } else {
 431                if (dout != NULL && din != NULL)
 432                        ret = omap3_spi_txrx(slave, len, dout, din, flags);
 433                else if (dout != NULL)
 434                        ret = omap3_spi_write(slave, len, dout, flags);
 435                else if (din != NULL)
 436                        ret = omap3_spi_read(slave, len, din, flags);
 437        }
 438        return ret;
 439}
 440
 441int spi_cs_is_valid(unsigned int bus, unsigned int cs)
 442{
 443        return 1;
 444}
 445
 446void spi_cs_activate(struct spi_slave *slave)
 447{
 448}
 449
 450void spi_cs_deactivate(struct spi_slave *slave)
 451{
 452}
 453