[U-Boot] [PATCH v3 1/2] Freescale 85xx/P1/P2 eSPI controller driver

From: Can Aydin can.aydin@locatacorp.com
Driver for the Freescale eSPI controller found in 85xx, P1/P2 and P4xx SoCs.
Signed-off-by: Can Aydin can.aydin@locatacorp.com --- Changes for v2: - Coding style cleanup - Removed modifications to common code
Changes for v3: - fixed whitespace between function calls and parameters - replaced unneeded byte copy test function with memcpy - removed dead code and C++ style comments - separated board configuration patch from driver patch
drivers/spi/Makefile | 1 + drivers/spi/fsl_espi.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++ include/fsl_espi.h | 50 ++++++++++ 3 files changed, 291 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/fsl_espi.c create mode 100644 include/fsl_espi.h
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index dfcbb8b..1aaa8e7 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -31,6 +31,7 @@ COBJS-$(CONFIG_ATMEL_SPI) += atmel_spi.o COBJS-$(CONFIG_BFIN_SPI) += bfin_spi.o COBJS-$(CONFIG_CF_SPI) += cf_spi.o COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o +COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o diff --git a/drivers/spi/fsl_espi.c b/drivers/spi/fsl_espi.c new file mode 100644 index 0000000..749d3f1 --- /dev/null +++ b/drivers/spi/fsl_espi.c @@ -0,0 +1,240 @@ +/* + * Freescale eSPI controller driver. + * + * Copyright 2010 Locata Corporation Pty. Ltd. + * Author: Can Aydin can.aydin@locatacorp.com + * Collab: Clayton Gumbrell clayton.gumbrell@locatacorp.com + * + * Adapted from Freescale ltib code by Mingkai Hu (Mingkai.hu@freescale.com) + * Copyright 2009 Freescale Semiconductor, Inc. + * + * 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 <malloc.h> +#include <spi.h> +#include <fsl_espi.h> +#include <asm/immap_85xx.h> + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + volatile ccsr_espi_t *espi = (void *)(CONFIG_SYS_MPC85xx_ESPI_ADDR); + struct spi_slave *slave; + sys_info_t sysinfo; + unsigned long spibrg = 0; + unsigned char pm = 0; + int i; + + if (!spi_cs_is_valid(bus, cs)) + return NULL; + + slave = malloc(sizeof(struct spi_slave)); + if (!slave) + return NULL; + + slave->bus = bus; + slave->cs = cs; + + /* Enable eSPI interface */ + espi->mode = ESPI_MODE_RXTHR(3) | ESPI_MODE_TXTHR(4) | ESPI_MODE_EN; + + espi->event = 0xffffffff; /* Clear all eSPI events */ + espi->mask = 0x00000000; /* Mask all eSPI interrupts */ + + /* Init CS mode interface */ + for (i = 0; i < ESPI_MAX_CS_NUM; i++) + espi->csmode[i] = ESPI_CSMODE_INIT_VAL; + + espi->csmode[cs] &= + ~(ESPI_CSMODE_PM(0xF) | ESPI_CSMODE_DIV16 + | ESPI_CSMODE_CI_INACTIVEHIGH | ESPI_CSMODE_CP_BEGIN_EDGCLK + | ESPI_CSMODE_REV_MSB_FIRST | ESPI_CSMODE_LEN(ESPI_MAX_CHARSIZE)); + + /* Set eSPI BRG clock source */ + get_sys_info(&sysinfo); + spibrg = sysinfo.freqSystemBus / 2; + if ((spibrg / max_hz) > 32) { + espi->csmode[cs] |= ESPI_CSMODE_DIV16; + pm = spibrg / (max_hz * 16 * 2); + if (pm > 16) { + pm = 16; + debug("Requested speed is too low: %d Hz" + " %u Hz is used.\n", max_hz, + (uint) (spibrg / (32 * 16))); + } + } else + pm = spibrg / (max_hz * 2); + if (pm) + pm--; + espi->csmode[cs] |= ESPI_CSMODE_PM(pm); + + /* Set eSPI mode */ + if (mode & SPI_CPHA) + espi->csmode[cs] |= ESPI_CSMODE_CP_BEGIN_EDGCLK; + if (mode & SPI_CPOL) + espi->csmode[cs] |= ESPI_CSMODE_CI_INACTIVEHIGH; + + /* Character bit order: msb first */ + espi->csmode[cs] |= ESPI_CSMODE_REV_MSB_FIRST; + + /* Character length in bits, between 0x3~0xf, i.e. 4bits~16bits */ + espi->csmode[cs] |= ESPI_CSMODE_LEN(ESPI_CHARSIZE); + + return slave; +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, + void *din, unsigned long flags) +{ + volatile ccsr_espi_t *espi = (void *)(CONFIG_SYS_MPC85xx_ESPI_ADDR); + u32 com; + u32 event; + u8 wordsize = 4; /* Fifo access registers are 32 bits */ + u32 tmpdout, tmpdin; + unsigned int len, rxcount, txcount; + + if (!(flags & (SPI_XFER_BEGIN | SPI_XFER_END))) { + debug("spi_xfer: Segmented transfers not supported.\n"); + return 1; + } + + if (!bitlen) + return 0; + + len = bitlen / ESPI_CHARSIZE + ((bitlen % ESPI_CHARSIZE) ? 1 : 0); + + if (len > ESPI_MAX_TRANLEN) { + debug("spi_xfer: oversize xfer(%u), max %u(0x%X)\n", + len, ESPI_MAX_TRANLEN, ESPI_MAX_TRANLEN); + return 1; + } + + rxcount = len; + txcount = len; + debug("spi_xfer: slave %u:%u dout %08X(%08x) din %08X(%08x) len %u\n", + slave->bus, slave->cs, + *(uint *) dout, (uint) dout, *(uint *) din, (uint) din, len); + + /* Prepare eSPI transfer to slave cs with length len */ + com = 0; + com |= ESPI_COM_CS(slave->cs); + com |= ESPI_COM_TRANLEN(len); + espi->com = com; + + /* Clear all eSPI events and initiate new command */ + espi->event = 0xffffffff; + debugX(1, "spi_xfer: rxcount %08X, txcount %08X\n", rxcount, txcount); + while (rxcount) { + + event = espi->event; + debugX(4, "spi_xfer: SPIE 0x%08x\n", event); + debugX(3, "spi_xfer: SPIE (TXCNT): %u\n", + ESPI_EV_GET_TXCNT(event)); + if ((event & ESPI_EV_TNF) && (txcount)) { + /* Tx fifo not full and we still have stuff to send */ + if ((txcount >= wordsize) && + (ESPI_EV_GET_TXCNT(event) >= wordsize)) { + /* Have a full word and room to put it */ + tmpdout = *(u32 *) dout; + txcount -= wordsize; + dout += wordsize; + espi->tx = tmpdout; + } else if (ESPI_EV_GET_TXCNT(event) >= txcount) { + /* Less than a word left and room to put it */ + tmpdout = 0; + /* Avoid dout buffer read overflow */ + memcpy(&tmpdout, dout, txcount); + espi->tx = tmpdout; + txcount = 0; + } + espi->event |= ESPI_EV_TNF; + debugX(2, "*** spi_xfer: ... %08x written\n", tmpdout); + debugX(3, "spi_xfer: rxcount %08X, txcount %08X\n", + rxcount, txcount); + } + + event = espi->event; + debugX(4, "spi_xfer: SPIE 0x%08x\n", event); + debugX(3, "spi_xfer: SPIE (RXCNT): %u\n", + ESPI_EV_GET_RXCNT(event)); + if (event & ESPI_EV_RNE) { /* Rx fifo has data */ + unsigned int count = ESPI_EV_GET_RXCNT(event); + if (count >= wordsize) { + /* Have a full word to read */ + tmpdin = espi->rx; + *(u32 *) din = tmpdin; + if ((rxcount -= wordsize) > 0) { + din += wordsize; + } + } else if (count >= rxcount) { + /* Less than a word left to read */ + tmpdin = espi->rx; + /* Avoid din buffer write overflow */ + memcpy(din, &tmpdin, rxcount); + rxcount = 0; + } + debugX(2, "*** spi_xfer: ... %08x read\n", tmpdin); + debugX(3, "spi_xfer: rxcount %08X, txcount %08X\n", + rxcount, txcount); + espi->event |= ESPI_EV_RNE; + } + + } + + /* clear the RXCNT and TXCNT */ + espi->mode &= ~ESPI_MODE_EN; + espi->mode |= ESPI_MODE_EN; + + return 0; +} + +void spi_free_slave(struct spi_slave *slave) +{ + free((struct espi_slave *)slave); +} + +void spi_init(void) +{ + +} + +int spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + return bus == 0 && cs < ESPI_MAX_CS_NUM; +} + +void spi_cs_activate(struct spi_slave *slave) +{ + +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + +} diff --git a/include/fsl_espi.h b/include/fsl_espi.h new file mode 100644 index 0000000..a8d6035 --- /dev/null +++ b/include/fsl_espi.h @@ -0,0 +1,50 @@ +/* + * Include header for Freescale eSPI controller + * + * Copyright 2010 Locata Corporation Pty. Ltd. + * Can Aydin can.aydin@locatacorp.com + * + * Adapted from Freescale ltib code by Mingkai Hu (Mingkai.hu@freescale.com) + * Copyright 2009 Freescale Semiconductor, Inc. + */ + +#ifndef __FSL_ESPI_H__ +#define __FSL_ESPI_H__ + +#define ESPI_MAX_CS_NUM 4 + +#define ESPI_MAX_TRANLEN 0x10000 +#define ESPI_MAX_CHARSIZE 16 + +/* TODO: Add support for character sizes other than 8-bits */ +#define ESPI_CHARSIZE 8 + +#define ESPI_EV_TNF (1 << 8) /* Tx fifo not full */ +#define ESPI_EV_RNE (1 << 9) /* Rx fifo not empty */ +#define ESPI_EV_DONE (1 << 14) /* Transfer Complete */ +#define ESPI_EV_GET_TXCNT(spie) (((spie) >> 16) & 0x3F) /* Tx count */ +#define ESPI_EV_GET_RXCNT(spie) (((spie) >> 24) & 0x3F) /* Rx count */ + +#define ESPI_MODE_EN (1 << 31) /* Enable interface */ +#define ESPI_MODE_TXTHR(x) ((x) << 8) /* Tx FIFO threshold */ +#define ESPI_MODE_RXTHR(x) ((x) << 0) /* Rx FIFO threshold */ + +#define ESPI_COM_CS(x) ((x) << 30) +#define ESPI_COM_TRANLEN(x) (((x)-1) << 0) + +#define ESPI_CSMODE_CI_INACTIVEHIGH (1 << 31) +#define ESPI_CSMODE_CP_BEGIN_EDGCLK (1 << 30) +#define ESPI_CSMODE_REV_MSB_FIRST (1 << 29) +#define ESPI_CSMODE_DIV16 (1 << 28) +#define ESPI_CSMODE_PM(x) ((x) << 24) +#define ESPI_CSMODE_POL_ASSERTED_LOW (1 << 20) +#define ESPI_CSMODE_LEN(x) (((x)-1) << 16) +#define ESPI_CSMODE_CSBEF(x) ((x) << 12) +#define ESPI_CSMODE_CSAFT(x) ((x) << 8) +#define ESPI_CSMODE_CSCG(x) ((x) << 3) + +#define ESPI_CSMODE_INIT_VAL (ESPI_CSMODE_POL_ASSERTED_LOW | \ + ESPI_CSMODE_CSBEF(0) | ESPI_CSMODE_CSAFT(0) | \ + ESPI_CSMODE_CSCG(1)) + +#endif /* __FSL_ESPI_H__ */
participants (1)
-
caydinc@gmail.com