Add qemu 2.4.0
[kvmfornfv.git] / qemu / roms / u-boot / arch / powerpc / cpu / mpc8260 / spi.c
diff --git a/qemu/roms/u-boot/arch/powerpc/cpu/mpc8260/spi.c b/qemu/roms/u-boot/arch/powerpc/cpu/mpc8260/spi.c
new file mode 100644 (file)
index 0000000..8c91a71
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2001 Navin Boppuri / Prashant Patel
+ *     <nboppuri@trinetcommunication.com>,
+ *     <pmpatel@trinetcommunication.com>
+ * Copyright (c) 2001 Gerd Mennchen <Gerd.Mennchen@icn.siemens.de>
+ * Copyright (c) 2001-2003 Wolfgang Denk, DENX Software Engineering, <wd@denx.de>.
+ *
+ * SPDX-License-Identifier:    GPL-2.0+
+ */
+
+/*
+ * MPC8260 CPM SPI interface.
+ *
+ * Parts of this code are probably not portable and/or specific to
+ * the board which I used for the tests. Please send fixes/complaints
+ * to wd@denx.de
+ *
+ */
+
+#include <common.h>
+#include <asm/cpm_8260.h>
+#include <linux/ctype.h>
+#include <malloc.h>
+#include <post.h>
+#include <net.h>
+
+#if defined(CONFIG_SPI)
+
+/* Warning:
+ * You cannot enable DEBUG for early system initalization, i. e. when
+ * this driver is used to read environment parameters like "baudrate"
+ * from EEPROM which are used to initialize the serial port which is
+ * needed to print the debug messages...
+ */
+#undef DEBUG
+
+#define SPI_EEPROM_WREN                0x06
+#define SPI_EEPROM_RDSR                0x05
+#define SPI_EEPROM_READ                0x03
+#define SPI_EEPROM_WRITE       0x02
+
+/* ---------------------------------------------------------------
+ * Offset for initial SPI buffers in DPRAM:
+ * We need a 520 byte scratch DPRAM area to use at an early stage.
+ * It is used between the two initialization calls (spi_init_f()
+ * and spi_init_r()).
+ * The value 0x2000 makes it far enough from the start of the data
+ * area (as well as from the stack pointer).
+ * --------------------------------------------------------------- */
+#ifndef        CONFIG_SYS_SPI_INIT_OFFSET
+#define        CONFIG_SYS_SPI_INIT_OFFSET      0x2000
+#endif
+
+#define CPM_SPI_BASE 0x100
+
+#ifdef DEBUG
+
+#define        DPRINT(a)       printf a;
+/* -----------------------------------------------
+ * Helper functions to peek into tx and rx buffers
+ * ----------------------------------------------- */
+static const char * const hex_digit = "0123456789ABCDEF";
+
+static char quickhex (int i)
+{
+       return hex_digit[i];
+}
+
+static void memdump (void *pv, int num)
+{
+       int i;
+       unsigned char *pc = (unsigned char *) pv;
+
+       for (i = 0; i < num; i++)
+               printf ("%c%c ", quickhex (pc[i] >> 4), quickhex (pc[i] & 0x0f));
+       printf ("\t");
+       for (i = 0; i < num; i++)
+               printf ("%c", isprint (pc[i]) ? pc[i] : '.');
+       printf ("\n");
+}
+#else  /* !DEBUG */
+
+#define        DPRINT(a)
+
+#endif /* DEBUG */
+
+/* -------------------
+ * Function prototypes
+ * ------------------- */
+void spi_init (void);
+
+ssize_t spi_read (uchar *, int, uchar *, int);
+ssize_t spi_write (uchar *, int, uchar *, int);
+ssize_t spi_xfer (size_t);
+
+/* -------------------
+ * Variables
+ * ------------------- */
+
+#define MAX_BUFFER     0x104
+
+/* ----------------------------------------------------------------------
+ * Initially we place the RX and TX buffers at a fixed location in DPRAM!
+ * ---------------------------------------------------------------------- */
+static uchar *rxbuf =
+  (uchar *)&((immap_t *)CONFIG_SYS_IMMR)->im_dprambase
+                       [CONFIG_SYS_SPI_INIT_OFFSET];
+static uchar *txbuf =
+  (uchar *)&((immap_t *)CONFIG_SYS_IMMR)->im_dprambase
+                       [CONFIG_SYS_SPI_INIT_OFFSET+MAX_BUFFER];
+
+/* **************************************************************************
+ *
+ *  Function:    spi_init_f
+ *
+ *  Description: Init SPI-Controller (ROM part)
+ *
+ *  return:      ---
+ *
+ * *********************************************************************** */
+void spi_init_f (void)
+{
+       unsigned int dpaddr;
+
+       volatile spi_t *spi;
+       volatile immap_t *immr;
+       volatile cpm8260_t *cp;
+       volatile cbd_t *tbdf, *rbdf;
+
+       immr = (immap_t *)  CONFIG_SYS_IMMR;
+       cp   = (cpm8260_t *) &immr->im_cpm;
+
+       immr->im_dprambase16[PROFF_SPI_BASE / sizeof(u16)] = PROFF_SPI;
+       spi  = (spi_t *)&immr->im_dprambase[PROFF_SPI];
+
+/* 1 */
+       /* ------------------------------------------------
+        * Initialize Port D SPI pins
+        * (we are only in Master Mode !)
+        * ------------------------------------------------ */
+
+       /* --------------------------------------------
+        * GPIO or per. Function
+        * PPARD[16] = 1 [0x00008000] (SPIMISO)
+        * PPARD[17] = 1 [0x00004000] (SPIMOSI)
+        * PPARD[18] = 1 [0x00002000] (SPICLK)
+        * PPARD[12] = 0 [0x00080000] -> GPIO: (CS for ATC EEPROM)
+        * -------------------------------------------- */
+       immr->im_ioport.iop_ppard |=  0x0000E000;       /* set  bits    */
+       immr->im_ioport.iop_ppard &= ~0x00080000;       /* reset bit    */
+
+       /* ----------------------------------------------
+        * In/Out or per. Function 0/1
+        * PDIRD[16] = 0 [0x00008000] -> PERI1: SPIMISO
+        * PDIRD[17] = 0 [0x00004000] -> PERI1: SPIMOSI
+        * PDIRD[18] = 0 [0x00002000] -> PERI1: SPICLK
+        * PDIRD[12] = 1 [0x00080000] -> GPIO OUT: CS for ATC EEPROM
+        * ---------------------------------------------- */
+       immr->im_ioport.iop_pdird &= ~0x0000E000;
+       immr->im_ioport.iop_pdird |= 0x00080000;
+
+       /* ----------------------------------------------
+        * special option reg.
+        * PSORD[16] = 1 [0x00008000] -> SPIMISO
+        * PSORD[17] = 1 [0x00004000] -> SPIMOSI
+        * PSORD[18] = 1 [0x00002000] -> SPICLK
+        * ---------------------------------------------- */
+       immr->im_ioport.iop_psord |= 0x0000E000;
+
+       /* Initialize the parameter ram.
+        * We need to make sure many things are initialized to zero
+        */
+       spi->spi_rstate = 0;
+       spi->spi_rdp    = 0;
+       spi->spi_rbptr  = 0;
+       spi->spi_rbc    = 0;
+       spi->spi_rxtmp  = 0;
+       spi->spi_tstate = 0;
+       spi->spi_tdp    = 0;
+       spi->spi_tbptr  = 0;
+       spi->spi_tbc    = 0;
+       spi->spi_txtmp  = 0;
+
+       /* Allocate space for one transmit and one receive buffer
+        * descriptor in the DP ram
+        */
+#ifdef CONFIG_SYS_ALLOC_DPRAM
+       dpaddr = m8260_cpm_dpalloc (sizeof(cbd_t)*2, 8);
+#else
+       dpaddr = CPM_SPI_BASE;
+#endif
+
+/* 3 */
+       /* Set up the SPI parameters in the parameter ram */
+       spi->spi_rbase = dpaddr;
+       spi->spi_tbase = dpaddr + sizeof (cbd_t);
+
+       /***********IMPORTANT******************/
+
+       /*
+        * Setting transmit and receive buffer descriptor pointers
+        * initially to rbase and tbase. Only the microcode patches
+        * documentation talks about initializing this pointer. This
+        * is missing from the sample I2C driver. If you dont
+        * initialize these pointers, the kernel hangs.
+        */
+       spi->spi_rbptr = spi->spi_rbase;
+       spi->spi_tbptr = spi->spi_tbase;
+
+/* 4 */
+       /* Init SPI Tx + Rx Parameters */
+       while (cp->cp_cpcr & CPM_CR_FLG)
+               ;
+       cp->cp_cpcr = mk_cr_cmd(CPM_CR_SPI_PAGE, CPM_CR_SPI_SBLOCK,
+                                                       0, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+       while (cp->cp_cpcr & CPM_CR_FLG)
+               ;
+
+/* 6 */
+       /* Set to big endian. */
+       spi->spi_tfcr = CPMFCR_EB;
+       spi->spi_rfcr = CPMFCR_EB;
+
+/* 7 */
+       /* Set maximum receive size. */
+       spi->spi_mrblr = MAX_BUFFER;
+
+/* 8 + 9 */
+       /* tx and rx buffer descriptors */
+       tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase];
+       rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase];
+
+       tbdf->cbd_sc &= ~BD_SC_READY;
+       rbdf->cbd_sc &= ~BD_SC_EMPTY;
+
+       /* Set the bd's rx and tx buffer address pointers */
+       rbdf->cbd_bufaddr = (ulong) rxbuf;
+       tbdf->cbd_bufaddr = (ulong) txbuf;
+
+/* 10 + 11 */
+       immr->im_spi.spi_spie = SPI_EMASK;              /* Clear all SPI events */
+       immr->im_spi.spi_spim = 0x00;                   /* Mask  all SPI events */
+
+
+       return;
+}
+
+/* **************************************************************************
+ *
+ *  Function:    spi_init_r
+ *
+ *  Description: Init SPI-Controller (RAM part) -
+ *              The malloc engine is ready and we can move our buffers to
+ *              normal RAM
+ *
+ *  return:      ---
+ *
+ * *********************************************************************** */
+void spi_init_r (void)
+{
+       volatile spi_t *spi;
+       volatile immap_t *immr;
+       volatile cbd_t *tbdf, *rbdf;
+
+       immr = (immap_t *)  CONFIG_SYS_IMMR;
+
+       spi  = (spi_t *)&immr->im_dprambase[PROFF_SPI];
+
+       /* tx and rx buffer descriptors */
+       tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase];
+       rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase];
+
+       /* Allocate memory for RX and TX buffers */
+       rxbuf = (uchar *) malloc (MAX_BUFFER);
+       txbuf = (uchar *) malloc (MAX_BUFFER);
+
+       rbdf->cbd_bufaddr = (ulong) rxbuf;
+       tbdf->cbd_bufaddr = (ulong) txbuf;
+
+       return;
+}
+
+/****************************************************************************
+ *  Function:    spi_write
+ **************************************************************************** */
+ssize_t spi_write (uchar *addr, int alen, uchar *buffer, int len)
+{
+       int i;
+
+       memset(rxbuf, 0, MAX_BUFFER);
+       memset(txbuf, 0, MAX_BUFFER);
+       *txbuf = SPI_EEPROM_WREN;               /* write enable         */
+       spi_xfer(1);
+       memcpy(txbuf, addr, alen);
+       *txbuf = SPI_EEPROM_WRITE;              /* WRITE memory array   */
+       memcpy(alen + txbuf, buffer, len);
+       spi_xfer(alen + len);
+                                               /* ignore received data */
+       for (i = 0; i < 1000; i++) {
+               *txbuf = SPI_EEPROM_RDSR;       /* read status          */
+               txbuf[1] = 0;
+               spi_xfer(2);
+               if (!(rxbuf[1] & 1)) {
+                       break;
+               }
+               udelay(1000);
+       }
+       if (i >= 1000) {
+               printf ("*** spi_write: Time out while writing!\n");
+       }
+
+       return len;
+}
+
+/****************************************************************************
+ *  Function:    spi_read
+ **************************************************************************** */
+ssize_t spi_read (uchar *addr, int alen, uchar *buffer, int len)
+{
+       memset(rxbuf, 0, MAX_BUFFER);
+       memset(txbuf, 0, MAX_BUFFER);
+       memcpy(txbuf, addr, alen);
+       *txbuf = SPI_EEPROM_READ;               /* READ memory array    */
+
+       /*
+        * There is a bug in 860T (?) that cuts the last byte of input
+        * if we're reading into DPRAM. The solution we choose here is
+        * to always read len+1 bytes (we have one extra byte at the
+        * end of the buffer).
+        */
+       spi_xfer(alen + len + 1);
+       memcpy(buffer, alen + rxbuf, len);
+
+       return len;
+}
+
+/****************************************************************************
+ *  Function:    spi_xfer
+ **************************************************************************** */
+ssize_t spi_xfer (size_t count)
+{
+       volatile immap_t *immr;
+       volatile spi_t *spi;
+       cbd_t *tbdf, *rbdf;
+       int tm;
+
+       DPRINT (("*** spi_xfer entered ***\n"));
+
+       immr = (immap_t *) CONFIG_SYS_IMMR;
+
+       spi  = (spi_t *)&immr->im_dprambase[PROFF_SPI];
+
+       tbdf = (cbd_t *) & immr->im_dprambase[spi->spi_tbase];
+       rbdf = (cbd_t *) & immr->im_dprambase[spi->spi_rbase];
+
+       /* Board-specific: Set CS for device (ATC EEPROM) */
+       immr->im_ioport.iop_pdatd &= ~0x00080000;
+
+       /* Setting tx bd status and data length */
+       tbdf->cbd_sc  = BD_SC_READY | BD_SC_LAST | BD_SC_WRAP;
+       tbdf->cbd_datlen = count;
+
+       DPRINT (("*** spi_xfer: Bytes to be xferred: %d ***\n",
+                                                       tbdf->cbd_datlen));
+
+       /* Setting rx bd status and data length */
+       rbdf->cbd_sc = BD_SC_EMPTY | BD_SC_WRAP;
+       rbdf->cbd_datlen = 0;    /* rx length has no significance */
+
+       immr->im_spi.spi_spmode = SPMODE_REV    |
+                       SPMODE_MSTR     |
+                       SPMODE_EN       |
+                       SPMODE_LEN(8)   |       /* 8 Bits per char */
+                       SPMODE_PM(0x8) ;        /* medium speed */
+       immr->im_spi.spi_spie = SPI_EMASK;              /* Clear all SPI events */
+       immr->im_spi.spi_spim = 0x00;                   /* Mask  all SPI events */
+
+       /* start spi transfer */
+       DPRINT (("*** spi_xfer: Performing transfer ...\n"));
+       immr->im_spi.spi_spcom |= SPI_STR;              /* Start transmit */
+
+       /* --------------------------------
+        * Wait for SPI transmit to get out
+        * or time out (1 second = 1000 ms)
+        * -------------------------------- */
+       for (tm=0; tm<1000; ++tm) {
+               if (immr->im_spi.spi_spie & SPI_TXB) {  /* Tx Buffer Empty */
+                       DPRINT (("*** spi_xfer: Tx buffer empty\n"));
+                       break;
+               }
+               if ((tbdf->cbd_sc & BD_SC_READY) == 0) {
+                       DPRINT (("*** spi_xfer: Tx BD done\n"));
+                       break;
+               }
+               udelay (1000);
+       }
+       if (tm >= 1000) {
+               printf ("*** spi_xfer: Time out while xferring to/from SPI!\n");
+       }
+       DPRINT (("*** spi_xfer: ... transfer ended\n"));
+
+#ifdef DEBUG
+       printf ("\nspi_xfer: txbuf after xfer\n");
+       memdump ((void *) txbuf, 16);   /* dump of txbuf before transmit */
+       printf ("spi_xfer: rxbuf after xfer\n");
+       memdump ((void *) rxbuf, 16);   /* dump of rxbuf after transmit */
+       printf ("\n");
+#endif
+
+       /* Clear CS for device */
+       immr->im_ioport.iop_pdatd |= 0x00080000;
+
+       return count;
+}
+#endif /* CONFIG_SPI */