@@ -38,6 +38,7 @@ COBJS-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o
COBJS-$(CONFIG_CF_SPI) += cf_spi.o
COBJS-$(CONFIG_CF_QSPI) += cf_qspi.o
COBJS-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
+COBJS-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o
COBJS-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
COBJS-$(CONFIG_ICH_SPI) += ich.o
COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
new file mode 100644
@@ -0,0 +1,385 @@
+/*
+ * Faraday Multi-function Controller - SPI Mode
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <spi.h>
+#include <malloc.h>
+
+#include <faraday/ftgpio010.h>
+#include "ftssp010_spi.h"
+
+struct ftssp010_chip {
+ void __iomem *regs;
+ uint32_t fifo;
+ uint32_t rev;
+ uint32_t div;
+ uint32_t mode;
+
+ struct {
+ void __iomem *regs;
+ uint32_t pin;
+ } gpio;
+};
+
+static struct ftssp010_chip chip_list[] = {
+#ifdef CONFIG_FTSSP010_BASE
+ {
+ .regs = (void __iomem *)CONFIG_FTSSP010_BASE,
+# ifdef CONFIG_FTSSP010_GPIO_BASE
+ .gpio = {
+ (void __iomem *)CONFIG_FTSSP010_GPIO_BASE,
+ CONFIG_FTSSP010_GPIO_PIN
+ },
+# endif
+ },
+#endif /* #ifdef CONFIG_FTSSP010_BASE */
+#ifdef CONFIG_FTSSP010_BASE1
+ { .regs = (void __iomem *)CONFIG_FTSSP010_BASE1, },
+# ifdef CONFIG_FTSSP010_GPIO_BASE1
+ .gpio = {
+ (void __iomem *)CONFIG_FTSSP010_GPIO_BASE1,
+ CONFIG_FTSSP010_GPIO_PIN1
+ },
+# endif
+#endif
+#ifdef CONFIG_FTSSP010_BASE2
+ { .regs = (void __iomem *)CONFIG_FTSSP010_BASE2, },
+# ifdef CONFIG_FTSSP010_GPIO_BASE2
+ .gpio = {
+ (void __iomem *)CONFIG_FTSSP010_GPIO_BASE2,
+ CONFIG_FTSSP010_GPIO_PIN2
+ },
+# endif
+#endif
+#ifdef CONFIG_FTSSP010_BASE3
+ { .regs = (void __iomem *)CONFIG_FTSSP010_BASE3, },
+# ifdef CONFIG_FTSSP010_GPIO_BASE3
+ .gpio = {
+ (void __iomem *)CONFIG_FTSSP010_GPIO_BASE3,
+ CONFIG_FTSSP010_GPIO_PIN3
+ },
+# endif
+#endif
+};
+
+static inline int ftssp010_wait_tx(struct ftssp010_chip *chip)
+{
+ struct ftssp010_regs __iomem *regs = chip->regs;
+ int ret = -1;
+ ulong ts;
+
+ for (ts = get_timer(0); get_timer(ts) < 100; ) {
+ if (!(readl(®s->sr) & SR_TFNF))
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ printf("ftssp010: tx timeout\n");
+
+ return ret;
+}
+
+static inline int ftssp010_wait_rx(struct ftssp010_chip *chip)
+{
+ struct ftssp010_regs __iomem *regs = chip->regs;
+ int ret = -1;
+ ulong ts;
+
+ for (ts = get_timer(0); get_timer(ts) < 100; ) {
+ if (!SR_RFVE(readl(®s->sr)))
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ printf("ftssp010: rx timeout\n");
+
+ return ret;
+}
+
+static int ftssp010_spi_work_transfer_v1_19(struct ftssp010_chip *chip,
+ const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+ struct ftssp010_regs __iomem *regs = chip->regs;
+ const uint8_t *txb = tx_buf;
+ uint8_t *rxb = rx_buf;
+
+ while (len > 0) {
+ int i, depth = min(chip->fifo >> 2, len);
+ uint32_t xmsk = 0;
+
+ if (tx_buf) {
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_tx(chip);
+ writel(*txb++, ®s->dr);
+ }
+ xmsk |= CR2_TXEN | CR2_TXDOE;
+ if ((readl(®s->cr[2]) & xmsk) != xmsk)
+ setbits_le32(®s->cr[2], xmsk);
+ }
+ if (rx_buf) {
+ xmsk |= CR2_RXEN;
+ if ((readl(®s->cr[2]) & xmsk) != xmsk)
+ setbits_le32(®s->cr[2], xmsk);
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_rx(chip);
+ *rxb++ = (uint8_t)readl(®s->dr);
+ }
+ }
+
+ len -= depth;
+ }
+
+ return 0;
+}
+
+static int ftssp010_spi_work_transfer(struct ftssp010_chip *chip,
+ const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+ struct ftssp010_regs __iomem *regs = chip->regs;
+ const uint8_t *txb = tx_buf;
+ uint8_t *rxb = rx_buf;
+
+ while (len > 0) {
+ int i, depth = min(chip->fifo >> 2, len);
+ uint32_t tmp;
+
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_tx(chip);
+ writel(txb ? (*txb++) : 0, ®s->dr);
+ }
+ for (i = 0; i < depth; ++i) {
+ ftssp010_wait_rx(chip);
+ tmp = readl(®s->dr);
+ if (rxb)
+ *rxb++ = (uint8_t)tmp;
+ }
+
+ len -= depth;
+ }
+
+ return 0;
+}
+
+/*-----------------------------------------------------------------------
+ * Determine if a SPI chipselect is valid.
+ * This function is provided by the board if the low-level SPI driver
+ * needs it to determine if a given chipselect is actually valid.
+ *
+ * Returns: 1 if bus:cs identifies a valid chip on this board, 0
+ * otherwise.
+ */
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+ int ret = 0;
+ struct ftssp010_chip *chip;
+ struct ftssp010_regs __iomem *regs;
+ uint32_t txfifo, rxfifo;
+
+ if (bus >= ARRAY_SIZE(chip_list))
+ return ret;
+
+ chip = chip_list + bus;
+ regs = chip->regs;
+ chip->rev = readl(®s->revr);
+ txfifo = FEAR_TXFIFO(readl(®s->fear));
+ rxfifo = FEAR_RXFIFO(readl(®s->fear));
+ chip->fifo = min(txfifo, rxfifo);
+
+ debug("ftssp010: rev=0x%08X, fifo=%d\n", chip->rev, chip->fifo);
+
+ if (chip->rev >= 0x00011900) {
+ if (cs < 4)
+ ret = 1;
+ } else if (!cs) {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+ struct ftgpio010_regs *gpio = chip->gpio.regs;
+ uint32_t mask = BIT_MASK(chip->gpio.pin);
+
+ /* setup gpio pin as an output pin */
+ setbits_le32(&gpio->dir, mask);
+ ret = 1;
+#endif
+ }
+
+ return ret;
+}
+
+/*-----------------------------------------------------------------------
+ * Activate a SPI chipselect.
+ * This function is provided by the board code when using a driver
+ * that can't control its chipselects automatically (e.g.
+ * common/soft_spi.c). When called, it should activate the chip select
+ * to the device identified by "slave".
+ */
+void spi_cs_activate(struct spi_slave *slave)
+{
+ struct ftssp010_chip *chip = chip_list + slave->bus;
+ struct ftssp010_regs __iomem *regs = chip->regs;
+
+ /* cs pull low */
+ if (chip->rev >= 0x00011900) {
+ writel((slave->cs << 10) | CR2_SSPEN | CR2_TXFCLR
+ | CR2_RXFCLR, ®s->cr[2]);
+ } else {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+ struct ftgpio010_regs *gpio = chip->gpio.regs;
+ uint32_t mask = BIT_MASK(chip->gpio.pin);
+
+ setbits_le32(&gpio->clr, mask);
+#endif
+ }
+ udelay_masked(1);
+}
+
+/*-----------------------------------------------------------------------
+ * Deactivate a SPI chipselect.
+ * This function is provided by the board code when using a driver
+ * that can't control its chipselects automatically (e.g.
+ * common/soft_spi.c). When called, it should deactivate the chip
+ * select to the device identified by "slave".
+ */
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ struct ftssp010_chip *chip = chip_list + slave->bus;
+ struct ftssp010_regs __iomem *regs = chip->regs;
+ ulong ts;
+
+ /* wait until device idle */
+ for (ts = get_timer(0); get_timer(ts) < 100; ) {
+ if (readl(®s->sr) & SR_BUSY)
+ continue;
+ break;
+ }
+ if (readl(®s->sr) & SR_BUSY)
+ printf("ftspi010: busy timeout at cs deactivate\n");
+
+ /* cs pull high */
+ if (chip->rev >= 0x00011900) {
+ writel((slave->cs << 10) | CR2_FS, ®s->cr[2]);
+ } else {
+#ifdef CONFIG_FTSSP010_GPIO_BASE
+ struct ftgpio010_regs *gpio = chip->gpio.regs;
+ uint32_t mask = BIT_MASK(chip->gpio.pin);
+
+ setbits_le32(&gpio->set, mask);
+#endif
+ }
+ udelay_masked(1);
+}
+
+void spi_init(void)
+{
+}
+
+struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
+{
+ uint32_t clk, div;
+ struct spi_slave *ss;
+ struct ftssp010_chip *chip;
+
+ if (!spi_cs_is_valid(bus, cs))
+ return NULL;
+
+ if (mode != SPI_MODE_0) {
+ printf("ftssp010: MODE%d is not supported\n", mode);
+ return NULL;
+ }
+
+ ss = malloc(sizeof(struct spi_slave));
+ if (!ss)
+ return NULL;
+
+ ss->bus = bus;
+ ss->cs = cs;
+
+#ifdef CONFIG_FTSSP010_SCLK
+ clk = CONFIG_FTSSP010_SCLK;
+#else
+ clk = clk_get_rate("SSP");
+#endif
+ if (max_hz > 0) {
+ for (div = 0; div < 0xFFFF; ++div) {
+ if ((clk / (2 * (div + 1))) <= max_hz)
+ break;
+ }
+ } else {
+ div = 7;
+ }
+
+ chip = chip_list + bus;
+ chip->div = div;
+ chip->mode = mode;
+
+ debug("ftssp010: bus=%d, hz=%u\n", bus, (clk / (2 * (div + 1))));
+
+ return ss;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ free(slave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct ftssp010_chip *chip = chip_list + slave->bus;
+ struct ftssp010_regs __iomem *regs = chip->regs;
+
+ writel(CR1_SDL(8) | CR1_CLKDIV(chip->div), ®s->cr[1]);
+
+ if (chip->rev >= 0x00011900) {
+ writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH,
+ ®s->cr[0]);
+ writel(CR2_TXFCLR | CR2_RXFCLR,
+ ®s->cr[2]);
+ } else {
+ writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO,
+ ®s->cr[0]);
+ writel(CR2_TXFCLR | CR2_RXFCLR | CR2_SSPEN | CR2_TXDOE,
+ ®s->cr[2]);
+ }
+
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ struct ftssp010_chip *chip = chip_list + slave->bus;
+ struct ftssp010_regs __iomem *regs = chip->regs;
+
+ writel(0, ®s->cr[2]);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct ftssp010_chip *chip = chip_list + slave->bus;
+ uint32_t len = bitlen >> 3;
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ if (chip->rev >= 0x00011900)
+ ftssp010_spi_work_transfer_v1_19(chip, dout, din, len, flags);
+ else
+ ftssp010_spi_work_transfer(chip, dout, din, len, flags);
+
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
new file mode 100644
@@ -0,0 +1,86 @@
+/*
+ * Faraday Multi-function Controller - SPI Mode
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#ifndef _FTSSP010_H
+#define _FTSSP010_H
+
+/* FTSSP010 HW Registers */
+struct ftssp010_regs {
+ uint32_t cr[3];/* control register */
+ uint32_t sr; /* status register */
+ uint32_t icr; /* interrupt control register */
+ uint32_t isr; /* interrupt status register */
+ uint32_t dr; /* data register */
+ uint32_t rsvd[17];
+ uint32_t revr; /* revision register */
+ uint32_t fear; /* feature register */
+};
+
+/* Control register 0 */
+#define CR0_FFMT_MASK (7 << 12)
+#define CR0_FFMT_SSP (0 << 12)
+#define CR0_FFMT_SPI (1 << 12)
+#define CR0_FFMT_MICROWIRE (2 << 12)
+#define CR0_FFMT_I2S (3 << 12)
+#define CR0_FFMT_AC97 (4 << 12)
+#define CR0_FLASH (1 << 11)
+#define CR0_FSDIST(x) (((x) & 0x03) << 8)
+#define CR0_LBM (1 << 7) /* Loopback mode */
+#define CR0_LSB (1 << 6) /* LSB first */
+#define CR0_FSPO (1 << 5) /* Frame sync atcive low */
+#define CR0_FSJUSTIFY (1 << 4)
+#define CR0_OPM_SLAVE (0 << 2)
+#define CR0_OPM_MASTER (3 << 2)
+#define CR0_OPM_I2S_MSST (3 << 2) /* Master stereo mode */
+#define CR0_OPM_I2S_MSMO (2 << 2) /* Master mono mode */
+#define CR0_OPM_I2S_SLST (1 << 2) /* Slave stereo mode */
+#define CR0_OPM_I2S_SLMO (0 << 2) /* Slave mono mode */
+#define CR0_SCLKPO (1 << 1) /* SCLK Remain HIGH */
+#define CR0_SCLKPH (1 << 0) /* Half CLK cycle */
+
+/* Control Register 1 */
+
+#define CR1_PDL(x) (((x) & 0xff) << 24) /* padding length */
+#define CR1_SDL(x) ((((x) - 1) & 0x1f) << 16) /* data length */
+#define CR1_CLKDIV(x) ((x) & 0xffff) /* clk divider */
+
+/* Control Register 2 */
+#define CR2_FSOS(x) (((x) & 0x03) << 10) /* FS/CS Select */
+#define CR2_FS (1 << 9) /* FS/CS Signal Level */
+#define CR2_TXEN (1 << 8) /* Tx Enable */
+#define CR2_RXEN (1 << 7) /* Rx Enable */
+#define CR2_SSPRST (1 << 6) /* SSP reset */
+#define CR2_TXFCLR (1 << 3) /* TX FIFO Clear */
+#define CR2_RXFCLR (1 << 2) /* RX FIFO Clear */
+#define CR2_TXDOE (1 << 1) /* TX Data Output Enable */
+#define CR2_SSPEN (1 << 0) /* SSP Enable */
+
+/*
+ * Status Register
+ */
+#define SR_RFF (1 << 0) /* receive FIFO full */
+#define SR_TFNF (1 << 1) /* transmit FIFO not full */
+#define SR_BUSY (1 << 2) /* bus is busy */
+#define SR_RFVE(reg) (((reg) >> 4) & 0x1f) /* receive FIFO valid entries */
+#define SR_TFVE(reg) (((reg) >> 12) & 0x1f) /* transmit FIFO valid entries */
+
+/*
+ * Feature Register
+ */
+#define FEAR_WIDTH(reg) ((((reg) >> 0) & 0xff) + 1)
+#define FEAR_RXFIFO(reg) ((((reg) >> 8) & 0xff) + 1)
+#define FEAR_TXFIFO(reg) ((((reg) >> 16) & 0xff) + 1)
+#define FEAR_AC97 (1 << 24)
+#define FEAR_I2S (1 << 25)
+#define FEAR_SPI_MWR (1 << 26)
+#define FEAR_SSP (1 << 27)
+#define FEAR_SPDIF (1 << 28)
+
+#endif
new file mode 100644
@@ -0,0 +1,25 @@
+/*
+ * Faraday GPIO Controller
+ *
+ * (C) Copyright 2010 Faraday Technology
+ * Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is released under the terms of GPL v2 and any later version.
+ * See the file COPYING in the root directory of the source tree for details.
+ */
+
+#ifndef __FTGPIO010_H
+#define __FTGPIO010_H
+
+struct ftgpio010_regs {
+ uint32_t out; /* 0x00: Data Output */
+ uint32_t in; /* 0x04: Data Input */
+ uint32_t dir; /* 0x08: Pin Direction */
+ uint32_t bypass; /* 0x0c: Bypass */
+ uint32_t set; /* 0x10: Data Set */
+ uint32_t clr; /* 0x14: Data Clear */
+ uint32_t pull_up; /* 0x18: Pull-Up Enabled */
+ uint32_t pull_st; /* 0x1c: Pull State (0=pull-down, 1=pull-up) */
+};
+
+#endif /* __FTGPIO010_H */