uboot/arch/powerpc/cpu/mpc8xx/spi.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2001 Navin Boppuri / Prashant Patel
   3 *      <nboppuri@trinetcommunication.com>,
   4 *      <pmpatel@trinetcommunication.com>
   5 * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de>
   6 * Copyright (c) 2001 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>.
   7 *
   8 * SPDX-License-Identifier:     GPL-2.0+
   9 */
  10
  11/*
  12 * MPC8xx CPM SPI interface.
  13 *
  14 * Parts of this code are probably not portable and/or specific to
  15 * the board which I used for the tests. Please send fixes/complaints
  16 * to wd@denx.de
  17 *
  18 */
  19
  20#include <common.h>
  21#include <mpc8xx.h>
  22#include <commproc.h>
  23#include <linux/ctype.h>
  24#include <malloc.h>
  25#include <post.h>
  26#include <serial.h>
  27
  28#if (defined(CONFIG_SPI)) || (CONFIG_POST & CONFIG_SYS_POST_SPI)
  29
  30/* Warning:
  31 * You cannot enable DEBUG for early system initalization, i. e. when
  32 * this driver is used to read environment parameters like "baudrate"
  33 * from EEPROM which are used to initialize the serial port which is
  34 * needed to print the debug messages...
  35 */
  36#undef  DEBUG
  37
  38#define SPI_EEPROM_WREN         0x06
  39#define SPI_EEPROM_RDSR         0x05
  40#define SPI_EEPROM_READ         0x03
  41#define SPI_EEPROM_WRITE        0x02
  42
  43/* ---------------------------------------------------------------
  44 * Offset for initial SPI buffers in DPRAM:
  45 * We need a 520 byte scratch DPRAM area to use at an early stage.
  46 * It is used between the two initialization calls (spi_init_f()
  47 * and spi_init_r()).
  48 * The value 0xb00 makes it far enough from the start of the data
  49 * area (as well as from the stack pointer).
  50 * --------------------------------------------------------------- */
  51#ifndef CONFIG_SYS_SPI_INIT_OFFSET
  52#define CONFIG_SYS_SPI_INIT_OFFSET      0xB00
  53#endif
  54
  55#ifdef  DEBUG
  56
  57#define DPRINT(a)       printf a;
  58/* -----------------------------------------------
  59 * Helper functions to peek into tx and rx buffers
  60 * ----------------------------------------------- */
  61static const char * const hex_digit = "0123456789ABCDEF";
  62
  63static char quickhex (int i)
  64{
  65        return hex_digit[i];
  66}
  67
  68static void memdump (void *pv, int num)
  69{
  70        int i;
  71        unsigned char *pc = (unsigned char *) pv;
  72
  73        for (i = 0; i < num; i++)
  74                printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f));
  75        printf ("\t");
  76        for (i = 0; i < num; i++)
  77                printf ("%c", isprint (pc[i]) ? pc[i] : '.');
  78        printf ("\n");
  79}
  80#else   /* !DEBUG */
  81
  82#define DPRINT(a)
  83
  84#endif  /* DEBUG */
  85
  86/* -------------------
  87 * Function prototypes
  88 * ------------------- */
  89void spi_init (void);
  90
  91ssize_t spi_read (uchar *, int, uchar *, int);
  92ssize_t spi_write (uchar *, int, uchar *, int);
  93ssize_t spi_xfer (size_t);
  94
  95/* -------------------
  96 * Variables
  97 * ------------------- */
  98
  99#define MAX_BUFFER      0x104
 100
 101/* ----------------------------------------------------------------------
 102 * Initially we place the RX and TX buffers at a fixed location in DPRAM!
 103 * ---------------------------------------------------------------------- */
 104static uchar *rxbuf =
 105  (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem
 106                        [CONFIG_SYS_SPI_INIT_OFFSET];
 107static uchar *txbuf =
 108  (uchar *)&((cpm8xx_t *)&((immap_t *)CONFIG_SYS_IMMR)->im_cpm)->cp_dpmem
 109                        [CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER];
 110
 111/* **************************************************************************
 112 *
 113 *  Function:    spi_init_f
 114 *
 115 *  Description: Init SPI-Controller (ROM part)
 116 *
 117 *  return:      ---
 118 *
 119 * *********************************************************************** */
 120void spi_init_f (void)
 121{
 122        unsigned int dpaddr;
 123
 124        volatile spi_t *spi;
 125        volatile immap_t *immr;
 126        volatile cpm8xx_t *cp;
 127        volatile cbd_t *tbdf, *rbdf;
 128
 129        immr = (immap_t *)  CONFIG_SYS_IMMR;
 130        cp   = (cpm8xx_t *) &immr->im_cpm;
 131
 132#ifdef CONFIG_SYS_SPI_UCODE_PATCH
 133        spi  = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase];
 134#else
 135        spi  = (spi_t *)&cp->cp_dparam[PROFF_SPI];
 136        /* Disable relocation */
 137        spi->spi_rpbase = 0;
 138#endif
 139
 140/* 1 */
 141        /* ------------------------------------------------
 142         * Initialize Port B SPI pins -> page 34-8 MPC860UM
 143         * (we are only in Master Mode !)
 144         * ------------------------------------------------ */
 145
 146        /* --------------------------------------------
 147         * GPIO or per. Function
 148         * PBPAR[28] = 1 [0x00000008] -> PERI: (SPIMISO)
 149         * PBPAR[29] = 1 [0x00000004] -> PERI: (SPIMOSI)
 150         * PBPAR[30] = 1 [0x00000002] -> PERI: (SPICLK)
 151         * PBPAR[31] = 0 [0x00000001] -> GPIO: (CS for PCUE/CCM-EEPROM)
 152         * -------------------------------------------- */
 153        cp->cp_pbpar |=  0x0000000E;    /* set  bits    */
 154        cp->cp_pbpar &= ~0x00000001;    /* reset bit    */
 155
 156        /* ----------------------------------------------
 157         * In/Out or per. Function 0/1
 158         * PBDIR[28] = 1 [0x00000008] -> PERI1: SPIMISO
 159         * PBDIR[29] = 1 [0x00000004] -> PERI1: SPIMOSI
 160         * PBDIR[30] = 1 [0x00000002] -> PERI1: SPICLK
 161         * PBDIR[31] = 1 [0x00000001] -> GPIO OUT: CS for PCUE/CCM-EEPROM
 162         * ---------------------------------------------- */
 163        cp->cp_pbdir |= 0x0000000F;
 164
 165        /* ----------------------------------------------
 166         * open drain or active output
 167         * PBODR[28] = 1 [0x00000008] -> open drain: SPIMISO
 168         * PBODR[29] = 0 [0x00000004] -> active output SPIMOSI
 169         * PBODR[30] = 0 [0x00000002] -> active output: SPICLK
 170         * PBODR[31] = 0 [0x00000001] -> active output: GPIO OUT: CS for PCUE/CCM
 171         * ---------------------------------------------- */
 172
 173        cp->cp_pbodr |=  0x00000008;
 174        cp->cp_pbodr &= ~0x00000007;
 175
 176        /* Initialize the parameter ram.
 177         * We need to make sure many things are initialized to zero
 178         */
 179        spi->spi_rstate = 0;
 180        spi->spi_rdp    = 0;
 181        spi->spi_rbptr  = 0;
 182        spi->spi_rbc    = 0;
 183        spi->spi_rxtmp  = 0;
 184        spi->spi_tstate = 0;
 185        spi->spi_tdp    = 0;
 186        spi->spi_tbptr  = 0;
 187        spi->spi_tbc    = 0;
 188        spi->spi_txtmp  = 0;
 189
 190        /* Allocate space for one transmit and one receive buffer
 191         * descriptor in the DP ram
 192         */
 193#ifdef CONFIG_SYS_ALLOC_DPRAM
 194        dpaddr = dpram_alloc_align (sizeof(cbd_t)*2, 8);
 195#else
 196        dpaddr = CPM_SPI_BASE;
 197#endif
 198
 199/* 3 */
 200        /* Set up the SPI parameters in the parameter ram */
 201        spi->spi_rbase = dpaddr;
 202        spi->spi_tbase = dpaddr + sizeof (cbd_t);
 203
 204        /***********IMPORTANT******************/
 205
 206        /*
 207         * Setting transmit and receive buffer descriptor pointers
 208         * initially to rbase and tbase. Only the microcode patches
 209         * documentation talks about initializing this pointer. This
 210         * is missing from the sample I2C driver. If you dont
 211         * initialize these pointers, the kernel hangs.
 212         */
 213        spi->spi_rbptr = spi->spi_rbase;
 214        spi->spi_tbptr = spi->spi_tbase;
 215
 216/* 4 */
 217#ifdef CONFIG_SYS_SPI_UCODE_PATCH
 218        /*
 219         *  Initialize required parameters if using microcode patch.
 220         */
 221        spi->spi_rstate = 0;
 222        spi->spi_tstate = 0;
 223#else
 224        /* Init SPI Tx + Rx Parameters */
 225        while (cp->cp_cpcr & CPM_CR_FLG)
 226                ;
 227        cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SPI, CPM_CR_INIT_TRX) | CPM_CR_FLG;
 228        while (cp->cp_cpcr & CPM_CR_FLG)
 229                ;
 230#endif  /* CONFIG_SYS_SPI_UCODE_PATCH */
 231
 232/* 5 */
 233        /* Set SDMA configuration register */
 234        immr->im_siu_conf.sc_sdcr = 0x0001;
 235
 236/* 6 */
 237        /* Set to big endian. */
 238        spi->spi_tfcr = SMC_EB;
 239        spi->spi_rfcr = SMC_EB;
 240
 241/* 7 */
 242        /* Set maximum receive size. */
 243        spi->spi_mrblr = MAX_BUFFER;
 244
 245/* 8 + 9 */
 246        /* tx and rx buffer descriptors */
 247        tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase];
 248        rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase];
 249
 250        tbdf->cbd_sc &= ~BD_SC_READY;
 251        rbdf->cbd_sc &= ~BD_SC_EMPTY;
 252
 253        /* Set the bd's rx and tx buffer address pointers */
 254        rbdf->cbd_bufaddr = (ulong) rxbuf;
 255        tbdf->cbd_bufaddr = (ulong) txbuf;
 256
 257/* 10 + 11 */
 258        cp->cp_spim = 0;                        /* Mask  all SPI events */
 259        cp->cp_spie = SPI_EMASK;                /* Clear all SPI events */
 260
 261        return;
 262}
 263
 264/* **************************************************************************
 265 *
 266 *  Function:    spi_init_r
 267 *
 268 *  Description: Init SPI-Controller (RAM part) -
 269 *               The malloc engine is ready and we can move our buffers to
 270 *               normal RAM
 271 *
 272 *  return:      ---
 273 *
 274 * *********************************************************************** */
 275void spi_init_r (void)
 276{
 277        volatile cpm8xx_t *cp;
 278        volatile spi_t *spi;
 279        volatile immap_t *immr;
 280        volatile cbd_t *tbdf, *rbdf;
 281
 282        immr = (immap_t *)  CONFIG_SYS_IMMR;
 283        cp   = (cpm8xx_t *) &immr->im_cpm;
 284
 285#ifdef CONFIG_SYS_SPI_UCODE_PATCH
 286        spi  = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase];
 287#else
 288        spi  = (spi_t *)&cp->cp_dparam[PROFF_SPI];
 289        /* Disable relocation */
 290        spi->spi_rpbase = 0;
 291#endif
 292
 293        /* tx and rx buffer descriptors */
 294        tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase];
 295        rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase];
 296
 297        /* Allocate memory for RX and TX buffers */
 298        rxbuf = (uchar *) malloc (MAX_BUFFER);
 299        txbuf = (uchar *) malloc (MAX_BUFFER);
 300
 301        rbdf->cbd_bufaddr = (ulong) rxbuf;
 302        tbdf->cbd_bufaddr = (ulong) txbuf;
 303
 304        return;
 305}
 306
 307/****************************************************************************
 308 *  Function:    spi_write
 309 **************************************************************************** */
 310ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len)
 311{
 312        int i;
 313
 314        memset(rxbuf, 0, MAX_BUFFER);
 315        memset(txbuf, 0, MAX_BUFFER);
 316        *txbuf = SPI_EEPROM_WREN;               /* write enable         */
 317        spi_xfer(1);
 318        memcpy(txbuf, addr, alen);
 319        *txbuf = SPI_EEPROM_WRITE;              /* WRITE memory array   */
 320        memcpy(alen + txbuf, buffer, len);
 321        spi_xfer(alen + len);
 322                                                /* ignore received data */
 323        for (i = 0; i < 1000; i++) {
 324                *txbuf = SPI_EEPROM_RDSR;       /* read status          */
 325                txbuf[1] = 0;
 326                spi_xfer(2);
 327                if (!(rxbuf[1] & 1)) {
 328                        break;
 329                }
 330                udelay(1000);
 331        }
 332        if (i >= 1000) {
 333                printf ("*** spi_write: Time out while writing!\n");
 334        }
 335
 336        return len;
 337}
 338
 339/****************************************************************************
 340 *  Function:    spi_read
 341 **************************************************************************** */
 342ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len)
 343{
 344        memset(rxbuf, 0, MAX_BUFFER);
 345        memset(txbuf, 0, MAX_BUFFER);
 346        memcpy(txbuf, addr, alen);
 347        *txbuf = SPI_EEPROM_READ;               /* READ memory array    */
 348
 349        /*
 350         * There is a bug in 860T (?) that cuts the last byte of input
 351         * if we're reading into DPRAM. The solution we choose here is
 352         * to always read len+1 bytes (we have one extra byte at the
 353         * end of the buffer).
 354         */
 355        spi_xfer(alen + len + 1);
 356        memcpy(buffer, alen + rxbuf, len);
 357
 358        return len;
 359}
 360
 361/****************************************************************************
 362 *  Function:    spi_xfer
 363 **************************************************************************** */
 364ssize_t spi_xfer (size_t count)
 365{
 366        volatile immap_t *immr;
 367        volatile cpm8xx_t *cp;
 368        volatile spi_t *spi;
 369        cbd_t *tbdf, *rbdf;
 370        ushort loop;
 371        int tm;
 372
 373        DPRINT (("*** spi_xfer entered ***\n"));
 374
 375        immr = (immap_t *) CONFIG_SYS_IMMR;
 376        cp   = (cpm8xx_t *) &immr->im_cpm;
 377
 378#ifdef CONFIG_SYS_SPI_UCODE_PATCH
 379        spi  = (spi_t *)&cp->cp_dpmem[spi->spi_rpbase];
 380#else
 381        spi  = (spi_t *)&cp->cp_dparam[PROFF_SPI];
 382        /* Disable relocation */
 383        spi->spi_rpbase = 0;
 384#endif
 385
 386        tbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_tbase];
 387        rbdf = (cbd_t *) & cp->cp_dpmem[spi->spi_rbase];
 388
 389        /* Set CS for device */
 390        cp->cp_pbdat &= ~0x0001;
 391
 392        /* Setting tx bd status and data length */
 393        tbdf->cbd_sc  = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP;
 394        tbdf->cbd_datlen = count;
 395
 396        DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n",
 397                                                        tbdf->cbd_datlen));
 398
 399        /* Setting rx bd status and data length */
 400        rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
 401        rbdf->cbd_datlen = 0;    /* rx length has no significance */
 402
 403        loop = cp->cp_spmode & SPMODE_LOOP;
 404        cp->cp_spmode = /*SPMODE_DIV16  |*/     /* BRG/16 mode not used here */
 405                        loop            |
 406                        SPMODE_REV      |
 407                        SPMODE_MSTR     |
 408                        SPMODE_EN       |
 409                        SPMODE_LEN(8)   |       /* 8 Bits per char */
 410                        SPMODE_PM(0x8) ;        /* medium speed */
 411        cp->cp_spim = 0;                        /* Mask  all SPI events */
 412        cp->cp_spie = SPI_EMASK;                /* Clear all SPI events */
 413
 414        /* start spi transfer */
 415        DPRINT (("*** spi_xfer: Performing transfer ...\n"));
 416        cp->cp_spcom |= SPI_STR;                /* Start transmit */
 417
 418        /* --------------------------------
 419         * Wait for SPI transmit to get out
 420         * or time out (1 second = 1000 ms)
 421         * -------------------------------- */
 422        for (tm=0; tm<1000; ++tm) {
 423                if (cp->cp_spie & SPI_TXB) {    /* Tx Buffer Empty */
 424                        DPRINT (("*** spi_xfer: Tx buffer empty\n"));
 425                        break;
 426                }
 427                if ((tbdf->cbd_sc & BD_SC_READY) == 0) {
 428                        DPRINT (("*** spi_xfer: Tx BD done\n"));
 429                        break;
 430                }
 431                udelay (1000);
 432        }
 433        if (tm >= 1000) {
 434                printf ("*** spi_xfer: Time out while xferring to/from SPI!\n");
 435        }
 436        DPRINT (("*** spi_xfer: ... transfer ended\n"));
 437
 438#ifdef  DEBUG
 439        printf ("\nspi_xfer: txbuf after xfer\n");
 440        memdump ((void *) txbuf, 16);   /* dump of txbuf before transmit */
 441        printf ("spi_xfer: rxbuf after xfer\n");
 442        memdump ((void *) rxbuf, 16);   /* dump of rxbuf after transmit */
 443        printf ("\n");
 444#endif
 445
 446        /* Clear CS for device */
 447        cp->cp_pbdat |= 0x0001;
 448
 449        return count;
 450}
 451#endif  /* CONFIG_SPI || (CONFIG_POST & CONFIG_SYS_POST_SPI) */
 452
 453/*
 454 * SPI test
 455 *
 456 * The Serial Peripheral Interface (SPI) is tested in the local loopback mode.
 457 * The interface is configured accordingly and several packets
 458 * are transfered. The configurable test parameters are:
 459 *   TEST_MIN_LENGTH - minimum size of packet to transfer
 460 *   TEST_MAX_LENGTH - maximum size of packet to transfer
 461 *   TEST_NUM - number of tests
 462 */
 463
 464#if CONFIG_POST & CONFIG_SYS_POST_SPI
 465
 466#define TEST_MIN_LENGTH         1
 467#define TEST_MAX_LENGTH         MAX_BUFFER
 468#define TEST_NUM                1
 469
 470static void packet_fill (char * packet, int length)
 471{
 472        char c = (char) length;
 473        int i;
 474
 475        for (i = 0; i < length; i++)
 476        {
 477            packet[i] = c++;
 478        }
 479}
 480
 481static int packet_check (char * packet, int length)
 482{
 483        char c = (char) length;
 484        int i;
 485
 486        for (i = 0; i < length; i++) {
 487            if (packet[i] != c++) return -1;
 488        }
 489
 490        return 0;
 491}
 492
 493int spi_post_test (int flags)
 494{
 495        int res = -1;
 496        volatile immap_t *immr = (immap_t *) CONFIG_SYS_IMMR;
 497        volatile cpm8xx_t *cp = (cpm8xx_t *) & immr->im_cpm;
 498        int i;
 499        int l;
 500
 501        spi_init_f ();
 502        spi_init_r ();
 503
 504        cp->cp_spmode |= SPMODE_LOOP;
 505
 506        for (i = 0; i < TEST_NUM; i++) {
 507                for (l = TEST_MIN_LENGTH; l <= TEST_MAX_LENGTH; l += 8) {
 508                        packet_fill ((char *)txbuf, l);
 509
 510                        spi_xfer (l);
 511
 512                        if (packet_check ((char *)rxbuf, l) < 0) {
 513                                goto Done;
 514                        }
 515                }
 516        }
 517
 518        res = 0;
 519
 520      Done:
 521
 522        cp->cp_spmode &= ~SPMODE_LOOP;
 523
 524        /*
 525         * SCC2 parameter RAM space overlaps
 526         * the SPI parameter RAM space. So we need to restore
 527         * the SCC2 configuration if it is used by UART.
 528         */
 529
 530#if !defined(CONFIG_8xx_CONS_NONE)
 531        serial_reinit_all ();
 532#endif
 533
 534        if (res != 0) {
 535                post_log ("SPI test failed\n");
 536        }
 537
 538        return res;
 539}
 540#endif  /* CONFIG_POST & CONFIG_SYS_POST_SPI */
 541