U-Boot
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2002 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2001 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2000 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
December 2013
- 157 participants
- 569 discussions

[U-Boot] [PATCH v7] spi: Add Faraday SPI controller support
by Jagannadha Sutradharudu Teki 20 Dec '13
by Jagannadha Sutradharudu Teki 20 Dec '13
20 Dec '13
From: Kuo-Jung Su <dantesu(a)faraday-tech.com>
The Faraday FTSSP010 is a multi-function controller
which supports I2S/SPI/SSP/AC97/SPDIF. However This
patch implements only the SPI mode.
NOTE:
The DMA and CS/Clock control logic has been altered
since hardware revision 1.19.0. So this patch
would first detects the revision id of the underlying
chip, and then switch to the corresponding software
control routines.
Signed-off-by: Kuo-Jung Su <dantesu(a)faraday-tech.com>
Signed-off-by: Jagannadha Sutradharudu Teki <jaganna(a)xilinx.com>
CC: Tom Rini <trini(a)ti.com>
---
Changes for v7:
- Fixed some checkpatch warnings
- Removed unneeded "inline" in few lenghty defination funcs.
Changes for v6:
- Drop SPI_LSB_FIRST support, because this driver
supports only 8-bit mode access.
- Bug fix: Move chip enable from ftssp010_cs_set()
to spi_cs_activate(), because ftssp010_cs_set()
is supposed to update only the cs signal.
Changes for v5:
- Use SPDX-License-Identifier
- Add SPI mode checking & setup
- Coding Style cleanup
Changes for v4:
- Coding Style cleanup.
- Make it a separate patch, rather then a part of
Faraday A36x patch series
- Use macro constants for timeout control
Changes for v3:
- Coding Style cleanup.
- Drop macros for wirtel()/readl(), call them directly.
- Always insert a blank line between declarations and code.
- Replace all the infinite wait loop with a timeout.
- Add '__iomem' to all the declaration of HW register pointers.
Changes for v2:
- Coding Style cleanup.
- Use readl(), writel(), clrsetbits_le32() to replace REG() macros.
- Use structure based hardware registers to replace the macro constants.
- Replace BIT() with BIT_MASK().
drivers/spi/Makefile | 1 +
drivers/spi/ftssp010_spi.c | 508 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 509 insertions(+)
create mode 100644 drivers/spi/ftssp010_spi.c
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index d5a7143..81b6af6 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_CF_SPI) += cf_spi.o
obj-$(CONFIG_CF_QSPI) += cf_qspi.o
obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
+obj-$(CONFIG_FTSSP010_SPI) += ftssp010_spi.o
obj-$(CONFIG_ICH_SPI) += ich.o
obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
diff --git a/drivers/spi/ftssp010_spi.c b/drivers/spi/ftssp010_spi.c
new file mode 100644
index 0000000..aa3b5a0
--- /dev/null
+++ b/drivers/spi/ftssp010_spi.c
@@ -0,0 +1,508 @@
+/*
+ * (C) Copyright 2013
+ * Faraday Technology Corporation. <http://www.faraday-tech.com/tw/>
+ * Kuo-Jung Su <dantesu(a)gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/compat.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <spi.h>
+
+#ifndef CONFIG_FTSSP010_BASE_LIST
+#define CONFIG_FTSSP010_BASE_LIST { CONFIG_FTSSP010_BASE }
+#endif
+
+#ifndef CONFIG_FTSSP010_GPIO_BASE
+#define CONFIG_FTSSP010_GPIO_BASE 0
+#endif
+
+#ifndef CONFIG_FTSSP010_GPIO_LIST
+#define CONFIG_FTSSP010_GPIO_LIST { CONFIG_FTSSP010_GPIO_BASE }
+#endif
+
+#ifndef CONFIG_FTSSP010_CLOCK
+#define CONFIG_FTSSP010_CLOCK clk_get_rate("SSP");
+#endif
+
+#ifndef CONFIG_FTSSP010_TIMEOUT
+#define CONFIG_FTSSP010_TIMEOUT 100
+#endif
+
+/* FTSSP010 chip 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_LOOP (1 << 7) /* loopback mode */
+#define CR0_LSB (1 << 6) /* LSB */
+#define CR0_FSPO (1 << 5) /* fs atcive low (I2S only) */
+#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) /* clock polarity */
+#define CR0_SCLKPH (1 << 0) /* clock phase */
+
+/* Control Register 1 */
+#define CR1_PDL(x) (((x) & 0xff) << 24) /* padding length */
+#define CR1_SDL(x) ((((x) - 1) & 0x1f) << 16) /* data length */
+#define CR1_DIV(x) (((x) - 1) & 0xffff) /* clock divider */
+
+/* Control Register 2 */
+#define CR2_CS(x) (((x) & 3) << 10) /* CS/FS select */
+#define CR2_FS (1 << 9) /* CS/FS signal level */
+#define CR2_TXEN (1 << 8) /* tx enable */
+#define CR2_RXEN (1 << 7) /* rx enable */
+#define CR2_RESET (1 << 6) /* chip reset */
+#define CR2_TXFC (1 << 3) /* tx fifo Clear */
+#define CR2_RXFC (1 << 2) /* rx fifo Clear */
+#define CR2_TXDOE (1 << 1) /* tx data output enable */
+#define CR2_EN (1 << 0) /* chip enable */
+
+/* Status Register */
+#define SR_RFF (1 << 0) /* rx fifo full */
+#define SR_TFNF (1 << 1) /* tx fifo not full */
+#define SR_BUSY (1 << 2) /* chip busy */
+#define SR_RFVE(reg) (((reg) >> 4) & 0x1f) /* rx fifo valid entries */
+#define SR_TFVE(reg) (((reg) >> 12) & 0x1f) /* tx fifo valid entries */
+
+/* Feature Register */
+#define FEAR_BITS(reg) ((((reg) >> 0) & 0xff) + 1) /* data width */
+#define FEAR_RFSZ(reg) ((((reg) >> 8) & 0xff) + 1) /* rx fifo size */
+#define FEAR_TFSZ(reg) ((((reg) >> 16) & 0xff) + 1) /* tx fifo size */
+#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)
+
+/* FTGPIO010 chip registers */
+struct ftgpio010_regs {
+ uint32_t out; /* 0x00: Data Output */
+ uint32_t in; /* 0x04: Data Input */
+ uint32_t dir; /* 0x08: 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) */
+};
+
+struct ftssp010_gpio {
+ struct ftgpio010_regs *regs;
+ uint32_t pin;
+};
+
+struct ftssp010_spi {
+ struct spi_slave slave;
+ struct ftssp010_gpio gpio;
+ struct ftssp010_regs *regs;
+ uint32_t fifo;
+ uint32_t mode;
+ uint32_t div;
+ uint32_t clk;
+ uint32_t speed;
+ uint32_t revision;
+};
+
+static inline struct ftssp010_spi *to_ftssp010_spi(struct spi_slave *slave)
+{
+ return container_of(slave, struct ftssp010_spi, slave);
+}
+
+static int get_spi_chip(int bus, struct ftssp010_spi *chip)
+{
+ uint32_t fear, base[] = CONFIG_FTSSP010_BASE_LIST;
+
+ if (bus >= ARRAY_SIZE(base) || !base[bus])
+ return -1;
+
+ chip->regs = (struct ftssp010_regs *)base[bus];
+
+ chip->revision = readl(&chip->regs->revr);
+
+ fear = readl(&chip->regs->fear);
+ chip->fifo = min_t(uint32_t, FEAR_TFSZ(fear), FEAR_RFSZ(fear));
+
+ return 0;
+}
+
+static int get_spi_gpio(int bus, struct ftssp010_gpio *chip)
+{
+ uint32_t base[] = CONFIG_FTSSP010_GPIO_LIST;
+
+ if (bus >= ARRAY_SIZE(base) || !base[bus])
+ return -1;
+
+ chip->regs = (struct ftgpio010_regs *)(base[bus] & 0xfff00000);
+ chip->pin = base[bus] & 0x1f;
+
+ /* make it an output pin */
+ setbits_le32(&chip->regs->dir, 1 << chip->pin);
+
+ return 0;
+}
+
+static int ftssp010_wait(struct ftssp010_spi *chip)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ int ret = -1;
+ ulong t;
+
+ /* wait until device idle */
+ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) {
+ if (readl(®s->sr) & SR_BUSY)
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ puts("ftspi010: busy timeout\n");
+
+ return ret;
+}
+
+static int ftssp010_wait_tx(struct ftssp010_spi *chip)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ int ret = -1;
+ ulong t;
+
+ /* wait until tx fifo not full */
+ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) {
+ if (!(readl(®s->sr) & SR_TFNF))
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ puts("ftssp010: tx timeout\n");
+
+ return ret;
+}
+
+static int ftssp010_wait_rx(struct ftssp010_spi *chip)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ int ret = -1;
+ ulong t;
+
+ /* wait until rx fifo not empty */
+ for (t = get_timer(0); get_timer(t) < CONFIG_FTSSP010_TIMEOUT; ) {
+ if (!SR_RFVE(readl(®s->sr)))
+ continue;
+ ret = 0;
+ break;
+ }
+
+ if (ret)
+ puts("ftssp010: rx timeout\n");
+
+ return ret;
+}
+
+static int ftssp010_spi_work_transfer_v2(struct ftssp010_spi *chip,
+ const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+ struct ftssp010_regs *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_v1(struct ftssp010_spi *chip,
+ const void *tx_buf, void *rx_buf, int len, uint flags)
+{
+ struct ftssp010_regs *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;
+}
+
+static void ftssp010_cs_set(struct ftssp010_spi *chip, int high)
+{
+ struct ftssp010_regs *regs = chip->regs;
+ struct ftssp010_gpio *gpio = &chip->gpio;
+ uint32_t mask;
+
+ /* cs pull high/low */
+ if (chip->revision >= 0x11900) {
+ mask = CR2_CS(chip->slave.cs) | (high ? CR2_FS : 0);
+ writel(mask, ®s->cr[2]);
+ } else if (gpio->regs) {
+ mask = 1 << gpio->pin;
+ if (high)
+ writel(mask, &gpio->regs->set);
+ else
+ writel(mask, &gpio->regs->clr);
+ }
+
+ /* extra delay for signal propagation */
+ udelay_masked(1);
+}
+
+/*
+ * 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)
+{
+ struct ftssp010_spi chip;
+
+ if (get_spi_chip(bus, &chip))
+ return 0;
+
+ if (!cs)
+ return 1;
+ else if ((cs < 4) && (chip.revision >= 0x11900))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * 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_spi *chip = to_ftssp010_spi(slave);
+ struct ftssp010_regs *regs = chip->regs;
+
+ /* cs pull */
+ if (chip->mode & SPI_CS_HIGH)
+ ftssp010_cs_set(chip, 1);
+ else
+ ftssp010_cs_set(chip, 0);
+
+ /* chip enable + fifo clear */
+ setbits_le32(®s->cr[2], CR2_EN | CR2_TXFC | CR2_RXFC);
+}
+
+/*
+ * 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_spi *chip = to_ftssp010_spi(slave);
+
+ /* wait until chip idle */
+ ftssp010_wait(chip);
+
+ /* cs pull */
+ if (chip->mode & SPI_CS_HIGH)
+ ftssp010_cs_set(chip, 0);
+ else
+ ftssp010_cs_set(chip, 1);
+}
+
+void spi_init(void)
+{
+ /* nothing to do */
+}
+
+struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
+{
+ struct ftssp010_spi *chip;
+
+ if (mode & SPI_3WIRE) {
+ puts("ftssp010: can't do 3-wire\n");
+ return NULL;
+ }
+
+ if (mode & SPI_SLAVE) {
+ puts("ftssp010: can't do slave mode\n");
+ return NULL;
+ }
+
+ if (mode & SPI_PREAMBLE) {
+ puts("ftssp010: can't skip preamble bytes\n");
+ return NULL;
+ }
+
+ if (!spi_cs_is_valid(bus, cs)) {
+ puts("ftssp010: invalid (bus, cs)\n");
+ return NULL;
+ }
+
+ chip = spi_alloc_slave(struct ftssp010_spi, bus, cs);
+ if (!chip)
+ return NULL;
+
+ if (get_spi_chip(bus, chip))
+ goto free_out;
+
+ if (chip->revision < 0x11900 && get_spi_gpio(bus, &chip->gpio)) {
+ puts("ftssp010: Before revision 1.19.0, its clock & cs are\n"
+ "controlled by tx engine which is not synced with rx engine,\n"
+ "so the clock & cs might be shutdown before rx engine\n"
+ "finishs its jobs.\n"
+ "If possible, please add a dedicated gpio for it.\n");
+ }
+
+ chip->mode = mode;
+ chip->clk = CONFIG_FTSSP010_CLOCK;
+ chip->div = 2;
+ if (max_hz) {
+ while (chip->div < 0xffff) {
+ if ((chip->clk / (2 * chip->div)) <= max_hz)
+ break;
+ chip->div += 1;
+ }
+ }
+ chip->speed = chip->clk / (2 * chip->div);
+
+ return &chip->slave;
+
+free_out:
+ free(chip);
+ return NULL;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ free(slave);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct ftssp010_spi *chip = to_ftssp010_spi(slave);
+ struct ftssp010_regs *regs = chip->regs;
+
+ writel(CR1_SDL(8) | CR1_DIV(chip->div), ®s->cr[1]);
+
+ if (chip->revision >= 0x11900) {
+ writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO | CR0_FLASH,
+ ®s->cr[0]);
+ writel(CR2_TXFC | CR2_RXFC,
+ ®s->cr[2]);
+ } else {
+ writel(CR0_OPM_MASTER | CR0_FFMT_SPI | CR0_FSPO,
+ ®s->cr[0]);
+ writel(CR2_TXFC | CR2_RXFC | CR2_EN | CR2_TXDOE,
+ ®s->cr[2]);
+ }
+
+ if (chip->mode & SPI_LOOP)
+ setbits_le32(®s->cr[0], CR0_LOOP);
+
+ if (chip->mode & SPI_CPOL)
+ setbits_le32(®s->cr[0], CR0_SCLKPO);
+
+ if (chip->mode & SPI_CPHA)
+ setbits_le32(®s->cr[0], CR0_SCLKPH);
+
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ struct ftssp010_spi *chip = to_ftssp010_spi(slave);
+ struct ftssp010_regs *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_spi *chip = to_ftssp010_spi(slave);
+ uint32_t len = bitlen >> 3;
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(slave);
+
+ if (chip->revision >= 0x11900)
+ ftssp010_spi_work_transfer_v2(chip, dout, din, len, flags);
+ else
+ ftssp010_spi_work_transfer_v1(chip, dout, din, len, flags);
+
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(slave);
+
+ return 0;
+}
--
1.8.3
3
2
- add more serdes protocols support.
- fix some serdes lanes route.
- correct boot location info for SD/SPI boot.
Signed-off-by: Shengzhou Liu <Shengzhou.Liu(a)freescale.com>
---
board/freescale/t2080qds/t2080qds.c | 66 +++++++++++++++++++++++++++++++++----
1 file changed, 60 insertions(+), 6 deletions(-)
diff --git a/board/freescale/t2080qds/t2080qds.c b/board/freescale/t2080qds/t2080qds.c
index cac32fe..4fe8ccb 100644
--- a/board/freescale/t2080qds/t2080qds.c
+++ b/board/freescale/t2080qds/t2080qds.c
@@ -40,6 +40,11 @@ int checkboard(void)
printf("Sys ID: 0x%02x, Board Arch: V%d, ", QIXIS_READ(id), sw >> 4);
printf("Board Version: %c, boot from ", (sw & 0xf) + 'A' - 1);
+#ifdef CONFIG_SDCARD
+ puts("SD/MMC\n");
+#elif CONFIG_SPIFLASH
+ puts("SPI\n");
+#else
sw = QIXIS_READ(brdcfg[0]);
sw = (sw & QIXIS_LBMAP_MASK) >> QIXIS_LBMAP_SHIFT;
@@ -51,6 +56,7 @@ int checkboard(void)
puts("NAND\n");
else
printf("invalid setting of SW%u\n", QIXIS_LBMAP_SWITCH);
+#endif
printf("FPGA: v%d (%s), build %d", (int)QIXIS_READ(scver),
qixis_read_tag(buf), (int)qixis_read_minor());
@@ -97,13 +103,25 @@ int brd_mux_lane_to_slot(void)
/* SerDes1 is not enabled */
break;
case 0x1c:
- case 0x95:
case 0xa2:
- case 0x94:
/* SD1(A:D) => SLOT3 SGMII
* SD1(G:H) => SLOT1 SGMII
*/
- QIXIS_WRITE(brdcfg[12], 0x58);
+ QIXIS_WRITE(brdcfg[12], 0x1a);
+ break;
+ case 0x94:
+ case 0x95:
+ /* SD1(A:B) => SLOT3 SGMII(a)1.25bps
+ * SD1(C:D) => SFP Module, SGMII(a)3.125bps
+ * SD1(E:H) => SLOT1 SGMII(a)1.25bps
+ */
+ case 0x96:
+ /* SD1(A:B) => SLOT3 SGMII(a)1.25bps
+ * SD1(C) => SFP Module, SGMII(a)3.125bps
+ * SD1(D) => SFP Module, SGMII(a)1.25bps
+ * SD1(E:H) => SLOT1 PCIe4 x4
+ */
+ QIXIS_WRITE(brdcfg[12], 0x3a);
break;
case 0x51:
/* SD1(A:D) => SLOT3 XAUI
@@ -134,6 +152,34 @@ int brd_mux_lane_to_slot(void)
*/
QIXIS_WRITE(brdcfg[12], 0xda);
break;
+ case 0x6e:
+ /* SD1(A:B) => SFP Module, XFI
+ * SD1(C:D) => SLOT3 SGMII
+ * SD1(E:F) => SLOT1 PCIe4 x2
+ * SD1(G:H) => SLOT2 SGMII
+ */
+ QIXIS_WRITE(brdcfg[12], 0xd9);
+ break;
+ case 0xda:
+ /* SD1(A:H) => SLOT3 PCIe3 x8
+ */
+ QIXIS_WRITE(brdcfg[12], 0x0);
+ break;
+ case 0xc8:
+ /* SD1(A) => SLOT3 PCIe3 x1
+ * SD1(B) => SFP Module, SGMII(a)1.25bps
+ * SD1(C:D) => SFP Module, SGMII(a)3.125bps
+ * SD1(E:F) => SLOT1 PCIe4 x2
+ * SD1(G:H) => SLOT2 SGMII
+ */
+ QIXIS_WRITE(brdcfg[12], 0x79);
+ break;
+ case 0xab:
+ /* SD1(A:D) => SLOT3 PCIe3 x4
+ * SD1(E:H) => SLOT1 PCIe4 x4
+ */
+ QIXIS_WRITE(brdcfg[12], 0x1a);
+ break;
default:
printf("WARNING: unsupported for SerDes1 Protocol %d\n",
srds_prtcl_s1);
@@ -147,7 +193,7 @@ int brd_mux_lane_to_slot(void)
case 0x01:
case 0x02:
/* SD2(A:H) => SLOT4 PCIe1 */
- QIXIS_WRITE(brdcfg[13], 0x20);
+ QIXIS_WRITE(brdcfg[13], 0x10);
break;
case 0x15:
case 0x16:
@@ -164,7 +210,7 @@ int brd_mux_lane_to_slot(void)
* SD2(E:F) => SLOT5 Aurora
* SD2(G:H) => SATA1,SATA2
*/
- QIXIS_WRITE(brdcfg[13], 0x70);
+ QIXIS_WRITE(brdcfg[13], 0x78);
break;
case 0x1f:
/*
@@ -180,7 +226,15 @@ int brd_mux_lane_to_slot(void)
* SD2(A:D) => SLOT4 SRIO2
* SD2(E:H) => SLOT5 SRIO1
*/
- QIXIS_WRITE(brdcfg[13], 0x50);
+ QIXIS_WRITE(brdcfg[13], 0xa0);
+ break;
+ case 0x36:
+ /*
+ * SD2(A:D) => SLOT4 SRIO2
+ * SD2(E:F) => Aurora
+ * SD2(G:H) => SATA1,SATA2
+ */
+ QIXIS_WRITE(brdcfg[13], 0x78);
break;
default:
printf("WARNING: unsupported for SerDes2 Protocol %d\n",
--
1.8.0
1
0
We switched to Kbuild style makefiles at v2014.01-rc1 release.
With that modification, we can write makefiles simpler.
But it is NOT real Kbuild.
As the next step, this series imports (+ adjusts) build scripts
from Linux Kernel under scripts/ directory.
By applying this series, we can get more advantages:
- short log
- perfect dependency tracking
- preparation to the next step: Kconfig
- other things...
Kbuild without Kconfig
----------------------
First of all, to make things clearer, let me explain
the difference between "Kbuild" and "Kconfig".
They are, I think, sometimes confusing.
Kbuild - build system used for Linux Kernel.
Some features of Kbuild are:
(a) We can describe makefiles simple.
Just addi objects to "obj-y" like this:
obj-$(CONFIG_FOO) += foo.o
(b) We can describe directory descending nicely
Add a directory name to "obj-y" like this:
obj-$(CONFIG_BAR) += bar/
(c) Short log like follows:
CC common/foo.o
CC common/bar.o
LD common/built-in.o
(d) Perfect dependency tracking
I think this is the biggest advantage.
To be honest, the dependency tracing of U-Boot build system
was not reliable.
Kconfig - A tool to manage CONFIG macros.
We can handle the dependency among CONFIG macros.
Kconfig allows us to modify CONFIG settings easily
by "make config".
GUI interface are also available by "make menuconfig"
All defined CONFIG macros are stored into ".config" file
I think most of U-boot developers are already familiar with above.
(In most cases, they are Linux Kernel developers too.)
I definitely want to port both of these, but I want to do them separately: Kbuild first.
(If we do Kbuild and Kconfig at the same time, it might be messed up.)
So, I want to do "Kbuild without Kconfig" in this series.
The conventional tool (mkconfig + boards.cfg file)
is used for board configuration.
Prerequisite
------------
You need to apply some patches beforehand to use this series.
This series uses the followings as prerequisites:
[1] sandbox: Use system headers first for sandbox's os.c in a different way
http://patchwork.ozlabs.org/patch/294233/
[2] Makefile: fix the typo error for mrproper (posted by Bo Shen)
http://patchwork.ozlabs.org/patch/301493/
[3] nand_util.c: Use '%zd' for length in nand_unlock debug print (posted by Tom Rini)
http://patchwork.ozlabs.org/patch/301740/
This is not mandatory, but recommended.
It fixes some warnings, so you can get cleaner buildman log.
How to Build ?
--------------
We can build the same as before.
Do board configuraton first and then run "make".
$ make omap4_panda_config
Configuring for omap4_panda board...
$ make CROSS_COMPILE=arm-linux-gnueabi-
GEN include/autoconf.mk.dep
GEN include/autoconf.mk
CC lib/asm-offsets.s
GEN include/generated/generic-asm-offsets.h
CC arch/arm/cpu/armv7/omap4/asm-offsets.s
GEN include/generated/asm-offsets.h
HOSTCC scripts/basic/fixdep
...
You will find a difference at a glance, short log
If you need detail log message, please add "V=1".
(You can also use "V=2")
Please note we can no longer use
$ make omap4_panda CROSS_COMPILE=arm-linux-gnueabi-
to do board configuration and "make" at the same time.
Instead, we can use Kbuild-ish way for that purpose:
$ make omap4_panda_config all CROSS_COMPILE=arm-linux-gnuabi-
This series keeps the other features:
- Support out-of-tree build
You can use "O=<dir_name>" like this
$ mkdir build_dir
$ make omap4_panda_config all O=build_dir CROSS_COMPILE=arm-linux-gnueabi-
- Works with parallel make option
Add "-j" option for this. Compiling will get faster.
- Of cource, SPL, TPL build are supported
(nand_spl also works. But "nand_spl" is obsolete and we should switch to "spl".
Until when should we continue to maintain nand_spl?)
- Breaks no boards (except some boards which are already broken)
I built all target boards to prove correctness of this series
at least for compile test.
My Next Plan
------------
- Import Kconfig
Use "make config", "make menuconfig", "make defconfig", etc. in U-Boot.
- More refactoring
Some parts of makefiles are still dirty.
I want to refactor more makefiles in follow-up patches.
- Use "obj-m" for standalone program?? Loadable module??
I have not deceided about this yet.
Known Problems
--------------
- ".*.su" files at the top directory
After build, you will notice ".*.su" files at the top directory.
$ ls -a
. .20193.su CREDITS api disk include scripts u-boot-nand.bin u-boot.srec
.. .20198.su Licenses arch doc lib snapshot.commit u-boot-pad.img
.18993.su .20203.su MAKEALL board drivers mkconfig spl u-boot.bin
.18998.su .checkpatch.conf Makefile boards.cfg dts nand_spl test u-boot.ldr
.19037.su .git README common examples net tools u-boot.lds
.19042.su .gitignore System.map config.mk fs post u-boot u-boot.map
These files ".<process_number>.su" are created when -fstack-usage is given to $(call cc-option, ...)
I will fix this problem correctly lator.
- I marked dirty parts with "FIX ME".
In some board-specific config.mk files.
# FIX ME
ifneq ($(filter lib lib/lzma lib/zlib, $(obj)),)
ccflags-y := -O2
endif
In the top Makefile
# FIX ME
cpp_flags := $(KBUILD_CPPFLAGS) $(CPPFLAGS) $(UBOOTINCLUDE) $(NOSTDINC_FLAGS)
c_flags := $(KBUILD_CFLAGS) $(cpp_flags)
I will re-write them more nicely after other parts are prepared.
Changes for v2:
- At version 1, nand_spl boards got broken at 12 and fixed at 14.
Fix this problem
- At version 1, sandbox got broken at 17 and fixed at 21.
Fix this problem
- Add a new patch at the tail:
35/35 "Kbuild: chech clean source and generate Makefile for out-of-tree build"
- Rebase on v2014.01-rc2 tag
Masahiro Yamada (35):
.gitignore: ingore files generated by Kbuild
Makefile.host.tmp: add a new script to refactor tools
tools: convert makefiles to kbuild style
board: samsung: refactor host programs
examples: Use scripts/Makefile.build
nand-spl: Use scripts/Makefile.build
Makfile: move suffix rules to Makefile.build
Makefile: move some variable definitions to the top Makefile
Makefile: move BFD_ROOT_DIR to tools/gdb/Makefile
Kbuild: import Kbuild.include from linux v3.12 tag
Kbuild: Use Kbuild.include
Makefile: move more flags to the top Makefile
Makefile: refactor include path settings
Makefile: move more stuff to top Makefile
Makefile: move some flags to spl/Makefile
Makefile: move some flags to examples makefiles
Kbuild: change out-of-tree building
Kbuild: add dummy obj-y to create built-in.o
Makefile: rename scripts/Makefile.build to scripts/Makefile.build.tmp
Kbuild: import more build scripts from Linux v3.12 tag
Kbuild: use Linux Kernel build scripts
Kbuild: delete temporary build scripts
Kbuild: move some lines to more suitable place
Kbuild: convert some make rules to Kbuild style
Kbuild: move include directives of board configuration files
Kbuild: generate {spl,tpl}-autoconf.mk only when it is necessary
Makefile: remove a cleaning target "tidy"
Kbuild: change the top Makefile to more Kbuild-ish structure
examples: move api/ and standalone/ to examples/Makefile
Kbuild: refactor Makefile and spl/Makefile more
Makefile: Do not pass MTD_VERSION from the top Makefile
Makefile: refactor tools-all targets
Kbuild: use scripts/Makefile.clean
Kbuild: support simultaneous board configuration and "make all"
Kbuild: check clean source and generate Makefile for out-of-tree build
.gitignore | 28 +-
MAKEALL | 8 +-
Makefile | 1300 +++++++++++++-------
arch/arm/cpu/arm1136/config.mk | 2 +-
arch/arm/cpu/arm926ejs/config.mk | 2 +-
arch/arm/cpu/arm926ejs/davinci/config.mk | 2 +-
arch/arm/cpu/armv7/am33xx/config.mk | 2 +-
arch/arm/cpu/armv7/config.mk | 2 +-
arch/arm/cpu/armv7/omap3/config.mk | 2 +-
arch/arm/cpu/armv7/omap4/config.mk | 2 +-
arch/arm/cpu/armv7/omap5/config.mk | 2 +-
arch/arm/cpu/armv7/socfpga/config.mk | 2 +-
arch/arm/cpu/armv7/tegra114/Makefile | 3 +-
arch/arm/cpu/armv7/tegra30/Makefile | 3 +-
arch/arm/imx-common/Makefile | 2 +-
arch/blackfin/config.mk | 10 +-
arch/blackfin/cpu/Makefile | 10 +-
arch/blackfin/lib/Makefile | 5 +-
arch/m68k/cpu/mcf5227x/Makefile | 2 +-
arch/m68k/cpu/mcf523x/Makefile | 2 +-
arch/m68k/cpu/mcf52x2/Makefile | 2 +-
arch/m68k/cpu/mcf532x/Makefile | 2 +-
arch/m68k/cpu/mcf5445x/Makefile | 2 +-
arch/m68k/cpu/mcf547x_8x/Makefile | 2 +-
arch/mips/cpu/mips32/config.mk | 2 +-
arch/mips/cpu/mips64/config.mk | 2 +-
arch/mips/cpu/xburst/config.mk | 2 +-
arch/nds32/config.mk | 2 +-
arch/nds32/cpu/n1213/Makefile | 3 +
arch/powerpc/cpu/mpc8xx/Makefile | 2 +-
arch/powerpc/lib/Makefile | 4 +-
arch/sandbox/cpu/Makefile | 11 +-
arch/sparc/config.mk | 3 +-
arch/x86/lib/Makefile | 2 +-
board/ait/cam_enc_4xx/config.mk | 2 +-
board/avionic-design/medcom-wide/Makefile | 2 +-
board/avionic-design/plutux/Makefile | 2 +-
board/avionic-design/tec/Makefile | 2 +-
board/bct-brettl2/config.mk | 7 +-
board/bf518f-ezbrd/config.mk | 7 +-
board/bf526-ezbrd/config.mk | 7 +-
board/bf527-ad7160-eval/config.mk | 7 +-
board/bf527-ezkit/config.mk | 7 +-
board/bf527-sdp/config.mk | 7 +-
board/bf533-ezkit/config.mk | 7 +-
board/bf533-stamp/config.mk | 7 +-
board/bf537-stamp/config.mk | 7 +-
board/bf538f-ezkit/config.mk | 7 +-
board/bf548-ezkit/config.mk | 7 +-
board/bf561-acvilon/config.mk | 7 +-
board/bf561-ezkit/config.mk | 7 +-
board/br4/config.mk | 7 +-
board/cm-bf527/config.mk | 7 +-
board/cm-bf533/config.mk | 7 +-
board/cm-bf537e/config.mk | 7 +-
board/cm-bf537u/config.mk | 7 +-
board/cm-bf548/config.mk | 7 +-
board/cm-bf561/config.mk | 7 +-
board/compal/paz00/Makefile | 2 +-
board/compulab/trimslice/Makefile | 2 +-
board/cray/L1/Makefile | 10 +-
board/freescale/common/Makefile | 5 +-
board/h2200/Makefile | 2 +-
board/ip04/config.mk | 7 +-
board/matrix_vision/mvblm7/Makefile | 4 +-
board/matrix_vision/mvblx/Makefile | 2 +-
board/matrix_vision/mvsmr/Makefile | 2 +-
board/nvidia/common/Makefile | 2 +-
board/pcs440ep/config.mk | 2 +-
board/pr1/config.mk | 7 +-
board/samsung/origen/Makefile | 23 +-
.../origen/tools/{mkv310_image.c => mkorigenspl.c} | 0
board/samsung/smdkv310/Makefile | 16 +-
.../tools/{mkv310_image.c => mksmdkv310spl.c} | 0
board/sandburst/karef/Makefile | 2 +-
board/sandburst/metrobox/Makefile | 2 +-
board/spear/common/Makefile | 5 +-
board/spear/x600/Makefile | 5 +-
board/st-ericsson/snowball/Makefile | 2 +-
board/st-ericsson/u8500/Makefile | 2 +-
board/tcm-bf518/config.mk | 7 +-
board/tcm-bf537/config.mk | 7 +-
common/Makefile | 11 +-
config.mk | 333 +----
disk/Makefile | 2 +-
doc/DocBook/Makefile | 17 -
drivers/bios_emulator/Makefile | 5 +-
drivers/hwmon/Makefile | 2 +-
drivers/net/npe/Makefile | 4 +-
drivers/rtc/Makefile | 2 +-
drivers/usb/musb-new/Makefile | 7 +-
dts/Makefile | 20 +-
examples/Makefile | 9 +
examples/api/Makefile | 44 +-
examples/standalone/Makefile | 71 +-
fs/ubifs/Makefile | 2 +-
fs/yaffs2/Makefile | 9 +-
lib/Makefile | 2 +-
lib/lzma/Makefile | 2 +-
mkconfig | 2 +-
nand_spl/board/amcc/acadia/Makefile | 45 +-
nand_spl/board/amcc/bamboo/Makefile | 45 +-
nand_spl/board/amcc/canyonlands/Makefile | 45 +-
nand_spl/board/amcc/kilauea/Makefile | 43 +-
nand_spl/board/amcc/sequoia/Makefile | 47 +-
nand_spl/board/freescale/mpc8315erdb/Makefile | 47 +-
nand_spl/board/freescale/mpc8536ds/Makefile | 59 +-
nand_spl/board/freescale/mpc8569mds/Makefile | 59 +-
nand_spl/board/freescale/mpc8572ds/Makefile | 59 +-
nand_spl/board/freescale/p1023rds/Makefile | 60 +-
nand_spl/board/freescale/p1_p2_rdb/Makefile | 59 +-
nand_spl/board/sheldon/simpc8313/Makefile | 48 +-
net/Makefile | 2 +-
post/lib_powerpc/fpu/Makefile | 30 +-
rules.mk | 51 -
scripts/Kbuild.include | 282 +++++
scripts/Makefile | 2 +
scripts/Makefile.build | 519 +++++++-
scripts/Makefile.clean | 108 ++
scripts/Makefile.host | 170 +++
scripts/Makefile.lib | 375 ++++++
scripts/basic/.gitignore | 1 +
scripts/basic/Makefile | 15 +
scripts/basic/fixdep.c | 462 +++++++
scripts/mkmakefile | 59 +
spl/Makefile | 185 +--
tools/.gitignore | 2 +-
tools/Makefile | 364 ++----
tools/crc32.c | 1 +
tools/easylogo/Makefile | 12 +-
tools/env/Makefile | 34 +-
tools/env/crc32.c | 1 +
tools/env/ctype.c | 1 +
tools/env/env_attr.c | 1 +
tools/env/env_flags.c | 1 +
tools/env/linux_string.c | 1 +
tools/env_embedded.c | 1 +
tools/fdt.c | 1 +
tools/fdt_ro.c | 1 +
tools/fdt_rw.c | 1 +
tools/fdt_strerror.c | 1 +
tools/fdt_wip.c | 1 +
tools/gdb/Makefile | 64 +-
tools/image-fit.c | 1 +
tools/image-sig.c | 1 +
tools/image.c | 1 +
tools/kernel-doc/Makefile | 21 +-
tools/md5.c | 1 +
tools/rsa-sign.c | 1 +
tools/sha1.c | 1 +
150 files changed, 3608 insertions(+), 2026 deletions(-)
rename board/samsung/origen/tools/{mkv310_image.c => mkorigenspl.c} (100%)
rename board/samsung/smdkv310/tools/{mkv310_image.c => mksmdkv310spl.c} (100%)
create mode 100644 examples/Makefile
delete mode 100644 rules.mk
create mode 100644 scripts/Kbuild.include
create mode 100644 scripts/Makefile
create mode 100644 scripts/Makefile.clean
create mode 100644 scripts/Makefile.host
create mode 100644 scripts/Makefile.lib
create mode 100644 scripts/basic/.gitignore
create mode 100644 scripts/basic/Makefile
create mode 100644 scripts/basic/fixdep.c
create mode 100644 scripts/mkmakefile
create mode 100644 tools/crc32.c
create mode 100644 tools/env/crc32.c
create mode 100644 tools/env/ctype.c
create mode 100644 tools/env/env_attr.c
create mode 100644 tools/env/env_flags.c
create mode 100644 tools/env/linux_string.c
create mode 100644 tools/env_embedded.c
create mode 100644 tools/fdt.c
create mode 100644 tools/fdt_ro.c
create mode 100644 tools/fdt_rw.c
create mode 100644 tools/fdt_strerror.c
create mode 100644 tools/fdt_wip.c
create mode 100644 tools/image-fit.c
create mode 100644 tools/image-sig.c
create mode 100644 tools/image.c
create mode 100644 tools/md5.c
create mode 100644 tools/rsa-sign.c
create mode 100644 tools/sha1.c
--
1.8.3.2
2
37
Hey,
The following changes since commit d627eefcd5e72db00889718ca9ee1dcb4d026fc9:
Merge remote-tracking branch 'u-boot-pxa/master' into 'u-boot-arm/master' (2013-12-18 22:19:02 +0100)
are available in the git repository at:
git://git.denx.de/u-boot-ti.git master
for you to fetch changes up to 2931fa4db349c97f882ffda42e901208654b5ca9:
ARM: AM43xx: Add Maintainer (2013-12-18 21:14:45 -0500)
Changes in v2:
- Dropped Dan's patch as he's right and it depends on another series we
aren't quite ready for.
- Reworked Lokesh's ioregs related patch to cover cm_t335 now as well.
----------------------------------------------------------------
Lokesh Vutla (12):
ARM: AM43xx: Update the base addresses of modules
ARM: AM43xx: Adapt to ti_armv7_common.h config file
ARM: AM43xx: Add L2 Support
ARM: AM43xx: Add extra ENV settings
ARM: AM43xx: Select clk source for Timer2
ARM: AM43xx: Update Current Booting devices list
ARM: AM43xx: mux: Update mux data
ARM: AM43xx: clocks: Update DPLL details
ARM: AM33xx+: Update ioregs to pass different values
ARM: AM43xx: EPOS_EVM: Add support for LPDDR2
ARM: AM43xx: GP_EVM: Add support for DDR3
ARM: AM43xx: Add Maintainer
Sekhar Nori (2):
ARM: AM43XX: board: add support for reading onboard EEPROM
ARM: AM43XX: Add CONFIG_ENV_VARS_UBOOT_RUNTIME_CONFIG support
Stefan Roese (1):
arm: omap3: Fix beagleboard SPL boot hangup (GPIO clocks not enabled)
arch/arm/cpu/armv7/am33xx/clock.c | 12 +-
arch/arm/cpu/armv7/am33xx/clock_am33xx.c | 15 +
arch/arm/cpu/armv7/am33xx/clock_am43xx.c | 12 +-
arch/arm/cpu/armv7/am33xx/ddr.c | 146 +++++++-
arch/arm/cpu/armv7/am33xx/emif4.c | 25 +-
arch/arm/cpu/armv7/omap-common/emif-common.c | 14 -
arch/arm/include/asm/arch-am33xx/clock.h | 7 +-
arch/arm/include/asm/arch-am33xx/clocks_am33xx.h | 3 +
arch/arm/include/asm/arch-am33xx/cpu.h | 24 +-
arch/arm/include/asm/arch-am33xx/ddr_defs.h | 41 ++-
arch/arm/include/asm/arch-am33xx/gpio.h | 12 +
arch/arm/include/asm/arch-am33xx/hardware.h | 7 -
arch/arm/include/asm/arch-am33xx/hardware_am33xx.h | 2 +
arch/arm/include/asm/arch-am33xx/hardware_am43xx.h | 16 +
arch/arm/include/asm/arch-am33xx/mux_am43xx.h | 45 +++
arch/arm/include/asm/arch-am33xx/omap.h | 4 +-
arch/arm/include/asm/arch-am33xx/spl.h | 13 +-
arch/arm/include/asm/emif.h | 26 ++
board/compulab/cm_t335/spl.c | 10 +-
board/isee/igep0033/board.c | 10 +-
board/phytec/pcm051/board.c | 20 +-
board/siemens/dxr2/board.c | 12 +-
board/siemens/pxm2/board.c | 10 +-
board/siemens/rut/board.c | 10 +-
board/ti/am335x/board.c | 40 ++-
board/ti/am43xx/board.c | 355 +++++++++++++++++++-
board/ti/am43xx/board.h | 36 ++
board/ti/am43xx/mux.c | 35 +-
board/ti/ti814x/evm.c | 4 +-
board/ti/ti816x/evm.c | 12 +-
boards.cfg | 2 +-
include/configs/am43xx_evm.h | 204 +++++------
include/configs/omap3_beagle.h | 3 +
33 files changed, 1005 insertions(+), 182 deletions(-)
Thanks!
--
Tom
2
1
Hi Tom,
Small PR - addons on one spi driver, others and few fixes.
Thanks,
Jagan.
The following changes since commit f3bf212abc4139f12b472e97c1992ab32671b609:
serial_sh: add support for SH7753 (2013-12-18 16:50:00 +0900)
are available in the git repository at:
git://git.denx.de/u-boot-spi.git master
for you to fetch changes up to ac5cce38de8f97a120b8c98f34be0d5eec50a6fb:
driver: mtd: sf_ops: claim bus while doing memcpy (2013-12-19 12:23:22 +0530)
----------------------------------------------------------------
Luka Perkov (2):
sf: probe: Hex values are in lower case
sf: probe: add support for MX25L2006E
Nobuhiro Iwamatsu (1):
spi: Add support SH Quad SPI driver
Poddar, Sourav (2):
config: dra7_evm: Add Bank Address Register(BAR) config
driver: mtd: sf_ops: claim bus while doing memcpy
Yen Lin (1):
spi: tegra: clear RDY bit prior to every transfer
doc/SPI/README.sh_qspi_test | 38 ++++++
drivers/mtd/spi/sf_ops.c | 6 +
drivers/mtd/spi/sf_probe.c | 3 +-
drivers/spi/Makefile | 1 +
drivers/spi/sh_qspi.c | 277 +++++++++++++++++++++++++++++++++++++++++++
drivers/spi/tegra114_spi.c | 21 ++--
include/configs/dra7xx_evm.h | 1 +
7 files changed, 333 insertions(+), 14 deletions(-)
create mode 100644 doc/SPI/README.sh_qspi_test
create mode 100644 drivers/spi/sh_qspi.c
2
1

[U-Boot] [RFC PATCH V4 1/2] ARM: OMAP4/5: Add alternative method for HSIC USB devices reset
by Lubomir Popov 19 Dec '13
by Lubomir Popov 19 Dec '13
19 Dec '13
Add option for individual reset of HSIC-connected USB devices by the
ehci-hcd.c driver upon applying port power, with per-device configurable
reset hold and delay times. This may replace the reset functionality via
usb_hub.c and board file (which does not work on some boards).
Make HSIC work on all OMAP543x-ES1.0 ports.
Signed-off-by: Lubomir Popov <lpopov(a)mm-sol.com>
---
V4 is just a resend from another machine. V1/2/3 were corrupted during
transmission.
drivers/usb/host/ehci-hcd.c | 15 ++++
drivers/usb/host/ehci-omap.c | 174 ++++++++++++++++++++++++++++++++++++++-----
2 files changed, 170 insertions(+), 19 deletions(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 8bd1eb8..17efb69 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -119,6 +119,12 @@ static struct descriptor {
#define ehci_is_TDI() (0)
#endif
+/* OMAP HSIC workaround option: */
+__weak void omap_ehci_hsic_reset_device(int port)
+{
+ return;
+}
+
int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
{
return PORTSC_PSPD(reg);
@@ -803,6 +809,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,
if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) {
reg |= EHCI_PS_PP;
ehci_writel(status_reg, reg);
+ /*
+ * OMAP4/5: Reset device for 'fail to connect'
+ * workaround. Weak function, actual reset
+ * should happen in ehci-omap.c and only if we
+ * have defined HSIC devices (in the board file)
+ * that we want to reset at this moment.
+ */
+ omap_ehci_hsic_reset_device(
+ le16_to_cpu(req->index));
}
break;
case USB_PORT_FEAT_RESET:
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 1b215c2..d3609a4 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -7,6 +7,13 @@
* Sunil Kumar <sunilsaini05(a)gmail.com>
* Shashi Ranjan <shashiranjanmca05(a)gmail.com>
*
+ * (C) Copyright 2013 Lubomir Popov, MM Solutions <lpopov(a)mm-sol.com>
+ * - Add option for individual reset of HSIC-connected USB devices by the
+ * ehci-hcd.c driver upon applying port power, with per-device configurable
+ * reset hold and delay times. This may replace the reset functionality via
+ * usb_hub.c and board file;
+ * - Make HSIC work on all OMAP5430-ES1.0 ports;
+ * - Add explanatory comments where appropriate.
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -26,6 +33,8 @@ static struct omap_uhh *const uhh = (struct omap_uhh *)OMAP_UHH_BASE;
static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE;
static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE;
+static struct omap_usbhs_board_data *usbhs_bdp;
+
static int omap_uhh_reset(void)
{
int timeout = 0;
@@ -106,7 +115,7 @@ static void omap_usbhs_hsic_init(int port)
writel(reg, &usbtll->channel_conf + port);
}
-#ifdef CONFIG_USB_ULPI
+#if defined(CONFIG_USB_ULPI) && defined(CONFIG_USB_ULPI_VIEWPORT_OMAP)
static void omap_ehci_soft_phy_reset(int port)
{
struct ulpi_viewport ulpi_vp;
@@ -158,10 +167,141 @@ static inline void omap_ehci_phy_reset(int on, int delay)
#define omap_ehci_phy_reset(on, delay) do {} while (0)
#endif
+/*
+ * Individual HSIC USB device reset to fix 'fail to connect' for some devices.
+ * Note that a HSIC-connected device is actually a permanently attached USB
+ * slave, while a PHY is just a hardware extension of the host port, and
+ * handling them in the same manner is not appropriate.
+ * In order to invoke this feature, define CONFIG_OMAP_HSIC_PORTx_RESET_GPIO
+ * in the board header (where x is the port number with HSIC-attached device
+ * that we want to reset via this method, and the value is the number of the
+ * particular GPIO) - the real functions shall then build and override the
+ * __weak dummy in ehci-hcd.c that is called upon applying port power. The
+ * active reset hold time, as well as the delay after release of reset, are
+ * configurable per device (port) via CONFIG_OMAP_PORTx_RST_HOLD_US and
+ * CONFIG_OMAP_PORTx_DLY_AFTER_US.
+ *
+ * Applicable to OMAP4/5 only (except for the OMAP4430, where HSIC is not
+ * functional). Valid HSIC ports are:
+ * OMAP4460/70: 1, 2
+ * OMAP5430: 1, 2, 3
+ * OMAP5432: 2, 3
+ */
+#if defined(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO) || \
+ defined(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO) || \
+ defined(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO)
+/* Should not be called for non-HSIC ports */
+static void omap_ehci_hsic_reset(int port, int on, int delay)
+{
+ debug("HSIC device reset: port %d, reset %s, delay %d us\n",
+ port, on ? "On" : "Off", delay);
+#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO
+ if (port == 1) {
+ gpio_request(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO,
+ "USB HSIC1 Reset");
+ gpio_direction_output(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO, !on);
+ }
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO
+ if (port == 2) {
+ gpio_request(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO,
+ "USB HSIC2 Reset");
+ gpio_direction_output(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO, !on);
+ }
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO
+ if (port == 3) {
+ gpio_request(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO,
+ "USB HSIC3 Reset");
+ gpio_direction_output(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO, !on);
+ }
+#endif
+ if (delay)
+ udelay(delay);
+}
+
+/*
+ * Called by ehci-hcd when setting the USB_PORT_FEAT_POWER feature
+ * (overrides __weak function in ehci-hcd.c)
+ */
+void omap_ehci_hsic_reset_device(int port)
+{
+ int rst_hold; /* Reset active hold time, us */
+ int dly_after; /* Delay after releasing reset, us */
+
+ if ((port <= 0) ||
+ !(usbhs_bdp) ||
+ !(is_ehci_hsic_mode(usbhs_bdp->port_mode[port-1])))
+ return;
+
+#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO
+ if (port == 1) {
+#ifdef CONFIG_OMAP_PORT1_RST_HOLD_US
+ rst_hold = CONFIG_OMAP_PORT1_RST_HOLD_US;
+#else
+ rst_hold = 10; /* Provide a default hold time */
+#endif
+#ifdef CONFIG_OMAP_PORT1_DLY_AFTER_US
+ dly_after = CONFIG_OMAP_PORT1_DLY_AFTER_US;
+#else
+ dly_after = 0; /* No delay by default */
+#endif
+ }
+#endif
+
+#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO
+ if (port == 2) {
+#ifdef CONFIG_OMAP_PORT2_RST_HOLD_US
+ rst_hold = CONFIG_OMAP_PORT2_RST_HOLD_US;
+#else
+ rst_hold = 10; /* Provide a default hold time */
+#endif
+#ifdef CONFIG_OMAP_PORT2_DLY_AFTER_US
+ dly_after = CONFIG_OMAP_PORT2_DLY_AFTER_US;
+#else
+ dly_after = 0; /* No delay by default */
+#endif
+ }
+#endif
+
+#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO
+ if (port == 3) {
+#ifdef CONFIG_OMAP_PORT3_RST_HOLD_US
+ rst_hold = CONFIG_OMAP_PORT3_RST_HOLD_US;
+#else
+ rst_hold = 10; /* Provide a default hold time */
+#endif
+#ifdef CONFIG_OMAP_PORT3_DLY_AFTER_US
+ dly_after = CONFIG_OMAP_PORT3_DLY_AFTER_US;
+#else
+ dly_after = 0; /* No delay by default */
+#endif
+ }
+#endif
+ omap_ehci_hsic_reset(port, 1, rst_hold);
+ omap_ehci_hsic_reset(port, 0, dly_after);
+}
+
+#else
+/* No CONFIG_OMAP_HSIC_PORTx_RESET_GPIO defined */
+#define omap_ehci_hsic_reset(port, on, delay) do {} while (0)
+#endif
+
/* Reset is needed otherwise the kernel-driver will throw an error. */
int omap_ehci_hcd_stop(void)
{
debug("Resetting OMAP EHCI\n");
+ /* Put HSIC devices, if any, in RESET */
+#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO
+ omap_ehci_hsic_reset(1, 1, 0);
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO
+ omap_ehci_hsic_reset(2, 1, 0);
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO
+ omap_ehci_hsic_reset(3, 1, 0);
+#endif
+ /* Reset PHYs, if any */
omap_ehci_phy_reset(1, 0);
if (omap_uhh_reset() < 0)
@@ -184,13 +324,15 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata,
int ret;
unsigned int i, reg = 0, rev = 0;
- debug("Initializing OMAP EHCI\n");
+ debug("Initializing OMAP EHCI %d\n", index);
ret = board_usb_init(index, USB_INIT_HOST);
if (ret < 0)
return ret;
- /* Put the PHY in RESET */
+ usbhs_bdp = usbhs_pdata;
+
+ /* Put the PHYs, if any, in RESET */
omap_ehci_phy_reset(1, 10);
ret = omap_uhh_reset();
@@ -230,35 +372,28 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata,
clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
else
setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
- } else if (rev == OMAP_USBHS_REV2) {
-
- clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR),
- OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
-
- /* Clear port mode fields for PHY mode */
-
- if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
- setbits_le32(®, OMAP_P1_MODE_HSIC);
-
- if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
- setbits_le32(®, OMAP_P2_MODE_HSIC);
-
- } else if (rev == OMAP_USBHS_REV2_1) {
+ } else if ((rev == OMAP_USBHS_REV2) || (rev == OMAP_USBHS_REV2_1)) {
+ /*
+ * OMAP4 and OMAP5-ES1 UHH are R.2.0, OMAP5-ES2 - R.2.1
+ *
+ * Clear port mode fields for ULPI PHY mode. On OMAP4 the P3
+ * field is reserved, but clearing it does not harm.
+ */
clrsetbits_le32(®,
(OMAP_P1_MODE_CLEAR |
OMAP_P2_MODE_CLEAR |
OMAP_P3_MODE_CLEAR),
OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
- /* Clear port mode fields for PHY mode */
-
+ /* Warning: HSIC mode for Port 1 not usable on OMAP5432 */
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
setbits_le32(®, OMAP_P1_MODE_HSIC);
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
setbits_le32(®, OMAP_P2_MODE_HSIC);
+ /* Warning: HSIC mode for Port 3 possible on OMAP5 only */
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2]))
setbits_le32(®, OMAP_P3_MODE_HSIC);
}
@@ -270,6 +405,7 @@ int omap_ehci_hcd_init(int index, struct omap_usbhs_board_data *usbhs_pdata,
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i]))
omap_usbhs_hsic_init(i);
+ /* Release ULPI PHY reset and let PLL lock (may need more delay...) */
omap_ehci_phy_reset(0, 10);
/*
--
1.7.12.4 (Apple Git-37)
1
0

[U-Boot] [PATCH] Exynos5: Clock: Generic api to set and get clock rate and source
by Rajeshwari S Shinde 19 Dec '13
by Rajeshwari S Shinde 19 Dec '13
19 Dec '13
This patch implements generic api for exynos5250 and exynos5420.
These api's set and get clock rate based on the peripheral id.
Signed-off-by: Andrew Bresticker <abrestic(a)chromium.org>
Signed-off-by: Rajeshwari S Shinde <rajeshwari.s(a)samsung.com>
---
arch/arm/cpu/armv7/exynos/clock.c | 958 ++++++++++++---------------------
arch/arm/include/asm/arch-exynos/clk.h | 30 +-
drivers/mmc/exynos_dw_mmc.c | 15 +-
3 files changed, 385 insertions(+), 618 deletions(-)
diff --git a/arch/arm/cpu/armv7/exynos/clock.c b/arch/arm/cpu/armv7/exynos/clock.c
index b52e61a..09e156c 100644
--- a/arch/arm/cpu/armv7/exynos/clock.c
+++ b/arch/arm/cpu/armv7/exynos/clock.c
@@ -16,46 +16,97 @@
#define PLL_DIV_65536 65536
/* *
- * This structure is to store the src bit, div bit and prediv bit
- * positions of the peripheral clocks of the src and div registers
+ * This structure store positions of the peripheral clocks
+ * and their source, divider and predivider information.
+ * @periph_id: id of the peripheral
+ * @src_offset: offset of the source register
+ * @div_offset: offset of the divider register
+ * @prediv_offset: offset of the pre divider register
+ * @src_bit: bit location in the source register
+ * @div_bit: bit location in the divider register
+ * @pre_div_bit: bit location in the pre divider register
+ * @src_mask: mask for the source register value
+ * @div_mask: mask for the divider register value
+ * @pre_div_mask: mask for the pre divider register value
*/
struct clk_bit_info {
+ int32_t periph_id;
+ int32_t src_offset;
+ int32_t div_offset;
+ int32_t prediv_offset;
int8_t src_bit;
int8_t div_bit;
- int8_t prediv_bit;
+ int8_t pre_div_bit;
+ int8_t src_mask;
+ int32_t div_mask;
+ int32_t pre_div_mask;
};
-/* src_bit div_bit prediv_bit */
-static struct clk_bit_info clk_bit_info[PERIPH_ID_COUNT] = {
- {0, 0, -1},
- {4, 4, -1},
- {8, 8, -1},
- {12, 12, -1},
- {0, 0, 8},
- {4, 16, 24},
- {8, 0, 8},
- {12, 16, 24},
- {-1, -1, -1},
- {16, 0, 8},
- {20, 16, 24},
- {24, 0, 8},
- {0, 0, 4},
- {4, 12, 16},
- {-1, -1, -1},
- {-1, -1, -1},
- {-1, 24, 0},
- {-1, 24, 0},
- {-1, 24, 0},
- {-1, 24, 0},
- {-1, 24, 0},
- {-1, 24, 0},
- {-1, 24, 0},
- {-1, 24, 0},
- {24, 0, -1},
- {24, 0, -1},
- {24, 0, -1},
- {24, 0, -1},
- {24, 0, -1},
+static struct clk_bit_info exynos5_bit_info_table[] = {
+ {PERIPH_ID_UART0, 0x10250, 0x10558, -1, 0, 0, -1, 0xf, 0xf, -1},
+ {PERIPH_ID_UART1, 0x10250, 0x10558, -1, 4, 4, -1, 0xf, 0xf, -1},
+ {PERIPH_ID_UART2, 0x10250, 0x10558, -1, 8, 8, -1, 0xf, 0xf, -1},
+ {PERIPH_ID_UART3, 0x10250, 0x10558, -1, 12, 12, -1, 0xf, 0xf, -1},
+ {PERIPH_ID_I2C0, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C1, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C2, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C3, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C4, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C5, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C6, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C7, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C8, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C9, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_I2C10, -1, 0x10514, 0x10510, -1, 24, 0, -1, 0x7, 0x7},
+ {PERIPH_ID_SPI0, 0x10254, 0x1055c, 0x1055c, 16, 0, 8, 0xf, 0xf, 0xff},
+ {PERIPH_ID_SPI1, 0x10254, 0x1055c, 0x1055c, 20, 16, 24, 0xf, 0xf, 0xff},
+ {PERIPH_ID_SPI2, 0x10254, 0x10560, 0x10560, 24, 0, 8, 0xf, 0xf, 0xff},
+ {PERIPH_ID_SPI3, 0x10270, 0x10580, 0x10580, 0, 0, 4, 0xf, 0xf, 0xff},
+ {PERIPH_ID_SPI4, 0x10270, 0x10580, 0x10580, 4, 12, 16, 0xf, 0xf, 0xff},
+ {PERIPH_ID_SDMMC0, 0x10244, 0x1054c, 0x1054c, 0, 0, 8, 0xf, 0xf, 0xff},
+ {PERIPH_ID_SDMMC1, 0x10244, 0x1054c, 0x1054c, 4, 16, 24, 0xf, 0xf,
+ 0xff},
+ {PERIPH_ID_SDMMC2, 0x10244, 0x10550, 0x10550, 8, 0, 8, 0xf, 0xf, 0xff},
+ {PERIPH_ID_SDMMC3, 0x10244, 0x10550, 0x10550, 12, 16, 24, 0xf, 0xf,
+ 0xff},
+ {PERIPH_ID_PWM0, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
+ {PERIPH_ID_PWM1, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
+ {PERIPH_ID_PWM2, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
+ {PERIPH_ID_PWM3, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
+ {PERIPH_ID_PWM4, 0x10250, 0x10564, -1, 24, 0, 0xf, 0xf, -1},
+ {PERIPH_ID_I2S0, 0x10240, 0x10544, -1, 0, 0, -1, 0xf, 0xf, -1},
+};
+
+static struct clk_bit_info exynos5420_bit_info_table[] = {
+ {PERIPH_ID_UART0, 0x10250, 0x10558, -1, 4, 8, -1, 0x7, 0xf, -1},
+ {PERIPH_ID_UART1, 0x10250, 0x10558, -1, 8, 12, -1, 0x7, 0xf, -1},
+ {PERIPH_ID_UART2, 0x10250, 0x10558, -1, 12, 16, -1, 0x7, 0xf, -1},
+ {PERIPH_ID_UART3, 0x10250, 0x10558, -1, 16, 20, -1, 0x7, 0xf, -1},
+ {PERIPH_ID_I2C0, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C1, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C2, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C3, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C4, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C5, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C6, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C7, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C8, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C9, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2C10, -1, 0x10504, -1, -1, 8, -1, 0x3f, -1, -1},
+ {PERIPH_ID_SPI0, 0x10254, 0x1055c, 0x10568, 20, 20, 8, 0x7, 0xf, 0xff},
+ {PERIPH_ID_SPI1, 0x10254, 0x1055c, 0x10568, 24, 24, 16, 0x7, 0xf, 0xff},
+ {PERIPH_ID_SPI2, 0x10254, 0x1055c, 0x10568, 28, 28, 24, 0x7, 0xf, 0xff},
+ {PERIPH_ID_SPI3, 0x10270, 0x10584, 0x10584, 12, 16, 0, 0x7, 0xf, 0xff},
+ {PERIPH_ID_SPI4, 0x10270, 0x10584, 0x10584, 16, 20, 8, 0x7, 0xf, 0xff},
+ {PERIPH_ID_SDMMC0, 0x10244, 0x1054c, -1, 8, 0, -1, 0x7, 0x3ff, -1},
+ {PERIPH_ID_SDMMC1, 0x10244, 0x1054c, -1, 12, 10, -1, 0x7, 0x3ff, -1},
+ {PERIPH_ID_SDMMC2, 0x10244, 0x1054c, -1, 16, 20, -1, 0x7, 0x3ff, -1},
+ {PERIPH_ID_PWM0, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_PWM1, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_PWM2, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_PWM3, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_PWM4, -1, 0x10504, -1, -1, 8, -1, -1, 0x3f, -1},
+ {PERIPH_ID_I2S0, 0x10240, 0x10544, -1, 28, 20, -1, 0x7, 0xf, -1},
};
/* Epll Clock division values to achive different frequency output */
@@ -69,6 +120,27 @@ static struct set_epll_con_val exynos5_epll_div[] = {
{ 180633600, 0, 45, 3, 1, 10381 }
};
+static struct clk_bit_info *get_table_index(int periph_id)
+{
+ int i, count;
+ struct clk_bit_info *table;
+
+ if (proid_is_exynos5420()) {
+ table = exynos5420_bit_info_table;
+ count = ARRAY_SIZE(exynos5420_bit_info_table);
+ } else {
+ table = exynos5_bit_info_table;
+ count = ARRAY_SIZE(exynos5_bit_info_table);
+ }
+
+ for (i = 0; i < count; i++) {
+ if ((table + i)->periph_id == periph_id)
+ return table + i;
+ }
+
+ return NULL;
+}
+
/* exynos: return pll clock frequency */
static int exynos_get_pll_clk(int pllreg, unsigned int r, unsigned int k)
{
@@ -258,111 +330,6 @@ static unsigned long exynos5_get_pll_clk(int pllreg)
return fout;
}
-static unsigned long exynos5_get_periph_rate(int peripheral)
-{
- struct clk_bit_info *bit_info = &clk_bit_info[peripheral];
- unsigned long sclk, sub_clk;
- unsigned int src, div, sub_div;
- struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
-
- switch (peripheral) {
- case PERIPH_ID_UART0:
- case PERIPH_ID_UART1:
- case PERIPH_ID_UART2:
- case PERIPH_ID_UART3:
- src = readl(&clk->src_peric0);
- div = readl(&clk->div_peric0);
- break;
- case PERIPH_ID_PWM0:
- case PERIPH_ID_PWM1:
- case PERIPH_ID_PWM2:
- case PERIPH_ID_PWM3:
- case PERIPH_ID_PWM4:
- src = readl(&clk->src_peric0);
- div = readl(&clk->div_peric3);
- break;
- case PERIPH_ID_I2S0:
- src = readl(&clk->src_mau);
- div = readl(&clk->div_mau);
- case PERIPH_ID_SPI0:
- case PERIPH_ID_SPI1:
- src = readl(&clk->src_peric1);
- div = readl(&clk->div_peric1);
- break;
- case PERIPH_ID_SPI2:
- src = readl(&clk->src_peric1);
- div = readl(&clk->div_peric2);
- break;
- case PERIPH_ID_SPI3:
- case PERIPH_ID_SPI4:
- src = readl(&clk->sclk_src_isp);
- div = readl(&clk->sclk_div_isp);
- break;
- case PERIPH_ID_SDMMC0:
- case PERIPH_ID_SDMMC1:
- case PERIPH_ID_SDMMC2:
- case PERIPH_ID_SDMMC3:
- src = readl(&clk->src_fsys);
- div = readl(&clk->div_fsys1);
- break;
- case PERIPH_ID_I2C0:
- case PERIPH_ID_I2C1:
- case PERIPH_ID_I2C2:
- case PERIPH_ID_I2C3:
- case PERIPH_ID_I2C4:
- case PERIPH_ID_I2C5:
- case PERIPH_ID_I2C6:
- case PERIPH_ID_I2C7:
- sclk = exynos5_get_pll_clk(MPLL);
- sub_div = ((readl(&clk->div_top1) >> bit_info->div_bit)
- & 0x7) + 1;
- div = ((readl(&clk->div_top0) >> bit_info->prediv_bit)
- & 0x7) + 1;
- return (sclk / sub_div) / div;
- default:
- debug("%s: invalid peripheral %d", __func__, peripheral);
- return -1;
- };
-
- src = (src >> bit_info->src_bit) & 0xf;
-
- switch (src) {
- case EXYNOS_SRC_MPLL:
- sclk = exynos5_get_pll_clk(MPLL);
- break;
- case EXYNOS_SRC_EPLL:
- sclk = exynos5_get_pll_clk(EPLL);
- break;
- case EXYNOS_SRC_VPLL:
- sclk = exynos5_get_pll_clk(VPLL);
- break;
- default:
- return 0;
- }
-
- /* Ratio clock division for this peripheral */
- sub_div = (div >> bit_info->div_bit) & 0xf;
- sub_clk = sclk / (sub_div + 1);
-
- /* Pre-ratio clock division for SDMMC0 and 2 */
- if (peripheral == PERIPH_ID_SDMMC0 || peripheral == PERIPH_ID_SDMMC2) {
- div = (div >> bit_info->prediv_bit) & 0xff;
- return sub_clk / (div + 1);
- }
-
- return sub_clk;
-}
-
-unsigned long clock_get_periph_rate(int peripheral)
-{
- if (cpu_is_exynos5())
- return exynos5_get_periph_rate(peripheral);
- else
- return 0;
-}
-
-/* exynos5420: return pll clock frequency */
static unsigned long exynos5420_get_pll_clk(int pllreg)
{
struct exynos5420_clock *clk =
@@ -391,6 +358,15 @@ static unsigned long exynos5420_get_pll_clk(int pllreg)
r = readl(&clk->rpll_con0);
k = readl(&clk->rpll_con1);
break;
+ case SPLL:
+ r = readl(&clk->spll_con0);
+ break;
+ case CPLL:
+ r = readl(&clk->cpll_con0);
+ break;
+ case DPLL:
+ r = readl(&clk->dpll_con0);
+ break;
default:
printf("Unsupported PLL (%d)\n", pllreg);
return 0;
@@ -399,6 +375,240 @@ static unsigned long exynos5420_get_pll_clk(int pllreg)
return exynos_get_pll_clk(pllreg, r, k);
}
+static unsigned long exynos5420_src_clk(int peripheral)
+{
+ unsigned int src;
+ unsigned long sclk;
+ unsigned long clk_base = samsung_get_base_clock();
+ struct clk_bit_info *bit_info = get_table_index(peripheral);
+
+ /*
+ * I2C and PWM clocks are parented by aclk66_peric which is
+ * parented by CPLL (initialized in exynos5420_clock_init()).
+ */
+ if (bit_info->src_offset < 0)
+ return get_pll_clk(CPLL);
+
+ src = readl(clk_base + bit_info->src_offset);
+ src = (src >> bit_info->src_bit) & bit_info->src_mask;
+
+ switch (src) {
+ case 0x3:
+ sclk = get_pll_clk(MPLL);
+ break;
+ case 0x6:
+ sclk = get_pll_clk(EPLL);
+ break;
+ case 0x7:
+ sclk = get_pll_clk(RPLL);
+ break;
+ default:
+ sclk = 0;
+ }
+ return sclk;
+}
+
+static long exynos5_src_clk(int peripheral)
+{
+ unsigned int src;
+ unsigned long sclk;
+ unsigned long clk_base = samsung_get_base_clock();
+ struct clk_bit_info *bit_info = get_table_index(peripheral);
+
+ /*
+ * I2C clocks are parented by aclk66 which is always parented by
+ * MPLL on 5250.
+ */
+ if (bit_info->src_offset < 0)
+ return get_pll_clk(MPLL);
+
+ src = readl(clk_base + bit_info->src_offset);
+ src = (src >> bit_info->src_bit) & bit_info->src_mask;
+
+ switch (src) {
+ case 0x6:
+ sclk = get_pll_clk(MPLL);
+ break;
+ case 0x7:
+ sclk = get_pll_clk(EPLL);
+ break;
+ case 0x8:
+ sclk = get_pll_clk(RPLL);
+ break;
+ default:
+ sclk = -1;
+ }
+ return sclk;
+}
+
+static unsigned long get_src_clk(int peripheral)
+{
+ if (proid_is_exynos5420())
+ return exynos5420_src_clk(peripheral);
+ else if (proid_is_exynos5250())
+ return exynos5_src_clk(peripheral);
+ else
+ return 0;
+}
+
+long clock_get_periph_rate(int peripheral)
+{
+ struct clk_bit_info *bit_info = NULL;
+ unsigned long sclk, sub_clk;
+ unsigned int div, sub_div = 0;
+ unsigned long clk_base = samsung_get_base_clock();
+
+ bit_info = get_table_index(peripheral);
+ if (!bit_info) {
+ debug("Invalid peripheral id\n");
+ return -1;
+ }
+
+ sclk = get_src_clk(peripheral);
+ if (sclk < 0) {
+ debug("Unknown source clock\n");
+ return -1;
+ }
+
+ div = readl(clk_base + bit_info->div_offset);
+
+ /* Ratio clock division for this peripheral */
+ div = (div >> bit_info->div_bit) & bit_info->div_mask;
+ sub_clk = sclk / (div + 1);
+
+ if (bit_info->prediv_offset >= 0) {
+ sub_div = readl(clk_base + bit_info->prediv_offset);
+ sub_div = (sub_div >> bit_info->pre_div_bit) &
+ bit_info->pre_div_mask;
+ return sub_clk / (sub_div + 1);
+ }
+
+ return sub_clk;
+}
+
+/**
+ * Linearly searches for the most accurate main and fine stage clock scalars
+ * (divisors) for a specified target frequency and scalar bit sizes by checking
+ * all multiples of main_scalar_bits values. Will always return scalars up to or
+ * slower than target.
+ *
+ * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32
+ * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32
+ * @param input_freq Clock frequency to be scaled in Hz
+ * @param target_freq Desired clock frequency in Hz
+ * @param best_fine_scalar Pointer to store the fine stage divisor
+ *
+ * @return best_main_scalar Main scalar for desired frequency or -1 if none
+ * found
+ */
+static int clock_calc_best_scalar(unsigned int main_scaler_bits,
+ unsigned int fine_scalar_bits, unsigned int input_rate,
+ unsigned int target_rate, unsigned int *best_fine_scalar)
+{
+ int i;
+ int best_main_scalar = -1;
+ unsigned int best_error = target_rate;
+ const unsigned int cap = (1 << fine_scalar_bits) - 1;
+ const unsigned int loops = 1 << main_scaler_bits;
+
+ debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate,
+ target_rate, cap);
+
+ assert(best_fine_scalar != NULL);
+ assert(main_scaler_bits <= fine_scalar_bits);
+
+ *best_fine_scalar = 1;
+
+ if (input_rate == 0 || target_rate == 0)
+ return -1;
+
+ if (target_rate >= input_rate)
+ return 1;
+
+ for (i = 1; i <= loops; i++) {
+ const unsigned int effective_div = max(min(input_rate / i /
+ target_rate, cap), 1);
+ const unsigned int effective_rate = input_rate / i /
+ effective_div;
+ const int error = target_rate - effective_rate;
+
+ debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div,
+ effective_rate, error);
+
+ if (error >= 0 && error <= best_error) {
+ best_error = error;
+ best_main_scalar = i;
+ *best_fine_scalar = effective_div;
+ }
+ }
+
+ return best_main_scalar;
+}
+
+int clock_set_periph_source(int periph_id, int src)
+{
+ struct clk_bit_info *bit_info = NULL;
+ unsigned long clk_base = samsung_get_base_clock();
+
+ bit_info = get_table_index(periph_id);
+ if (!bit_info) {
+ debug("Invalid peripheral id\n");
+ return -1;
+ }
+
+ clrsetbits_le32(clk_base + bit_info->src_offset,
+ bit_info->src_mask << bit_info->src_bit,
+ (src & bit_info->src_mask) << bit_info->src_bit);
+
+ return 0;
+}
+
+int clock_set_periph_rate(int periph_id, unsigned long rate)
+{
+ struct clk_bit_info *bit_info = NULL;
+ int div;
+ unsigned int pre_div = 0;
+ unsigned long sclk, sub_clk;
+ unsigned long clk_base = samsung_get_base_clock();
+
+ bit_info = get_table_index(periph_id);
+ if (!bit_info) {
+ debug("Invalid peripheral id\n");
+ return -1;
+ }
+
+ if ((PERIPH_ID_SPI0 <= periph_id && periph_id <= PERIPH_ID_SPI2) ||
+ PERIPH_ID_SPI3 == periph_id || periph_id == PERIPH_ID_SPI4) {
+ div = clock_calc_best_scalar(4, 8, 400000000, rate, &pre_div);
+ if (div < 0) {
+ debug("%s: Cannot set clock rate %lu for periph %d",
+ __func__, rate, periph_id);
+ return -1;
+ }
+ div = div - 1;
+ pre_div = pre_div - 1;
+ } else {
+ sclk = get_src_clk(periph_id);
+ div = DIV_ROUND_UP(sclk, rate);
+ if (bit_info->prediv_offset >= 0) {
+ sub_clk = sclk / (div + 1);
+ pre_div = DIV_ROUND_UP(sub_clk, rate);
+ }
+ }
+
+ clrsetbits_le32(clk_base + bit_info->div_offset,
+ bit_info->div_mask << bit_info->div_bit,
+ (div & bit_info->div_mask) << bit_info->div_bit);
+
+ if (bit_info->prediv_offset >= 0)
+ clrsetbits_le32(clk_base + bit_info->prediv_offset,
+ bit_info->pre_div_mask << bit_info->pre_div_bit,
+ (pre_div & bit_info->pre_div_mask) <<
+ bit_info->pre_div_bit);
+
+ return 0;
+}
+
/* exynos4: return ARM clock frequency */
static unsigned long exynos4_get_arm_clk(void)
{
@@ -522,27 +732,6 @@ static unsigned long exynos4x12_get_pwm_clk(void)
return pclk;
}
-/* exynos5420: return pwm clock frequency */
-static unsigned long exynos5420_get_pwm_clk(void)
-{
- struct exynos5420_clock *clk =
- (struct exynos5420_clock *)samsung_get_base_clock();
- unsigned long pclk, sclk;
- unsigned int ratio;
-
- /*
- * CLK_DIV_PERIC3
- * PWM_RATIO [3:0]
- */
- ratio = readl(&clk->div_peric0);
- ratio = (ratio >> 28) & 0xf;
- sclk = get_pll_clk(MPLL);
-
- pclk = sclk / (ratio + 1);
-
- return pclk;
-}
-
/* exynos4: return uart clock frequency */
static unsigned long exynos4_get_uart_clk(int dev_index)
{
@@ -635,100 +824,6 @@ static unsigned long exynos4x12_get_uart_clk(int dev_index)
return uclk;
}
-/* exynos5: return uart clock frequency */
-static unsigned long exynos5_get_uart_clk(int dev_index)
-{
- struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
- unsigned long uclk, sclk;
- unsigned int sel;
- unsigned int ratio;
-
- /*
- * CLK_SRC_PERIC0
- * UART0_SEL [3:0]
- * UART1_SEL [7:4]
- * UART2_SEL [8:11]
- * UART3_SEL [12:15]
- * UART4_SEL [16:19]
- * UART5_SEL [23:20]
- */
- sel = readl(&clk->src_peric0);
- sel = (sel >> (dev_index << 2)) & 0xf;
-
- if (sel == 0x6)
- sclk = get_pll_clk(MPLL);
- else if (sel == 0x7)
- sclk = get_pll_clk(EPLL);
- else if (sel == 0x8)
- sclk = get_pll_clk(VPLL);
- else
- return 0;
-
- /*
- * CLK_DIV_PERIC0
- * UART0_RATIO [3:0]
- * UART1_RATIO [7:4]
- * UART2_RATIO [8:11]
- * UART3_RATIO [12:15]
- * UART4_RATIO [16:19]
- * UART5_RATIO [23:20]
- */
- ratio = readl(&clk->div_peric0);
- ratio = (ratio >> (dev_index << 2)) & 0xf;
-
- uclk = sclk / (ratio + 1);
-
- return uclk;
-}
-
-/* exynos5420: return uart clock frequency */
-static unsigned long exynos5420_get_uart_clk(int dev_index)
-{
- struct exynos5420_clock *clk =
- (struct exynos5420_clock *)samsung_get_base_clock();
- unsigned long uclk, sclk;
- unsigned int sel;
- unsigned int ratio;
-
- /*
- * CLK_SRC_PERIC0
- * UART0_SEL [3:0]
- * UART1_SEL [7:4]
- * UART2_SEL [8:11]
- * UART3_SEL [12:15]
- * UART4_SEL [16:19]
- * UART5_SEL [23:20]
- */
- sel = readl(&clk->src_peric0);
- sel = (sel >> ((dev_index * 4) + 4)) & 0x7;
-
- if (sel == 0x3)
- sclk = get_pll_clk(MPLL);
- else if (sel == 0x6)
- sclk = get_pll_clk(EPLL);
- else if (sel == 0x7)
- sclk = get_pll_clk(RPLL);
- else
- return 0;
-
- /*
- * CLK_DIV_PERIC0
- * UART0_RATIO [3:0]
- * UART1_RATIO [7:4]
- * UART2_RATIO [8:11]
- * UART3_RATIO [12:15]
- * UART4_RATIO [16:19]
- * UART5_RATIO [23:20]
- */
- ratio = readl(&clk->div_peric0);
- ratio = (ratio >> ((dev_index * 4) + 8)) & 0xf;
-
- uclk = sclk / (ratio + 1);
-
- return uclk;
-}
-
static unsigned long exynos4_get_mmc_clk(int dev_index)
{
struct exynos4_clock *clk =
@@ -778,79 +873,6 @@ static unsigned long exynos4_get_mmc_clk(int dev_index)
return uclk;
}
-static unsigned long exynos5_get_mmc_clk(int dev_index)
-{
- struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
- unsigned long uclk, sclk;
- unsigned int sel, ratio, pre_ratio;
- int shift = 0;
-
- sel = readl(&clk->src_fsys);
- sel = (sel >> (dev_index << 2)) & 0xf;
-
- if (sel == 0x6)
- sclk = get_pll_clk(MPLL);
- else if (sel == 0x7)
- sclk = get_pll_clk(EPLL);
- else if (sel == 0x8)
- sclk = get_pll_clk(VPLL);
- else
- return 0;
-
- switch (dev_index) {
- case 0:
- case 1:
- ratio = readl(&clk->div_fsys1);
- pre_ratio = readl(&clk->div_fsys1);
- break;
- case 2:
- case 3:
- ratio = readl(&clk->div_fsys2);
- pre_ratio = readl(&clk->div_fsys2);
- break;
- default:
- return 0;
- }
-
- if (dev_index == 1 || dev_index == 3)
- shift = 16;
-
- ratio = (ratio >> shift) & 0xf;
- pre_ratio = (pre_ratio >> (shift + 8)) & 0xff;
- uclk = (sclk / (ratio + 1)) / (pre_ratio + 1);
-
- return uclk;
-}
-
-static unsigned long exynos5420_get_mmc_clk(int dev_index)
-{
- struct exynos5420_clock *clk =
- (struct exynos5420_clock *)samsung_get_base_clock();
- unsigned long uclk, sclk;
- unsigned int sel, ratio;
- int shift = 0;
-
- sel = readl(&clk->src_fsys);
- sel = (sel >> ((dev_index * 4) + 8)) & 0x7;
-
- if (sel == 0x3)
- sclk = get_pll_clk(MPLL);
- else if (sel == 0x6)
- sclk = get_pll_clk(EPLL);
- else
- return 0;
-
- ratio = readl(&clk->div_fsys1);
-
- shift = dev_index * 10;
-
- ratio = (ratio >> shift) & 0x3ff;
- uclk = (sclk / (ratio + 1));
-
- return uclk;
-}
-
/* exynos4: set the mmc clock */
static void exynos4_set_mmc_clk(int dev_index, unsigned int div)
{
@@ -910,50 +932,6 @@ static void exynos4x12_set_mmc_clk(int dev_index, unsigned int div)
writel(val, addr);
}
-/* exynos5: set the mmc clock */
-static void exynos5_set_mmc_clk(int dev_index, unsigned int div)
-{
- struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
- unsigned int addr;
- unsigned int val;
-
- /*
- * CLK_DIV_FSYS1
- * MMC0_PRE_RATIO [15:8], MMC1_PRE_RATIO [31:24]
- * CLK_DIV_FSYS2
- * MMC2_PRE_RATIO [15:8], MMC3_PRE_RATIO [31:24]
- */
- if (dev_index < 2) {
- addr = (unsigned int)&clk->div_fsys1;
- } else {
- addr = (unsigned int)&clk->div_fsys2;
- dev_index -= 2;
- }
-
- val = readl(addr);
- val &= ~(0xff << ((dev_index << 4) + 8));
- val |= (div & 0xff) << ((dev_index << 4) + 8);
- writel(val, addr);
-}
-
-/* exynos5: set the mmc clock */
-static void exynos5420_set_mmc_clk(int dev_index, unsigned int div)
-{
- struct exynos5420_clock *clk =
- (struct exynos5420_clock *)samsung_get_base_clock();
- unsigned int addr;
- unsigned int val, shift;
-
- addr = (unsigned int)&clk->div_fsys1;
- shift = dev_index * 10;
-
- val = readl(addr);
- val &= ~(0x3ff << shift);
- val |= (div & 0x3ff) << shift;
- writel(val, addr);
-}
-
/* get_lcd_clk: return lcd clock frequency */
static unsigned long exynos4_get_lcd_clk(void)
{
@@ -1222,28 +1200,6 @@ void exynos4_set_mipi_clk(void)
writel(cfg, &clk->div_lcd0);
}
-/*
- * I2C
- *
- * exynos5: obtaining the I2C clock
- */
-static unsigned long exynos5_get_i2c_clk(void)
-{
- struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
- unsigned long aclk_66, aclk_66_pre, sclk;
- unsigned int ratio;
-
- sclk = get_pll_clk(MPLL);
-
- ratio = (readl(&clk->div_top1)) >> 24;
- ratio &= 0x7;
- aclk_66_pre = sclk / (ratio + 1);
- ratio = readl(&clk->div_top0);
- ratio &= 0x7;
- aclk_66 = aclk_66_pre / (ratio + 1);
- return aclk_66;
-}
int exynos5_set_epll_clk(unsigned long rate)
{
@@ -1358,200 +1314,6 @@ int exynos5_set_i2s_clk_prescaler(unsigned int src_frq,
return 0;
}
-/**
- * Linearly searches for the most accurate main and fine stage clock scalars
- * (divisors) for a specified target frequency and scalar bit sizes by checking
- * all multiples of main_scalar_bits values. Will always return scalars up to or
- * slower than target.
- *
- * @param main_scalar_bits Number of main scalar bits, must be > 0 and < 32
- * @param fine_scalar_bits Number of fine scalar bits, must be > 0 and < 32
- * @param input_freq Clock frequency to be scaled in Hz
- * @param target_freq Desired clock frequency in Hz
- * @param best_fine_scalar Pointer to store the fine stage divisor
- *
- * @return best_main_scalar Main scalar for desired frequency or -1 if none
- * found
- */
-static int clock_calc_best_scalar(unsigned int main_scaler_bits,
- unsigned int fine_scalar_bits, unsigned int input_rate,
- unsigned int target_rate, unsigned int *best_fine_scalar)
-{
- int i;
- int best_main_scalar = -1;
- unsigned int best_error = target_rate;
- const unsigned int cap = (1 << fine_scalar_bits) - 1;
- const unsigned int loops = 1 << main_scaler_bits;
-
- debug("Input Rate is %u, Target is %u, Cap is %u\n", input_rate,
- target_rate, cap);
-
- assert(best_fine_scalar != NULL);
- assert(main_scaler_bits <= fine_scalar_bits);
-
- *best_fine_scalar = 1;
-
- if (input_rate == 0 || target_rate == 0)
- return -1;
-
- if (target_rate >= input_rate)
- return 1;
-
- for (i = 1; i <= loops; i++) {
- const unsigned int effective_div = max(min(input_rate / i /
- target_rate, cap), 1);
- const unsigned int effective_rate = input_rate / i /
- effective_div;
- const int error = target_rate - effective_rate;
-
- debug("%d|effdiv:%u, effrate:%u, error:%d\n", i, effective_div,
- effective_rate, error);
-
- if (error >= 0 && error <= best_error) {
- best_error = error;
- best_main_scalar = i;
- *best_fine_scalar = effective_div;
- }
- }
-
- return best_main_scalar;
-}
-
-static int exynos5_set_spi_clk(enum periph_id periph_id,
- unsigned int rate)
-{
- struct exynos5_clock *clk =
- (struct exynos5_clock *)samsung_get_base_clock();
- int main;
- unsigned int fine;
- unsigned shift, pre_shift;
- unsigned mask = 0xff;
- u32 *reg;
-
- main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine);
- if (main < 0) {
- debug("%s: Cannot set clock rate for periph %d",
- __func__, periph_id);
- return -1;
- }
- main = main - 1;
- fine = fine - 1;
-
- switch (periph_id) {
- case PERIPH_ID_SPI0:
- reg = &clk->div_peric1;
- shift = 0;
- pre_shift = 8;
- break;
- case PERIPH_ID_SPI1:
- reg = &clk->div_peric1;
- shift = 16;
- pre_shift = 24;
- break;
- case PERIPH_ID_SPI2:
- reg = &clk->div_peric2;
- shift = 0;
- pre_shift = 8;
- break;
- case PERIPH_ID_SPI3:
- reg = &clk->sclk_div_isp;
- shift = 0;
- pre_shift = 4;
- break;
- case PERIPH_ID_SPI4:
- reg = &clk->sclk_div_isp;
- shift = 12;
- pre_shift = 16;
- break;
- default:
- debug("%s: Unsupported peripheral ID %d\n", __func__,
- periph_id);
- return -1;
- }
- clrsetbits_le32(reg, mask << shift, (main & mask) << shift);
- clrsetbits_le32(reg, mask << pre_shift, (fine & mask) << pre_shift);
-
- return 0;
-}
-
-static int exynos5420_set_spi_clk(enum periph_id periph_id,
- unsigned int rate)
-{
- struct exynos5420_clock *clk =
- (struct exynos5420_clock *)samsung_get_base_clock();
- int main;
- unsigned int fine, val;
- unsigned shift, pre_shift;
- unsigned div_mask = 0xf, pre_div_mask = 0xff;
-
- main = clock_calc_best_scalar(4, 8, 400000000, rate, &fine);
- if (main < 0) {
- debug("%s: Cannot set clock rate for periph %d",
- __func__, periph_id);
- return -1;
- }
- main = main - 1;
- fine = fine - 1;
-
- switch (periph_id) {
- case PERIPH_ID_SPI0:
- val = readl(&clk->div_peric1);
- val &= ~(div_mask << 20);
- val |= (main << 20);
- writel(val, &clk->div_peric1);
-
- val = readl(&clk->div_peric4);
- val &= ~(pre_div_mask << 8);
- val |= (fine << 8);
- writel(val, &clk->div_peric4);
- break;
- case PERIPH_ID_SPI1:
- val = readl(&clk->div_peric1);
- val &= ~(div_mask << 24);
- val |= (main << 24);
- writel(val, &clk->div_peric1);
-
- val = readl(&clk->div_peric4);
- val &= ~(pre_div_mask << 16);
- val |= (fine << 16);
- writel(val, &clk->div_peric4);
- break;
- case PERIPH_ID_SPI2:
- val = readl(&clk->div_peric1);
- val &= ~(div_mask << 28);
- val |= (main << 28);
- writel(val, &clk->div_peric1);
-
- val = readl(&clk->div_peric4);
- val &= ~(pre_div_mask << 24);
- val |= (fine << 24);
- writel(val, &clk->div_peric4);
- break;
- case PERIPH_ID_SPI3:
- shift = 16;
- pre_shift = 0;
- clrsetbits_le32(&clk->div_isp1, div_mask << shift,
- (main & div_mask) << shift);
- clrsetbits_le32(&clk->div_isp1, pre_div_mask << pre_shift,
- (fine & pre_div_mask) << pre_shift);
- break;
- case PERIPH_ID_SPI4:
- shift = 20;
- pre_shift = 8;
- clrsetbits_le32(&clk->div_isp1, div_mask << shift,
- (main & div_mask) << shift);
- clrsetbits_le32(&clk->div_isp1, pre_div_mask << pre_shift,
- (fine & pre_div_mask) << pre_shift);
- break;
- default:
- debug("%s: Unsupported peripheral ID %d\n", __func__,
- periph_id);
- return -1;
- }
-
- return 0;
-}
-
static unsigned long exynos4_get_i2c_clk(void)
{
struct exynos4_clock *clk =
@@ -1594,7 +1356,7 @@ unsigned long get_arm_clk(void)
unsigned long get_i2c_clk(void)
{
if (cpu_is_exynos5()) {
- return exynos5_get_i2c_clk();
+ return clock_get_periph_rate(PERIPH_ID_I2C0);
} else if (cpu_is_exynos4()) {
return exynos4_get_i2c_clk();
} else {
@@ -1606,8 +1368,6 @@ unsigned long get_i2c_clk(void)
unsigned long get_pwm_clk(void)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420())
- return exynos5420_get_pwm_clk();
return clock_get_periph_rate(PERIPH_ID_PWM0);
} else {
if (proid_is_exynos4412())
@@ -1619,9 +1379,7 @@ unsigned long get_pwm_clk(void)
unsigned long get_uart_clk(int dev_index)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420())
- return exynos5420_get_uart_clk(dev_index);
- return exynos5_get_uart_clk(dev_index);
+ return clock_get_periph_rate(PERIPH_ID_UART0 + dev_index);
} else {
if (proid_is_exynos4412())
return exynos4x12_get_uart_clk(dev_index);
@@ -1632,9 +1390,7 @@ unsigned long get_uart_clk(int dev_index)
unsigned long get_mmc_clk(int dev_index)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420())
- return exynos5420_get_mmc_clk(dev_index);
- return exynos5_get_mmc_clk(dev_index);
+ return clock_get_periph_rate(PERIPH_ID_SDMMC0 + dev_index);
} else {
return exynos4_get_mmc_clk(dev_index);
}
@@ -1642,15 +1398,9 @@ unsigned long get_mmc_clk(int dev_index)
void set_mmc_clk(int dev_index, unsigned int div)
{
- if (cpu_is_exynos5()) {
- if (proid_is_exynos5420())
- return exynos5420_set_mmc_clk(dev_index, div);
- exynos5_set_mmc_clk(dev_index, div);
- } else {
if (proid_is_exynos4412())
exynos4x12_set_mmc_clk(dev_index, div);
exynos4_set_mmc_clk(dev_index, div);
- }
}
unsigned long get_lcd_clk(void)
@@ -1678,9 +1428,7 @@ void set_mipi_clk(void)
int set_spi_clk(int periph_id, unsigned int rate)
{
if (cpu_is_exynos5()) {
- if (proid_is_exynos5420())
- return exynos5420_set_spi_clk(periph_id, rate);
- return exynos5_set_spi_clk(periph_id, rate);
+ return clock_set_periph_rate(periph_id, rate);
} else {
return 0;
}
diff --git a/arch/arm/include/asm/arch-exynos/clk.h b/arch/arm/include/asm/arch-exynos/clk.h
index cdeef32..a717805 100644
--- a/arch/arm/include/asm/arch-exynos/clk.h
+++ b/arch/arm/include/asm/arch-exynos/clk.h
@@ -15,12 +15,9 @@
#define VPLL 4
#define BPLL 5
#define RPLL 6
-
-enum pll_src_bit {
- EXYNOS_SRC_MPLL = 6,
- EXYNOS_SRC_EPLL,
- EXYNOS_SRC_VPLL,
-};
+#define SPLL 7
+#define CPLL 8
+#define DPLL 9
unsigned long get_pll_clk(int pllreg);
unsigned long get_arm_clk(void);
@@ -45,6 +42,25 @@ int set_spi_clk(int periph_id, unsigned int rate);
*
* @return frequency of the peripheral clk
*/
-unsigned long clock_get_periph_rate(int peripheral);
+long clock_get_periph_rate(int peripheral);
+
+/**
+ * set the clk frequency rate of the required peripheral
+ *
+ * @param peripheral Peripheral id
+ * @param rate frequency to be set
+ *
+ * @return 0 if success else -1
+ */
+int clock_set_periph_rate(int periph_id, unsigned long rate);
+/**
+ * set the clk source mux value of the required peripheral
+ *
+ * @param peripheral Peripheral id
+ * @param src source to be set
+ *
+ * @return 0 if success else -1
+ */
+int clock_set_periph_source(int periph_id, int src);
#endif
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index f7439a0..2290330 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -45,19 +45,22 @@ unsigned int exynos_dwmci_get_clk(int dev_index)
int exynos_dwmci_add_port(int index, u32 regbase, int bus_width, u32 clksel)
{
struct dwmci_host *host = NULL;
- unsigned int div;
- unsigned long freq, sclk;
+ int ret;
+ unsigned long freq;
host = malloc(sizeof(struct dwmci_host));
if (!host) {
printf("dwmci_host malloc fail!\n");
return 1;
}
+
/* request mmc clock vlaue of 52MHz. */
freq = 52000000;
- sclk = get_mmc_clk(index);
- div = DIV_ROUND_UP(sclk, freq);
- /* set the clock divisor for mmc */
- set_mmc_clk(index, div);
+ /* set the clock rate for mmc */
+ ret = clock_set_periph_rate(PERIPH_ID_SDMMC0 + index, freq);
+ if (ret < 0) {
+ debug("Clock rate not set\n");
+ return -1;
+ }
host->name = "EXYNOS DWMMC";
host->ioaddr = (void *)regbase;
--
1.7.12.4
3
2

19 Dec '13
This patch adds basic board support for SMDK5420 board.
These patches are tested for booting fine on EVT1 SMDK5420.
Changes in V2:
- Corrected a compilation issue for SMDK5420.
Changes in V3:
- Add patch to support variable size SPL support
- Add patch to disable SMU for eMMC.
Changes in V4:
- Added check for MAX77686 pmic compilation.
- Added correct calculation of gpio based addresses.
- Rebased on the latest u-boot code.
- Removed patches for UART and TZPC changes as
they were not needed.
- Added flag to disable SMU for eMMC.
Changes in V5:
- Moved functions board_mmc_init and board_eth_init
to common/board.c in case of device tree support.
Changes in V6:
- Rebased on the latest mainline branch.
- Moved the definitions for SMU to arch/arm dwmmc.h
Changes in V7:
- Removed below patch as it is already merged
"DWMMC: SMDK5420: Disable SMU for eMMC"
- Corrected the multi line comments and removal of
blank spaces and lines.
- Corrected the license.
Changes in V8:
- corrected the if loops with if conditions of
pro_id and cpu_id.
Changes in V9:
- Added macros to get the base address
- Rebased on latest code.
Changes in V10:
- Added new structures for Power and DMC registers for
5420.
- Changed the input parameters for common dmc functions.
- Removed unnecesarry blank lines and added where ever required.
Changes in V11:
-Rebased on latest u-boot-samsung branch.
Rajeshwari S Shinde (11):
EXYNOS5: Create a common board file
Exynos5420: Add base addresses for 5420
EXYNOS5420: Add power register structure.
EXYNOS5420: Add dmc and phy_control register structure
Exynos5420: Add clock initialization for 5420
Exynos5420: Add DDR3 initialization for 5420
Exynos5420: Add support for 5420 in pinmux and gpio
Exynos5420: Add base patch for SMDK5420
DTS: Add dts support for SMDK5420
Config: Add initial config for SMDK5420
SPL: EXYNOS: Prepare for variable size SPL support
arch/arm/cpu/armv7/exynos/clock.c | 279 ++++++++-
arch/arm/cpu/armv7/exynos/clock_init.h | 17 +
arch/arm/cpu/armv7/exynos/clock_init_exynos5.c | 352 ++++++++++-
arch/arm/cpu/armv7/exynos/dmc_common.c | 60 +-
arch/arm/cpu/armv7/exynos/dmc_init_ddr3.c | 439 ++++++++++++-
arch/arm/cpu/armv7/exynos/exynos5_setup.h | 764 ++++++++++++++++------
arch/arm/cpu/armv7/exynos/pinmux.c | 260 +++++++-
arch/arm/dts/exynos5.dtsi | 198 ++++++
arch/arm/dts/exynos5250.dtsi | 194 +-----
arch/arm/dts/exynos5420.dtsi | 70 +++
arch/arm/include/asm/arch-exynos/board.h | 17 +
arch/arm/include/asm/arch-exynos/clk.h | 1 +
arch/arm/include/asm/arch-exynos/clock.h | 494 +++++++++++++++
arch/arm/include/asm/arch-exynos/cpu.h | 52 +-
arch/arm/include/asm/arch-exynos/dmc.h | 177 ++++++
arch/arm/include/asm/arch-exynos/gpio.h | 143 ++++-
arch/arm/include/asm/arch-exynos/periph.h | 3 +
arch/arm/include/asm/arch-exynos/power.h | 837 +++++++++++++++++++++++++
board/samsung/common/Makefile | 4 +
board/samsung/common/board.c | 410 ++++++++++++
board/samsung/dts/exynos5420-smdk5420.dts | 169 +++++
board/samsung/smdk5250/exynos5-dt.c | 352 +----------
board/samsung/smdk5250/smdk5250.c | 182 +-----
board/samsung/smdk5420/Makefile | 11 +
board/samsung/smdk5420/smdk5420.c | 159 +++++
board/samsung/smdk5420/smdk5420_spl.c | 52 ++
boards.cfg | 1 +
include/configs/arndale.h | 1 +
include/configs/exynos5-dt.h | 289 +++++++++
include/configs/exynos5250-dt.h | 283 +--------
include/configs/smdk5420.h | 56 ++
spl/Makefile | 7 +-
tools/Makefile | 3 +-
tools/mkexynosspl.c | 167 +++--
34 files changed, 5188 insertions(+), 1315 deletions(-)
create mode 100644 arch/arm/dts/exynos5.dtsi
create mode 100644 arch/arm/dts/exynos5420.dtsi
create mode 100644 arch/arm/include/asm/arch-exynos/board.h
create mode 100644 board/samsung/common/board.c
create mode 100644 board/samsung/dts/exynos5420-smdk5420.dts
create mode 100644 board/samsung/smdk5420/Makefile
create mode 100644 board/samsung/smdk5420/smdk5420.c
create mode 100644 board/samsung/smdk5420/smdk5420_spl.c
create mode 100644 include/configs/exynos5-dt.h
create mode 100644 include/configs/smdk5420.h
--
1.7.12.4
4
19

[U-Boot] [PATCH] spi/cadence: Adding Cadence SPI driver support for SOCFPGA
by Chin Liang See 19 Dec '13
by Chin Liang See 19 Dec '13
19 Dec '13
To add the Cadence SPI driver support for Altera SOCFPGA. It
required information such as clocks and timing from platform's
configuration header file within include/configs folder
Signed-off-by: Chin Liang See <clsee(a)altera.com>
Cc: Jagannadha Sutradharudu Teki <jaganna(a)xilinx.com>
---
drivers/spi/Makefile | 1 +
drivers/spi/cadence_qspi.c | 337 ++++++++++++++++
drivers/spi/cadence_qspi.h | 56 +++
drivers/spi/cadence_qspi_apb.c | 873 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 1267 insertions(+)
create mode 100644 drivers/spi/cadence_qspi.c
create mode 100644 drivers/spi/cadence_qspi.h
create mode 100644 drivers/spi/cadence_qspi_apb.c
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index ed4ecd7..838e6ca 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o
obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o
obj-$(CONFIG_BFIN_SPI) += bfin_spi.o
obj-$(CONFIG_BFIN_SPI6XX) += bfin_spi6xx.o
+obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o
obj-$(CONFIG_CF_SPI) += cf_spi.o
obj-$(CONFIG_CF_QSPI) += cf_qspi.o
obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o
diff --git a/drivers/spi/cadence_qspi.c b/drivers/spi/cadence_qspi.c
new file mode 100644
index 0000000..e2bf39f
--- /dev/null
+++ b/drivers/spi/cadence_qspi.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) Altera Corporation <www.altera.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <spi.h>
+#include "cadence_qspi.h"
+
+#define CQSPI_STIG_READ 0
+#define CQSPI_STIG_WRITE 1
+#define CQSPI_INDIRECT_READ 2
+#define CQSPI_INDIRECT_WRITE 3
+
+static int qspi_is_init;
+static unsigned int qspi_calibrated_hz;
+static unsigned int qspi_calibrated_cs;
+
+struct cadence_qspi_slave {
+ struct spi_slave slave;
+ unsigned int mode;
+ unsigned int max_hz;
+ void *regbase;
+ void *ahbbase;
+ size_t cmd_len;
+ u8 cmd_buf[32];
+ size_t data_len;
+};
+
+#define to_cadence_qspi_slave(s) \
+ container_of(s, struct cadence_qspi_slave, slave)
+
+void spi_set_speed(struct spi_slave *slave, uint hz)
+{
+ struct cadence_qspi_slave *cadence_qspi = to_cadence_qspi_slave(slave);
+ void *base = cadence_qspi->regbase;
+
+ cadence_qspi_apb_config_baudrate_div(base, CONFIG_CQSPI_REF_CLK, hz);
+
+ /* Reconfigure delay timing if speed is changed. */
+ cadence_qspi_apb_delay(base, CONFIG_CQSPI_REF_CLK, hz,
+ CONFIG_CQSPI_TSHSL_NS, CONFIG_CQSPI_TSD2D_NS,
+ CONFIG_CQSPI_TCHSH_NS, CONFIG_CQSPI_TSLCH_NS);
+ return;
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+ unsigned int max_hz, unsigned int mode)
+{
+ struct cadence_qspi_slave *cadence_qspi;
+
+ debug("%s: bus %d cs %d max_hz %dMHz mode %d\n", __func__,
+ bus, cs, max_hz/1000000, mode);
+
+ if (!spi_cs_is_valid(bus, cs))
+ return NULL;
+
+ cadence_qspi = malloc(sizeof(struct cadence_qspi_slave));
+ if (!cadence_qspi) {
+ printf("QSPI: Can't allocate struct cadence_qspi_slave. "
+ "Bus %d cs %d\n", bus, cs);
+ return NULL;
+ }
+
+ cadence_qspi->slave.bus = bus;
+ cadence_qspi->slave.cs = cs;
+ cadence_qspi->mode = mode;
+ cadence_qspi->max_hz = max_hz;
+ cadence_qspi->regbase = (void *)QSPI_BASE;
+ cadence_qspi->ahbbase = (void *)QSPI_AHB_BASE;
+
+ if (!qspi_is_init)
+ spi_init();
+
+ return &cadence_qspi->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+ struct cadence_qspi_slave *cadence_qspi = to_cadence_qspi_slave(slave);
+ free(cadence_qspi);
+ return;
+}
+
+void spi_init(void)
+{
+ cadence_qspi_apb_controller_init((void *)QSPI_BASE);
+ qspi_is_init = 1;
+ return;
+}
+
+/* calibration sequence to determine the read data capture delay register */
+int spi_calibration(struct spi_slave *slave)
+{
+ struct cadence_qspi_slave *cadence_qspi = to_cadence_qspi_slave(slave);
+ void *base = cadence_qspi->regbase;
+ u8 opcode_rdid = 0x9F;
+ unsigned int idcode = 0, temp = 0;
+ int err = 0, i, range_lo = -1, range_hi = -1;
+
+ /* start with slowest clock (1 MHz) */
+ spi_set_speed(slave, 1000000);
+
+ /* configure the read data capture delay register to 0 */
+ cadence_qspi_apb_readdata_capture(base, 1, 0);
+
+ /* Enable QSPI */
+ cadence_qspi_apb_controller_enable(base);
+
+ /* read the ID which will be our golden value */
+ err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid,
+ 3, (u8 *)&idcode);
+ if (err) {
+ puts("SF: Calibration failed (read)\n");
+ return err;
+ }
+
+ /* use back the intended clock and find low range */
+ spi_set_speed(slave, cadence_qspi->max_hz);
+ for (i = 0; i < CQSPI_READ_CAPTURE_MAX_DELAY; i++) {
+ /* Disable QSPI */
+ cadence_qspi_apb_controller_disable(base);
+
+ /* reconfigure the read data capture delay register */
+ cadence_qspi_apb_readdata_capture(base, 1, i);
+
+ /* Enable back QSPI */
+ cadence_qspi_apb_controller_enable(base);
+
+ /* issue a RDID to get the ID value */
+ err = cadence_qspi_apb_command_read(base, 1, &opcode_rdid,
+ 3, (u8 *)&temp);
+ if (err) {
+ puts("SF: Calibration failed (read)\n");
+ return err;
+ }
+
+ /* search for range lo */
+ if (range_lo == -1 && temp == idcode) {
+ range_lo = i;
+ continue;
+ }
+
+ /* search for range hi */
+ if (range_lo != -1 && temp != idcode) {
+ range_hi = i - 1;
+ break;
+ }
+ range_hi = i;
+ }
+
+ if (range_lo == -1) {
+ puts("SF: Calibration failed (low range)\n");
+ return err;
+ }
+
+ /* Disable QSPI for subsequent initialization */
+ cadence_qspi_apb_controller_disable(base);
+
+ /* configure the final value for read data capture delay register */
+ cadence_qspi_apb_readdata_capture(base, 1, (range_hi + range_lo) / 2);
+ printf("SF: Read data capture delay calibrated to %i (%i - %i)\n",
+ (range_hi + range_lo) / 2, range_lo, range_hi);
+
+ /* just to ensure we do once only when speed or chip select change */
+ qspi_calibrated_hz = cadence_qspi->max_hz;
+ qspi_calibrated_cs = slave->cs;
+ return 0;
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+ struct cadence_qspi_slave *cadence_qspi = to_cadence_qspi_slave(slave);
+ unsigned int clk_pol = (cadence_qspi->mode & SPI_CPOL) ? 1 : 0;
+ unsigned int clk_pha = (cadence_qspi->mode & SPI_CPHA) ? 1 : 0;
+ void *base = cadence_qspi->regbase;
+ int err = 0;
+
+ debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
+
+ /* Disable QSPI */
+ cadence_qspi_apb_controller_disable(base);
+
+ /* Set Chip select */
+ cadence_qspi_apb_chipselect(base, slave->cs, CONFIG_CQSPI_DECODER);
+
+ /* Set SPI mode */
+ cadence_qspi_apb_set_clk_mode(base, clk_pol, clk_pha);
+
+ /* Set clock speed */
+ spi_set_speed(slave, cadence_qspi->max_hz);
+
+ /* calibration required for different SCLK speed or chip select */
+ if (qspi_calibrated_hz != cadence_qspi->max_hz ||
+ qspi_calibrated_cs != slave->cs) {
+ err = spi_calibration(slave);
+ if (err)
+ return err;
+ }
+
+ /* Enable QSPI */
+ cadence_qspi_apb_controller_enable(base);
+
+ return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ return;
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out,
+ void *data_in, unsigned long flags)
+{
+ struct cadence_qspi_slave *cadence_qspi = to_cadence_qspi_slave(slave);
+ void *base = cadence_qspi->regbase;
+ void *ahbbase = cadence_qspi->ahbbase;
+ u8 *cmd_buf = cadence_qspi->cmd_buf;
+ size_t data_bytes;
+ int err = 0;
+ u32 mode = CQSPI_STIG_WRITE;
+
+ if (flags & SPI_XFER_BEGIN) {
+ /* copy command to local buffer */
+ cadence_qspi->cmd_len = bitlen / 8;
+ memcpy(cmd_buf, data_out, cadence_qspi->cmd_len);
+ }
+
+ if (flags == (SPI_XFER_BEGIN | SPI_XFER_END)) {
+ /* if start and end bit are set, the data bytes is 0. */
+ data_bytes = 0;
+ } else {
+ data_bytes = bitlen / 8;
+ }
+
+ if ((flags & SPI_XFER_END) || (flags == 0)) {
+ if (cadence_qspi->cmd_len == 0) {
+ printf("QSPI: Error, command is empty.\n");
+ return -1;
+ }
+
+ if (data_in && data_bytes) {
+ /* read */
+ /* Use STIG if no address. */
+ if (!CQSPI_IS_ADDR(cadence_qspi->cmd_len))
+ mode = CQSPI_STIG_READ;
+ else
+ mode = CQSPI_INDIRECT_READ;
+ } else if (data_out && !(flags & SPI_XFER_BEGIN)) {
+ /* write */
+ if (!CQSPI_IS_ADDR(cadence_qspi->cmd_len))
+ mode = CQSPI_STIG_WRITE;
+ else
+ mode = CQSPI_INDIRECT_WRITE;
+ }
+
+ switch (mode) {
+ case CQSPI_STIG_READ:
+ err = cadence_qspi_apb_command_read(
+ base, cadence_qspi->cmd_len, cmd_buf,
+ data_bytes, data_in);
+
+ break;
+ case CQSPI_STIG_WRITE:
+ err = cadence_qspi_apb_command_write(base,
+ cadence_qspi->cmd_len, cmd_buf,
+ data_bytes, data_out);
+ break;
+ case CQSPI_INDIRECT_READ:
+ err = cadence_qspi_apb_indirect_read_setup(
+ base, QSPI_AHB_BASE,
+ cadence_qspi->cmd_len, cmd_buf);
+ if (!err) {
+ err = cadence_qspi_apb_indirect_read_execute
+ (base, ahbbase, data_bytes, data_in);
+ }
+ break;
+ case CQSPI_INDIRECT_WRITE:
+ err = cadence_qspi_apb_indirect_write_setup
+ (base, QSPI_AHB_BASE,
+ cadence_qspi->cmd_len, cmd_buf);
+ if (!err) {
+ err = cadence_qspi_apb_indirect_write_execute
+ (base, ahbbase, data_bytes, data_out);
+ }
+ break;
+ default:
+ err = -1;
+ break;
+ }
+
+ if (flags & SPI_XFER_END) {
+ /* clear command buffer */
+ memset(cmd_buf, 0, sizeof(cadence_qspi->cmd_buf));
+ cadence_qspi->cmd_len = 0;
+ }
+ }
+ return err;
+}
+
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+#if (CONFIG_CQSPI_DECODER == 1)
+ if (((cs >= 0) && (cs < CQSPI_DECODER_MAX_CS)) && ((bus >= 0) &&
+ (bus < CQSPI_DECODER_MAX_CS))) {
+ return 1;
+ }
+#else
+ if (((cs >= 0) && (cs < CQSPI_NO_DECODER_MAX_CS)) &&
+ ((bus >= 0) && (bus < CQSPI_NO_DECODER_MAX_CS))) {
+ return 1;
+ }
+#endif
+ printf("QSPI: Invalid bus or cs. Bus %d cs %d\n", bus, cs);
+ return 0;
+}
+
+void spi_cs_activate(struct spi_slave *slave)
+{
+ return;
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+ return;
+}
+
+void spi_enter_xip(struct spi_slave *slave, char xip_dummy)
+{
+ struct cadence_qspi_slave *cadence_qspi = to_cadence_qspi_slave(slave);
+ void *base = cadence_qspi->regbase;
+ /* Enter XiP */
+ cadence_qspi_apb_enter_xip(base, xip_dummy);
+ return;
+}
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
new file mode 100644
index 0000000..5e63bc4
--- /dev/null
+++ b/drivers/spi/cadence_qspi.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) Altera Corporation <www.altera.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef __CADENCE_QSPI_H__
+#define __CADENCE_QSPI_H__
+
+#define CQSPI_WRITEL writel
+#define CQSPI_READL readl
+
+#define QSPI_BASE (CONFIG_CQSPI_BASE)
+#define QSPI_AHB_BASE (CONFIG_CQSPI_AHB_BASE)
+#define CQSPI_IS_ADDR(cmd_len) (cmd_len > 1 ? 1 : 0)
+
+#define CQSPI_NO_DECODER_MAX_CS (4)
+#define CQSPI_DECODER_MAX_CS (16)
+#define CQSPI_READ_CAPTURE_MAX_DELAY (16)
+
+/* Functions call declaration */
+void cadence_qspi_apb_controller_init(void *reg_base_addr);
+void cadence_qspi_apb_controller_enable(void *reg_base_addr);
+void cadence_qspi_apb_controller_disable(void *reg_base_addr);
+
+int cadence_qspi_apb_command_read(void *reg_base_addr,
+ unsigned int cmdlen, const u8 *cmdbuf, unsigned int rxlen, u8 *rxbuf);
+int cadence_qspi_apb_command_write(void *reg_base_addr,
+ unsigned int cmdlen, const u8 *cmdbuf,
+ unsigned int txlen, const u8 *txbuf);
+
+int cadence_qspi_apb_indirect_read_setup(void *reg_base,
+ unsigned int ahb_phy_addr, unsigned int cmdlen, const u8 *cmdbuf);
+int cadence_qspi_apb_indirect_read_execute(void *reg_base_addr,
+ void *ahb_base_addr, unsigned int rxlen, u8 *rxbuf);
+
+int cadence_qspi_apb_indirect_write_setup(void *reg_base,
+ unsigned int ahb_phy_addr, unsigned int cmdlen, const u8 *cmdbuf);
+int cadence_qspi_apb_indirect_write_execute(void *reg_base_addr,
+ void *ahb_base_addr, unsigned int txlen, const u8 *txbuf);
+
+void cadence_qspi_apb_chipselect(void *reg_base,
+ unsigned int chip_select, unsigned int decoder_enable);
+void cadence_qspi_apb_set_clk_mode(void *reg_base_addr,
+ unsigned int clk_pol, unsigned int clk_pha);
+void cadence_qspi_apb_config_baudrate_div(void *reg_base,
+ unsigned int ref_clk_hz, unsigned int sclk_hz);
+void cadence_qspi_apb_delay(void *reg_base,
+ unsigned int ref_clk, unsigned int sclk_hz,
+ unsigned int tshsl_ns, unsigned int tsd2d_ns,
+ unsigned int tchsh_ns, unsigned int tslch_ns);
+void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy);
+void cadence_qspi_apb_readdata_capture(void *reg_base,
+ unsigned int bypass, unsigned int delay);
+
+#endif /* __CADENCE_QSPI_H__ */
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
new file mode 100644
index 0000000..7798acb
--- /dev/null
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) Altera Corporation <www.altera.com>
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/errno.h>
+#include "cadence_qspi.h"
+
+#define CQSPI_REG_POLL_US (1)
+#define CQSPI_REG_RETRY (10000)
+#define CQSPI_POLL_IDLE_RETRY (3)
+
+#define CQSPI_FIFO_WIDTH (4)
+
+/* Controller sram size in word */
+#define CQSPI_REG_SRAM_SIZE_WORD (128)
+#define CQSPI_REG_SRAM_RESV_WORDS (2)
+#define CQSPI_REG_SRAM_PARTITION_WR (1)
+#define CQSPI_REG_SRAM_PARTITION_RD \
+ (CQSPI_REG_SRAM_SIZE_WORD - CQSPI_REG_SRAM_RESV_WORDS)
+#define CQSPI_REG_SRAM_THRESHOLD_WORDS (50)
+
+/* Transfer mode */
+#define CQSPI_INST_TYPE_SINGLE (0)
+#define CQSPI_INST_TYPE_DUAL (1)
+#define CQSPI_INST_TYPE_QUAD (2)
+
+#define CQSPI_STIG_DATA_LEN_MAX (8)
+#define CQSPI_INDIRECTTRIGGER_ADDR_MASK (0xFFFFF)
+
+#define CQSPI_DUMMY_CLKS_PER_BYTE (8)
+#define CQSPI_DUMMY_BYTES_MAX (4)
+
+
+#define CQSPI_REG_SRAM_FILL_THRESHOLD \
+ ((CQSPI_REG_SRAM_SIZE_WORD / 2) * CQSPI_FIFO_WIDTH)
+/****************************************************************************
+ * Controller's configuration and status register (offset from QSPI_BASE)
+ ****************************************************************************/
+#define CQSPI_REG_CONFIG 0x00
+#define CQSPI_REG_CONFIG_CLK_POL_LSB 1
+#define CQSPI_REG_CONFIG_CLK_PHA_LSB 2
+#define CQSPI_REG_CONFIG_ENABLE_MASK (1 << 0)
+#define CQSPI_REG_CONFIG_DIRECT_MASK (1 << 7)
+#define CQSPI_REG_CONFIG_DECODE_MASK (1 << 9)
+#define CQSPI_REG_CONFIG_XIP_IMM_MASK (1 << 18)
+#define CQSPI_REG_CONFIG_CHIPSELECT_LSB 10
+#define CQSPI_REG_CONFIG_BAUD_LSB 19
+#define CQSPI_REG_CONFIG_IDLE_LSB 31
+#define CQSPI_REG_CONFIG_CHIPSELECT_MASK 0xF
+#define CQSPI_REG_CONFIG_BAUD_MASK 0xF
+
+#define CQSPI_REG_RD_INSTR 0x04
+#define CQSPI_REG_RD_INSTR_OPCODE_LSB 0
+#define CQSPI_REG_RD_INSTR_TYPE_INSTR_LSB 8
+#define CQSPI_REG_RD_INSTR_TYPE_ADDR_LSB 12
+#define CQSPI_REG_RD_INSTR_TYPE_DATA_LSB 16
+#define CQSPI_REG_RD_INSTR_MODE_EN_LSB 20
+#define CQSPI_REG_RD_INSTR_DUMMY_LSB 24
+#define CQSPI_REG_RD_INSTR_TYPE_INSTR_MASK 0x3
+#define CQSPI_REG_RD_INSTR_TYPE_ADDR_MASK 0x3
+#define CQSPI_REG_RD_INSTR_TYPE_DATA_MASK 0x3
+#define CQSPI_REG_RD_INSTR_DUMMY_MASK 0x1F
+
+#define CQSPI_REG_WR_INSTR 0x08
+#define CQSPI_REG_WR_INSTR_OPCODE_LSB 0
+
+#define CQSPI_REG_DELAY 0x0C
+#define CQSPI_REG_DELAY_TSLCH_LSB 0
+#define CQSPI_REG_DELAY_TCHSH_LSB 8
+#define CQSPI_REG_DELAY_TSD2D_LSB 16
+#define CQSPI_REG_DELAY_TSHSL_LSB 24
+#define CQSPI_REG_DELAY_TSLCH_MASK 0xFF
+#define CQSPI_REG_DELAY_TCHSH_MASK 0xFF
+#define CQSPI_REG_DELAY_TSD2D_MASK 0xFF
+#define CQSPI_REG_DELAY_TSHSL_MASK 0xFF
+
+#define CQSPI_READLCAPTURE 0x10
+#define CQSPI_READLCAPTURE_BYPASS_LSB 0
+#define CQSPI_READLCAPTURE_DELAY_LSB 1
+#define CQSPI_READLCAPTURE_DELAY_MASK 0xF
+
+#define CQSPI_REG_SIZE 0x14
+#define CQSPI_REG_SIZE_ADDRESS_LSB 0
+#define CQSPI_REG_SIZE_PAGE_LSB 4
+#define CQSPI_REG_SIZE_BLOCK_LSB 16
+#define CQSPI_REG_SIZE_ADDRESS_MASK 0xF
+#define CQSPI_REG_SIZE_PAGE_MASK 0xFFF
+#define CQSPI_REG_SIZE_BLOCK_MASK 0x3F
+
+#define CQSPI_REG_SRAMPARTITION 0x18
+#define CQSPI_REG_INDIRECTTRIGGER 0x1C
+
+#define CQSPI_REG_REMAP 0x24
+#define CQSPI_REG_MODE_BIT 0x28
+
+#define CQSPI_REG_SDRAMLEVEL 0x2C
+#define CQSPI_REG_SDRAMLEVEL_RD_LSB 0
+#define CQSPI_REG_SDRAMLEVEL_WR_LSB 16
+#define CQSPI_REG_SDRAMLEVEL_RD_MASK 0xFFFF
+#define CQSPI_REG_SDRAMLEVEL_WR_MASK 0xFFFF
+
+#define CQSPI_REG_IRQSTATUS 0x40
+#define CQSPI_REG_IRQMASK 0x44
+
+#define CQSPI_REG_INDIRECTRD 0x60
+#define CQSPI_REG_INDIRECTRD_START_MASK (1 << 0)
+#define CQSPI_REG_INDIRECTRD_CANCEL_MASK (1 << 1)
+#define CQSPI_REG_INDIRECTRD_INPROGRESS_MASK (1 << 2)
+#define CQSPI_REG_INDIRECTRD_DONE_MASK (1 << 5)
+
+#define CQSPI_REG_INDIRECTRDWATERMARK 0x64
+#define CQSPI_REG_INDIRECTRDSTARTADDR 0x68
+#define CQSPI_REG_INDIRECTRDBYTES 0x6C
+
+#define CQSPI_REG_CMDCTRL 0x90
+#define CQSPI_REG_CMDCTRL_EXECUTE_MASK (1 << 0)
+#define CQSPI_REG_CMDCTRL_INPROGRESS_MASK (1 << 1)
+#define CQSPI_REG_CMDCTRL_DUMMY_LSB 7
+#define CQSPI_REG_CMDCTRL_WR_BYTES_LSB 12
+#define CQSPI_REG_CMDCTRL_WR_EN_LSB 15
+#define CQSPI_REG_CMDCTRL_ADD_BYTES_LSB 16
+#define CQSPI_REG_CMDCTRL_ADDR_EN_LSB 19
+#define CQSPI_REG_CMDCTRL_RD_BYTES_LSB 20
+#define CQSPI_REG_CMDCTRL_RD_EN_LSB 23
+#define CQSPI_REG_CMDCTRL_OPCODE_LSB 24
+#define CQSPI_REG_CMDCTRL_DUMMY_MASK 0x1F
+#define CQSPI_REG_CMDCTRL_WR_BYTES_MASK 0x7
+#define CQSPI_REG_CMDCTRL_ADD_BYTES_MASK 0x3
+#define CQSPI_REG_CMDCTRL_RD_BYTES_MASK 0x7
+#define CQSPI_REG_CMDCTRL_OPCODE_MASK 0xFF
+
+#define CQSPI_REG_INDIRECTWR 0x70
+#define CQSPI_REG_INDIRECTWR_START_MASK (1 << 0)
+#define CQSPI_REG_INDIRECTWR_CANCEL_MASK (1 << 1)
+#define CQSPI_REG_INDIRECTWR_INPROGRESS_MASK (1 << 2)
+#define CQSPI_REG_INDIRECTWR_DONE_MASK (1 << 5)
+
+#define CQSPI_REG_INDIRECTWRWATERMARK 0x74
+#define CQSPI_REG_INDIRECTWRSTARTADDR 0x78
+#define CQSPI_REG_INDIRECTWRBYTES 0x7C
+
+#define CQSPI_REG_CMDADDRESS 0x94
+#define CQSPI_REG_CMDREADDATALOWER 0xA0
+#define CQSPI_REG_CMDREADDATAUPPER 0xA4
+#define CQSPI_REG_CMDWRITEDATALOWER 0xA8
+#define CQSPI_REG_CMDWRITEDATAUPPER 0xAC
+
+#define CQSPI_REG_IS_IDLE(base) \
+ ((CQSPI_READL(base + CQSPI_REG_CONFIG) >> \
+ CQSPI_REG_CONFIG_IDLE_LSB) & 0x1)
+
+#define CQSPI_CAL_DELAY(tdelay_ns, tref_ns, tsclk_ns) \
+ ((((tdelay_ns) - (tsclk_ns)) / (tref_ns)))
+
+#define CQSPI_GET_RD_SRAM_LEVEL(reg_basse) \
+ (((CQSPI_READL(reg_base + CQSPI_REG_SDRAMLEVEL)) >> \
+ CQSPI_REG_SDRAMLEVEL_RD_LSB) & CQSPI_REG_SDRAMLEVEL_RD_MASK)
+
+#define CQSPI_GET_WR_SRAM_LEVEL(reg_basse) \
+ (((CQSPI_READL(reg_base + CQSPI_REG_SDRAMLEVEL)) >> \
+ CQSPI_REG_SDRAMLEVEL_WR_LSB) & CQSPI_REG_SDRAMLEVEL_WR_MASK)
+
+static unsigned int cadence_qspi_apb_cmd2addr(const unsigned char *addr_buf,
+ unsigned int addr_width)
+{
+ unsigned int addr;
+
+ addr = (addr_buf[0] << 16) | (addr_buf[1] << 8) | addr_buf[2];
+
+ if (addr_width == 4)
+ addr = (addr << 8) | addr_buf[3];
+
+ return addr;
+}
+
+static void cadence_qspi_apb_read_fifo_data(void *dest,
+ const void *src_ahb_addr, unsigned int bytes)
+{
+ unsigned int temp;
+ int remaining = bytes;
+ unsigned int *dest_ptr = (unsigned int *)dest;
+ unsigned int *src_ptr = (unsigned int *)src_ahb_addr;
+
+ while (remaining > 0) {
+ if (remaining >= CQSPI_FIFO_WIDTH) {
+ *dest_ptr = CQSPI_READL(src_ptr);
+ remaining -= CQSPI_FIFO_WIDTH;
+ } else {
+ /* dangling bytes */
+ temp = CQSPI_READL(src_ptr);
+ memcpy(dest_ptr, &temp, remaining);
+ break;
+ }
+ dest_ptr++;
+ }
+
+ return;
+}
+
+static void cadence_qspi_apb_write_fifo_data(const void *dest_ahb_addr,
+ const void *src, unsigned int bytes)
+{
+ unsigned int temp;
+ int remaining = bytes;
+ unsigned int *dest_ptr = (unsigned int *)dest_ahb_addr;
+ unsigned int *src_ptr = (unsigned int *)src;
+
+ while (remaining > 0) {
+ if (remaining >= CQSPI_FIFO_WIDTH) {
+ CQSPI_WRITEL(*src_ptr, dest_ptr);
+ remaining -= sizeof(unsigned int);
+ } else {
+ /* dangling bytes */
+ memcpy(&temp, src_ptr, remaining);
+ CQSPI_WRITEL(temp, dest_ptr);
+ break;
+ }
+ src_ptr++;
+ }
+
+ return;
+}
+
+/* Read from SRAM FIFO with polling SRAM fill level. */
+static int qspi_read_sram_fifo_poll(const void *reg_base, void *dest_addr,
+ const void *src_addr, unsigned int num_bytes)
+{
+ unsigned int remaining = num_bytes;
+ unsigned int retry;
+ unsigned int sram_level = 0;
+ unsigned char *dest = (unsigned char *)dest_addr;
+
+ while (remaining > 0) {
+ retry = CQSPI_REG_RETRY;
+ while (retry--) {
+ sram_level = CQSPI_GET_RD_SRAM_LEVEL(reg_base);
+ if (sram_level)
+ break;
+ udelay(1);
+ }
+
+ if (!retry) {
+ printf("QSPI: No receive data after polling for %d "
+ "times\n", CQSPI_REG_RETRY);
+ return -1;
+ }
+
+ sram_level *= CQSPI_FIFO_WIDTH;
+ sram_level = sram_level > remaining ? remaining : sram_level;
+
+ /* Read data from FIFO. */
+ cadence_qspi_apb_read_fifo_data(dest, src_addr, sram_level);
+ dest += sram_level;
+ remaining -= sram_level;
+ udelay(1);
+ }
+ return 0;
+}
+
+
+/* Write to SRAM FIFO with polling SRAM fill level. */
+static int qpsi_write_sram_fifo_push(const void *reg_base, void *dest_addr,
+ const void *src_addr, unsigned int num_bytes)
+{
+ unsigned int retry = CQSPI_REG_RETRY;
+ unsigned int sram_level;
+ unsigned int wr_bytes;
+ unsigned char *src = (unsigned char *)src_addr;
+ int remaining = num_bytes;
+ unsigned int page_size = CONFIG_CQSPI_PAGE_SIZE;
+ unsigned int sram_threshold_words = CQSPI_REG_SRAM_THRESHOLD_WORDS;
+
+ while (remaining > 0) {
+ retry = CQSPI_REG_RETRY;
+ while (retry--) {
+ sram_level = CQSPI_GET_WR_SRAM_LEVEL(reg_base);
+ if (sram_level <= sram_threshold_words)
+ break;
+ }
+ if (!retry) {
+ printf("QSPI: SRAM fill level (0x%08x) "
+ "not hit lower expected level (0x%08x)",
+ sram_level, sram_threshold_words);
+ return -1;
+ }
+ /* Write a page or remaining bytes. */
+ wr_bytes = (remaining > page_size) ?
+ page_size : remaining;
+
+ cadence_qspi_apb_write_fifo_data(dest_addr, src, wr_bytes);
+ src += wr_bytes;
+ remaining -= wr_bytes;
+ }
+
+ return 0;
+}
+
+void cadence_qspi_apb_controller_enable(void *reg_base)
+{
+ unsigned int reg;
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG);
+ reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG);
+ return;
+}
+
+void cadence_qspi_apb_controller_disable(void *reg_base)
+{
+ unsigned int reg;
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG);
+ return;
+}
+
+/* Return 1 if idle, otherwise return 0 (busy). */
+static unsigned int cadence_qspi_wait_idle(void *reg_base)
+{
+ unsigned int start, count = 0;
+ /* timeout in unit of ms */
+ unsigned int timeout = 5000;
+
+ start = get_timer(0);
+ for ( ; get_timer(start) < timeout ; ) {
+ if (CQSPI_REG_IS_IDLE(reg_base))
+ count++;
+ else
+ count = 0;
+ /*
+ * Ensure the QSPI controller is in true idle state after
+ * reading back the same idle status consecutively
+ */
+ if (count >= CQSPI_POLL_IDLE_RETRY)
+ return 1;
+ }
+
+ /* Timeout, still in busy mode. */
+ printf("QSPI: QSPI is still busy after poll for %d times.\n",
+ CQSPI_REG_RETRY);
+ return 0;
+}
+
+void cadence_qspi_apb_readdata_capture(void *reg_base,
+ unsigned int bypass, unsigned int delay)
+{
+ unsigned int reg;
+ cadence_qspi_apb_controller_disable(reg_base);
+
+ reg = CQSPI_READL(reg_base + CQSPI_READLCAPTURE);
+
+ if (bypass)
+ reg |= (1 << CQSPI_READLCAPTURE_BYPASS_LSB);
+ else
+ reg &= ~(1 << CQSPI_READLCAPTURE_BYPASS_LSB);
+
+ reg &= ~(CQSPI_READLCAPTURE_DELAY_MASK
+ << CQSPI_READLCAPTURE_DELAY_LSB);
+
+ reg |= ((delay & CQSPI_READLCAPTURE_DELAY_MASK)
+ << CQSPI_READLCAPTURE_DELAY_LSB);
+
+ CQSPI_WRITEL(reg, reg_base + CQSPI_READLCAPTURE);
+
+ cadence_qspi_apb_controller_enable(reg_base);
+ return;
+}
+
+void cadence_qspi_apb_config_baudrate_div(void *reg_base,
+ unsigned int ref_clk_hz, unsigned int sclk_hz)
+{
+ unsigned int reg;
+ unsigned int div;
+
+ cadence_qspi_apb_controller_disable(reg_base);
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~(CQSPI_REG_CONFIG_BAUD_MASK << CQSPI_REG_CONFIG_BAUD_LSB);
+
+ div = ref_clk_hz / sclk_hz;
+
+ if (div > 32)
+ div = 32;
+
+ /* Check if even number. */
+ if ((div & 1))
+ div = (div / 2);
+ else
+ div = (div / 2) - 1;
+
+ debug("%s: ref_clk %dHz sclk %dHz Div 0x%x\n", __func__,
+ ref_clk_hz, sclk_hz, div);
+
+ div = (div & CQSPI_REG_CONFIG_BAUD_MASK) << CQSPI_REG_CONFIG_BAUD_LSB;
+ reg |= div;
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG);
+
+ cadence_qspi_apb_controller_enable(reg_base);
+ return;
+}
+
+void cadence_qspi_apb_set_clk_mode(void *reg_base,
+ unsigned int clk_pol, unsigned int clk_pha)
+{
+ unsigned int reg;
+
+ cadence_qspi_apb_controller_disable(reg_base);
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG);
+ reg &= ~(1 <<
+ (CQSPI_REG_CONFIG_CLK_POL_LSB | CQSPI_REG_CONFIG_CLK_PHA_LSB));
+
+ reg |= ((clk_pol & 0x1) << CQSPI_REG_CONFIG_CLK_POL_LSB);
+ reg |= ((clk_pha & 0x1) << CQSPI_REG_CONFIG_CLK_PHA_LSB);
+
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG);
+
+ cadence_qspi_apb_controller_enable(reg_base);
+ return;
+}
+
+void cadence_qspi_apb_chipselect(void *reg_base,
+ unsigned int chip_select, unsigned int decoder_enable)
+{
+ unsigned int reg;
+
+ cadence_qspi_apb_controller_disable(reg_base);
+
+ debug("%s : chipselect %d decode %d\n", __func__, chip_select,
+ decoder_enable);
+
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG);
+ /* docoder */
+ if (decoder_enable)
+ reg |= CQSPI_REG_CONFIG_DECODE_MASK;
+ else {
+ reg &= ~CQSPI_REG_CONFIG_DECODE_MASK;
+ /* Convert CS if without decoder.
+ * CS0 to 4b'1110
+ * CS1 to 4b'1101
+ * CS2 to 4b'1011
+ * CS3 to 4b'0111
+ */
+ chip_select = 0xF & ~(1 << chip_select);
+ }
+
+ reg &= ~(CQSPI_REG_CONFIG_CHIPSELECT_MASK
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB);
+ reg |= (chip_select & CQSPI_REG_CONFIG_CHIPSELECT_MASK)
+ << CQSPI_REG_CONFIG_CHIPSELECT_LSB;
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG);
+
+ cadence_qspi_apb_controller_enable(reg_base);
+ return;
+}
+
+void cadence_qspi_apb_delay(void *reg_base,
+ unsigned int ref_clk, unsigned int sclk_hz,
+ unsigned int tshsl_ns, unsigned int tsd2d_ns,
+ unsigned int tchsh_ns, unsigned int tslch_ns)
+{
+ unsigned int ref_clk_ns;
+ unsigned int sclk_ns;
+ unsigned int tshsl, tchsh, tslch, tsd2d;
+ unsigned int reg;
+
+ cadence_qspi_apb_controller_disable(reg_base);
+
+ /* Convert to ns. */
+ ref_clk_ns = (1000000000) / ref_clk;
+
+ /* Convert to ns. */
+ sclk_ns = (1000000000) / sclk_hz;
+
+ /* Plus 1 to round up 1 clock cycle. */
+ tshsl = CQSPI_CAL_DELAY(tshsl_ns, ref_clk_ns, sclk_ns) + 1;
+ tchsh = CQSPI_CAL_DELAY(tchsh_ns, ref_clk_ns, sclk_ns) + 1;
+ tslch = CQSPI_CAL_DELAY(tslch_ns, ref_clk_ns, sclk_ns) + 1;
+ tsd2d = CQSPI_CAL_DELAY(tsd2d_ns, ref_clk_ns, sclk_ns) + 1;
+
+ reg = ((tshsl & CQSPI_REG_DELAY_TSHSL_MASK)
+ << CQSPI_REG_DELAY_TSHSL_LSB);
+ reg |= ((tchsh & CQSPI_REG_DELAY_TCHSH_MASK)
+ << CQSPI_REG_DELAY_TCHSH_LSB);
+ reg |= ((tslch & CQSPI_REG_DELAY_TSLCH_MASK)
+ << CQSPI_REG_DELAY_TSLCH_LSB);
+ reg |= ((tsd2d & CQSPI_REG_DELAY_TSD2D_MASK)
+ << CQSPI_REG_DELAY_TSD2D_LSB);
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_DELAY);
+
+ cadence_qspi_apb_controller_enable(reg_base);
+ return;
+}
+
+void cadence_qspi_apb_controller_init(void *reg_base)
+{
+ unsigned reg;
+
+ cadence_qspi_apb_controller_disable(reg_base);
+
+ /* Configure the device size and address bytes */
+ reg = CQSPI_READL(reg_base + CQSPI_REG_SIZE);
+ /* Clear the previous value */
+ reg &= ~(CQSPI_REG_SIZE_PAGE_MASK << CQSPI_REG_SIZE_PAGE_LSB);
+ reg &= ~(CQSPI_REG_SIZE_BLOCK_MASK << CQSPI_REG_SIZE_BLOCK_LSB);
+ reg |= (CONFIG_CQSPI_PAGE_SIZE << CQSPI_REG_SIZE_PAGE_LSB);
+ reg |= (CONFIG_CQSPI_BLOCK_SIZE << CQSPI_REG_SIZE_BLOCK_LSB);
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_SIZE);
+
+ /* Configure the remap address register, no remap */
+ CQSPI_WRITEL(0, reg_base + CQSPI_REG_REMAP);
+
+ /* Disable all interrupts */
+ CQSPI_WRITEL(0, reg_base + CQSPI_REG_IRQMASK);
+
+ cadence_qspi_apb_controller_enable(reg_base);
+ return;
+}
+
+static int cadence_qspi_apb_exec_flash_cmd(void *reg_base,
+ unsigned int reg)
+{
+ unsigned int retry = CQSPI_REG_RETRY;
+
+ /* Write the CMDCTRL without start execution. */
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CMDCTRL);
+ /* Start execute */
+ reg |= CQSPI_REG_CMDCTRL_EXECUTE_MASK;
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CMDCTRL);
+
+ while (retry--) {
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CMDCTRL);
+ if ((reg & CQSPI_REG_CMDCTRL_INPROGRESS_MASK) == 0)
+ break;
+ udelay(1);
+ }
+
+ if (!retry) {
+ printf("QSPI: flash command execution timeout\n");
+ return -EIO;
+ }
+
+ /* Polling QSPI idle status. */
+ if (!cadence_qspi_wait_idle(reg_base))
+ return -EIO;
+
+ return 0;
+}
+
+/* For command RDID, RDSR. */
+int cadence_qspi_apb_command_read(void *reg_base,
+ unsigned int cmdlen, const u8 *cmdbuf, unsigned int rxlen,
+ u8 *rxbuf)
+{
+ unsigned int reg;
+ unsigned int read_len;
+ int status;
+
+ if (!cmdlen || rxlen > CQSPI_STIG_DATA_LEN_MAX || rxbuf == NULL) {
+ printf("QSPI: Invalid input arguments cmdlen %d "
+ "rxlen %d\n", cmdlen, rxlen);
+ return -EINVAL;
+ }
+
+ reg = cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB;
+
+ reg |= (0x1 << CQSPI_REG_CMDCTRL_RD_EN_LSB);
+
+ /* 0 means 1 byte. */
+ reg |= (((rxlen - 1) & CQSPI_REG_CMDCTRL_RD_BYTES_MASK)
+ << CQSPI_REG_CMDCTRL_RD_BYTES_LSB);
+ status = cadence_qspi_apb_exec_flash_cmd(reg_base, reg);
+ if (status != 0)
+ return status;
+
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CMDREADDATALOWER);
+
+ /* Put the read value into rx_buf */
+ read_len = (rxlen > 4) ? 4 : rxlen;
+ memcpy(rxbuf, ®, read_len);
+ rxbuf += read_len;
+
+ if (rxlen > 4) {
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CMDREADDATAUPPER);
+
+ read_len = rxlen - read_len;
+ memcpy(rxbuf, ®, read_len);
+ }
+ return 0;
+}
+
+/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */
+int cadence_qspi_apb_command_write(void *reg_base, unsigned int cmdlen,
+ const u8 *cmdbuf, unsigned int txlen, const u8 *txbuf)
+{
+ unsigned int reg = 0;
+ unsigned int addr_value;
+ unsigned int wr_data;
+ unsigned int wr_len;
+
+ if (!cmdlen || cmdlen > 5 || txlen > 8 || cmdbuf == NULL) {
+ printf("QSPI: Invalid input arguments cmdlen %d txlen %d\n",
+ cmdlen, txlen);
+ return -EINVAL;
+ }
+
+ reg |= cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB;
+
+ if (cmdlen == 4 || cmdlen == 5) {
+ /* Command with address */
+ reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
+ /* Number of bytes to write. */
+ reg |= ((cmdlen - 2) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK)
+ << CQSPI_REG_CMDCTRL_ADD_BYTES_LSB;
+ /* Get address */
+ addr_value = cadence_qspi_apb_cmd2addr(&cmdbuf[1],
+ cmdlen >= 5 ? 4 : 3);
+
+ CQSPI_WRITEL(addr_value, reg_base + CQSPI_REG_CMDADDRESS);
+ }
+
+ if (txlen) {
+ /* writing data = yes */
+ reg |= (0x1 << CQSPI_REG_CMDCTRL_WR_EN_LSB);
+ reg |= ((txlen - 1) & CQSPI_REG_CMDCTRL_WR_BYTES_MASK)
+ << CQSPI_REG_CMDCTRL_WR_BYTES_LSB;
+
+ wr_len = txlen > 4 ? 4 : txlen;
+ memcpy(&wr_data, txbuf, wr_len);
+ CQSPI_WRITEL(wr_data, reg_base +
+ CQSPI_REG_CMDWRITEDATALOWER);
+
+ if (txlen > 4) {
+ txbuf += wr_len;
+ wr_len = txlen - wr_len;
+ memcpy(&wr_data, txbuf, wr_len);
+ CQSPI_WRITEL(wr_data, reg_base +
+ CQSPI_REG_CMDWRITEDATAUPPER);
+ }
+ }
+
+ /* Execute the command */
+ return cadence_qspi_apb_exec_flash_cmd(reg_base, reg);
+}
+
+/* Opcode + Address (3/4 bytes) + dummy bytes (0-4 bytes) */
+int cadence_qspi_apb_indirect_read_setup(void *reg_base,
+ unsigned int ahb_phy_addr, unsigned int cmdlen, const u8 *cmdbuf)
+{
+ unsigned int reg;
+ unsigned int rd_reg;
+ unsigned int addr_value;
+ unsigned int dummy_clk;
+ unsigned int dummy_bytes;
+ unsigned int addr_bytes;
+
+ /*
+ * Identify addr_byte. All NOR flash device drivers are using fast read
+ * which always expecting 1 dummy byte, 1 cmd byte and 3/4 addr byte.
+ * With that, the length is in value of 5 or 6. Only FRAM chip from
+ * ramtron using normal read (which won't need dummy byte).
+ * Unlikely NOR flash using normal read due to performance issue.
+ */
+ if (cmdlen >= 5)
+ /* to cater fast read where cmd + addr + dummy */
+ addr_bytes = cmdlen - 2;
+ else
+ /* for normal read (only ramtron as of now) */
+ addr_bytes = cmdlen - 1;
+
+ /* Setup the indirect trigger address */
+ CQSPI_WRITEL((ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
+ reg_base + CQSPI_REG_INDIRECTTRIGGER);
+
+ /* Configure SRAM partition for read. */
+ CQSPI_WRITEL(CQSPI_REG_SRAM_PARTITION_RD, reg_base +
+ CQSPI_REG_SRAMPARTITION);
+
+ /* Configure the opcode */
+ rd_reg = cmdbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB;
+
+#if (CONFIG_SPI_FLASH_QUAD == 1)
+ /* Instruction and address at DQ0, data at DQ0-3. */
+ rd_reg |= CQSPI_INST_TYPE_QUAD << CQSPI_REG_RD_INSTR_TYPE_DATA_LSB;
+#endif
+
+ /* Get address */
+ addr_value = cadence_qspi_apb_cmd2addr(&cmdbuf[1], addr_bytes);
+ CQSPI_WRITEL(addr_value, reg_base + CQSPI_REG_INDIRECTRDSTARTADDR);
+
+ /* The remaining lenght is dummy bytes. */
+ dummy_bytes = cmdlen - addr_bytes - 1;
+ if (dummy_bytes) {
+
+ if (dummy_bytes > CQSPI_DUMMY_BYTES_MAX)
+ dummy_bytes = CQSPI_DUMMY_BYTES_MAX;
+
+ rd_reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB);
+#if defined(CONFIG_SPL_SPI_XIP) && defined(CONFIG_SPL_BUILD)
+ CQSPI_WRITEL(0x0, reg_base + CQSPI_REG_MODE_BIT);
+#else
+ CQSPI_WRITEL(0xFF, reg_base + CQSPI_REG_MODE_BIT);
+#endif
+
+ /* Convert to clock cycles. */
+ dummy_clk = dummy_bytes * CQSPI_DUMMY_CLKS_PER_BYTE;
+ /* Need to minus the mode byte (8 clocks). */
+ dummy_clk -= CQSPI_DUMMY_CLKS_PER_BYTE;
+
+ if (dummy_clk)
+ rd_reg |= (dummy_clk & CQSPI_REG_RD_INSTR_DUMMY_MASK)
+ << CQSPI_REG_RD_INSTR_DUMMY_LSB;
+ }
+
+ CQSPI_WRITEL(rd_reg, reg_base + CQSPI_REG_RD_INSTR);
+
+ /* set device size */
+ reg = CQSPI_READL(reg_base + CQSPI_REG_SIZE);
+ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
+ reg |= (addr_bytes - 1);
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_SIZE);
+ return 0;
+}
+
+int cadence_qspi_apb_indirect_read_execute(void *reg_base,
+ void *ahb_base_addr, unsigned int rxlen, u8 *rxbuf)
+{
+ unsigned int reg;
+
+ CQSPI_WRITEL(rxlen, reg_base + CQSPI_REG_INDIRECTRDBYTES);
+
+ /* Start the indirect read transfer */
+ CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_START_MASK,
+ reg_base + CQSPI_REG_INDIRECTRD);
+
+ if (qspi_read_sram_fifo_poll(reg_base, (void *)rxbuf,
+ (const void *)ahb_base_addr, rxlen)) {
+ goto failrd;
+ }
+
+ /* Check flash indirect controller */
+ reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTRD);
+ if (!(reg & CQSPI_REG_INDIRECTRD_DONE_MASK)) {
+ reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTRD);
+ printf("QSPI: indirect completion status "
+ "error with reg 0x%08x\n", reg);
+ goto failrd;
+ }
+
+ /* Clear indirect completion status */
+ CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_DONE_MASK,
+ reg_base + CQSPI_REG_INDIRECTRD);
+ return 0;
+
+failrd:
+ /* Cancel the indirect read */
+ CQSPI_WRITEL(CQSPI_REG_INDIRECTRD_CANCEL_MASK,
+ reg_base + CQSPI_REG_INDIRECTRD);
+ return -1;
+}
+
+/* Opcode + Address (3/4 bytes) */
+int cadence_qspi_apb_indirect_write_setup(void *reg_base,
+ unsigned int ahb_phy_addr, unsigned int cmdlen, const u8 *cmdbuf)
+{
+ unsigned int reg;
+ unsigned int addr_bytes = cmdlen > 4 ? 4 : 3;
+
+ if (cmdlen < 4 || cmdbuf == NULL) {
+ printf("QSPI: iInvalid input argument, len %d cmdbuf 0x%08x\n",
+ cmdlen, (unsigned int)cmdbuf);
+ return -EINVAL;
+ }
+ /* Setup the indirect trigger address */
+ CQSPI_WRITEL((ahb_phy_addr & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
+ reg_base + CQSPI_REG_INDIRECTTRIGGER);
+
+ CQSPI_WRITEL(CQSPI_REG_SRAM_PARTITION_WR,
+ reg_base + CQSPI_REG_SRAMPARTITION);
+
+ /* Configure the opcode */
+ reg = cmdbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB;
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_WR_INSTR);
+
+ /* Setup write address. */
+ reg = cadence_qspi_apb_cmd2addr(&cmdbuf[1], addr_bytes);
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_INDIRECTWRSTARTADDR);
+
+ reg = CQSPI_READL(reg_base + CQSPI_REG_SIZE);
+ reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
+ reg |= (addr_bytes - 1);
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_SIZE);
+ return 0;
+}
+
+int cadence_qspi_apb_indirect_write_execute(void *reg_base,
+ void *ahb_base_addr, unsigned int txlen, const u8 *txbuf)
+{
+ unsigned int reg = 0;
+ unsigned int retry;
+
+ /* Configure the indirect read transfer bytes */
+ CQSPI_WRITEL(txlen, reg_base + CQSPI_REG_INDIRECTWRBYTES);
+
+ /* Start the indirect write transfer */
+ CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_START_MASK,
+ reg_base + CQSPI_REG_INDIRECTWR);
+
+ if (qpsi_write_sram_fifo_push(reg_base, ahb_base_addr,
+ (const void *)txbuf, txlen)) {
+ goto failwr;
+ }
+
+ /* Wait until last write is completed (FIFO empty) */
+ retry = CQSPI_REG_RETRY;
+ while (retry--) {
+ reg = CQSPI_GET_WR_SRAM_LEVEL(reg_base);
+ if (reg == 0)
+ break;
+
+ udelay(1);
+ }
+ if (reg != 0) {
+ printf("QSPI: timeout for indirect write\n");
+ goto failwr;
+ }
+
+ /* Check flash indirect controller status */
+ retry = CQSPI_REG_RETRY;
+ while (retry--) {
+ reg = CQSPI_READL(reg_base + CQSPI_REG_INDIRECTWR);
+ if (reg & CQSPI_REG_INDIRECTWR_DONE_MASK)
+ break;
+ udelay(1);
+ }
+ if (!(reg & CQSPI_REG_INDIRECTWR_DONE_MASK)) {
+ printf("QSPI: indirect completion "
+ "status error with reg 0x%08x\n", reg);
+ goto failwr;
+ }
+
+ /* Clear indirect completion status */
+ CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_DONE_MASK,
+ reg_base + CQSPI_REG_INDIRECTWR);
+ return 0;
+
+failwr:
+ /* Cancel the indirect write */
+ CQSPI_WRITEL(CQSPI_REG_INDIRECTWR_CANCEL_MASK,
+ reg_base + CQSPI_REG_INDIRECTWR);
+ return -1;
+}
+
+void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy)
+{
+ unsigned int reg;
+
+ /* enter XiP mode immediately and enable direct mode */
+ reg = CQSPI_READL(reg_base + CQSPI_REG_CONFIG);
+ reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
+ reg |= CQSPI_REG_CONFIG_DIRECT_MASK;
+ reg |= CQSPI_REG_CONFIG_XIP_IMM_MASK;
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_CONFIG);
+
+ /* keep the XiP mode */
+ CQSPI_WRITEL(xip_dummy, reg_base + CQSPI_REG_MODE_BIT);
+
+ /* Enable mode bit at devrd */
+ reg = CQSPI_READL(reg_base + CQSPI_REG_RD_INSTR);
+ reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB);
+ CQSPI_WRITEL(reg, reg_base + CQSPI_REG_RD_INSTR);
+}
--
1.7.9.5
3
6

[U-Boot] [RFC PATCH V3 1/2] ARM: OMAP4/5: Add alternative method for HSIC USB devices reset
by Lubomir Popov 19 Dec '13
by Lubomir Popov 19 Dec '13
19 Dec '13
Add option for individual reset of HSIC-connected USB devices by the
ehci-hcd.c driver upon applying port power, with per-device configurable
reset hold and delay times. This may replace the reset functionality via
usb_hub.c and board file (which does not work on some boards).
Make HSIC work on all OMAP543x-ES1.0 ports.
Signed-off-by: Lubomir Popov <l-popov(a)ti.com>
---
V1 and V2 got garbled during transmission. V3 is just a resend (again).
drivers/usb/host/ehci-hcd.c | 15 ++++
drivers/usb/host/ehci-omap.c | 174 +++++++++++++++++++++++++++++++++++++-----
2 files changed, 170 insertions(+), 19 deletions(-)
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index 8bd1eb8..47b4097 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -119,6 +119,12 @@ static struct descriptor {
#define ehci_is_TDI() (0)
#endif
+/* OMAP HSIC workaround option: */
+__weak void omap_ehci_hsic_reset_device(int port)
+{
+ return;
+}
+
int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg)
{
return PORTSC_PSPD(reg);
@@ -803,6 +809,15 @@ ehci_submit_root(struct usb_device *dev, unsigned long
pipe, void *buffer,
if (HCS_PPC(ehci_readl(&ctrl->hccr->cr_hcsparams))) {
reg |= EHCI_PS_PP;
ehci_writel(status_reg, reg);
+ /*
+ * OMAP4/5: Reset device for 'fail to connect'
+ * workaround. Weak function, actual reset
+ * should happen in ehci-omap.c and only if we
+ * have defined HSIC devices (in the board file)
+ * that we want to reset at this moment.
+ */
+ omap_ehci_hsic_reset_device(
+ le16_to_cpu(req->index));
}
break;
case USB_PORT_FEAT_RESET:
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index 1b215c2..071739d 100644
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -7,6 +7,13 @@
* Sunil Kumar <sunilsaini05(a)gmail.com>
* Shashi Ranjan <shashiranjanmca05(a)gmail.com>
*
+ * (C) Copyright 2013 Lubomir Popov, MM Solutions <lpopov(a)mm-sol.com>
+ * - Add option for individual reset of HSIC-connected USB devices by the
+ * ehci-hcd.c driver upon applying port power, with per-device configurable
+ * reset hold and delay times. This may replace the reset functionality via
+ * usb_hub.c and board file;
+ * - Make HSIC work on all OMAP5430-ES1.0 ports;
+ * - Add explanatory comments where appropriate.
*
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -26,6 +33,8 @@ static struct omap_uhh *const uhh = (struct omap_uhh
*)OMAP_UHH_BASE;
static struct omap_usbtll *const usbtll = (struct omap_usbtll *)OMAP_USBTLL_BASE;
static struct omap_ehci *const ehci = (struct omap_ehci *)OMAP_EHCI_BASE;
+static struct omap_usbhs_board_data *usbhs_bdp;
+
static int omap_uhh_reset(void)
{
int timeout = 0;
@@ -106,7 +115,7 @@ static void omap_usbhs_hsic_init(int port)
writel(reg, &usbtll->channel_conf + port);
}
-#ifdef CONFIG_USB_ULPI
+#if defined(CONFIG_USB_ULPI) && defined(CONFIG_USB_ULPI_VIEWPORT_OMAP)
static void omap_ehci_soft_phy_reset(int port)
{
struct ulpi_viewport ulpi_vp;
@@ -158,10 +167,141 @@ static inline void omap_ehci_phy_reset(int on, int delay)
#define omap_ehci_phy_reset(on, delay) do {} while (0)
#endif
+/*
+ * Individual HSIC USB device reset to fix 'fail to connect' for some devices.
+ * Note that a HSIC-connected device is actually a permanently attached USB
+ * slave, while a PHY is just a hardware extension of the host port, and
+ * handling them in the same manner is not appropriate.
+ * In order to invoke this feature, define CONFIG_OMAP_HSIC_PORTx_RESET_GPIO
+ * in the board header (where x is the port number with HSIC-attached device
+ * that we want to reset via this method, and the value is the number of the
+ * particular GPIO) - the real functions shall then build and override the
+ * __weak dummy in ehci-hcd.c that is called upon applying port power. The
+ * active reset hold time, as well as the delay after release of reset, are
+ * configurable per device (port) via CONFIG_OMAP_PORTx_RST_HOLD_US and
+ * CONFIG_OMAP_PORTx_DLY_AFTER_US.
+ *
+ * Applicable to OMAP4/5 only (except for the OMAP4430, where HSIC is not
+ * functional). Valid HSIC ports are:
+ * OMAP4460/70: 1, 2
+ * OMAP5430: 1, 2, 3
+ * OMAP5432: 2, 3
+ */
+#if defined(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO) || \
+ defined(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO) || \
+ defined(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO)
+/* Should not be called for non-HSIC ports */
+static void omap_ehci_hsic_reset(int port, int on, int delay)
+{
+ debug("HSIC device reset: port %d, reset %s, delay %d us\n",
+ port, on ? "On" : "Off", delay);
+#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO
+ if (port == 1) {
+ gpio_request(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO,
+ "USB HSIC1 Reset");
+ gpio_direction_output(CONFIG_OMAP_HSIC_PORT1_RESET_GPIO, !on);
+ }
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO
+ if (port == 2) {
+ gpio_request(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO,
+ "USB HSIC2 Reset");
+ gpio_direction_output(CONFIG_OMAP_HSIC_PORT2_RESET_GPIO, !on);
+ }
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO
+ if (port == 3) {
+ gpio_request(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO,
+ "USB HSIC3 Reset");
+ gpio_direction_output(CONFIG_OMAP_HSIC_PORT3_RESET_GPIO, !on);
+ }
+#endif
+ if (delay)
+ udelay(delay);
+}
+
+/*
+ * Called by ehci-hcd when setting the USB_PORT_FEAT_POWER feature
+ * (overrides __weak function in ehci-hcd.c)
+ */
+void omap_ehci_hsic_reset_device(int port)
+{
+ int rst_hold; /* Reset active hold time, us */
+ int dly_after; /* Delay after releasing reset, us */
+
+ if ((port <= 0) ||
+ !(usbhs_bdp) ||
+ !(is_ehci_hsic_mode(usbhs_bdp->port_mode[port-1])))
+ return;
+
+#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO
+ if (port == 1) {
+#ifdef CONFIG_OMAP_PORT1_RST_HOLD_US
+ rst_hold = CONFIG_OMAP_PORT1_RST_HOLD_US;
+#else
+ rst_hold = 10; /* Provide a default hold time */
+#endif
+#ifdef CONFIG_OMAP_PORT1_DLY_AFTER_US
+ dly_after = CONFIG_OMAP_PORT1_DLY_AFTER_US;
+#else
+ dly_after = 0; /* No delay by default */
+#endif
+ }
+#endif
+
+#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO
+ if (port == 2) {
+#ifdef CONFIG_OMAP_PORT2_RST_HOLD_US
+ rst_hold = CONFIG_OMAP_PORT2_RST_HOLD_US;
+#else
+ rst_hold = 10; /* Provide a default hold time */
+#endif
+#ifdef CONFIG_OMAP_PORT2_DLY_AFTER_US
+ dly_after = CONFIG_OMAP_PORT2_DLY_AFTER_US;
+#else
+ dly_after = 0; /* No delay by default */
+#endif
+ }
+#endif
+
+#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO
+ if (port == 3) {
+#ifdef CONFIG_OMAP_PORT3_RST_HOLD_US
+ rst_hold = CONFIG_OMAP_PORT3_RST_HOLD_US;
+#else
+ rst_hold = 10; /* Provide a default hold time */
+#endif
+#ifdef CONFIG_OMAP_PORT3_DLY_AFTER_US
+ dly_after = CONFIG_OMAP_PORT3_DLY_AFTER_US;
+#else
+ dly_after = 0; /* No delay by default */
+#endif
+ }
+#endif
+ omap_ehci_hsic_reset(port, 1, rst_hold);
+ omap_ehci_hsic_reset(port, 0, dly_after);
+}
+
+#else
+/* No CONFIG_OMAP_HSIC_PORTx_RESET_GPIO defined */
+#define omap_ehci_hsic_reset(port, on, delay) do {} while (0)
+#endif
+
/* Reset is needed otherwise the kernel-driver will throw an error. */
int omap_ehci_hcd_stop(void)
{
debug("Resetting OMAP EHCI\n");
+ /* Put HSIC devices, if any, in RESET */
+#ifdef CONFIG_OMAP_HSIC_PORT1_RESET_GPIO
+ omap_ehci_hsic_reset(1, 1, 0);
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT2_RESET_GPIO
+ omap_ehci_hsic_reset(2, 1, 0);
+#endif
+#ifdef CONFIG_OMAP_HSIC_PORT3_RESET_GPIO
+ omap_ehci_hsic_reset(3, 1, 0);
+#endif
+ /* Reset PHYs, if any */
omap_ehci_phy_reset(1, 0);
if (omap_uhh_reset() < 0)
@@ -184,13 +324,15 @@ int omap_ehci_hcd_init(int index, struct
omap_usbhs_board_data *usbhs_pdata,
int ret;
unsigned int i, reg = 0, rev = 0;
- debug("Initializing OMAP EHCI\n");
+ debug("Initializing OMAP EHCI %d\n", index);
ret = board_usb_init(index, USB_INIT_HOST);
if (ret < 0)
return ret;
- /* Put the PHY in RESET */
+ usbhs_bdp = usbhs_pdata;
+
+ /* Put the PHYs, if any, in RESET */
omap_ehci_phy_reset(1, 10);
ret = omap_uhh_reset();
@@ -230,35 +372,28 @@ int omap_ehci_hcd_init(int index, struct
omap_usbhs_board_data *usbhs_pdata,
clrbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
else
setbits_le32(®, OMAP_UHH_HOSTCONFIG_ULPI_P3_BYPASS);
- } else if (rev == OMAP_USBHS_REV2) {
-
- clrsetbits_le32(®, (OMAP_P1_MODE_CLEAR | OMAP_P2_MODE_CLEAR),
- OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
-
- /* Clear port mode fields for PHY mode */
-
- if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
- setbits_le32(®, OMAP_P1_MODE_HSIC);
-
- if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
- setbits_le32(®, OMAP_P2_MODE_HSIC);
-
- } else if (rev == OMAP_USBHS_REV2_1) {
+ } else if ((rev == OMAP_USBHS_REV2) || (rev == OMAP_USBHS_REV2_1)) {
+ /*
+ * OMAP4 and OMAP5-ES1 UHH are R.2.0, OMAP5-ES2 - R.2.1
+ *
+ * Clear port mode fields for ULPI PHY mode. On OMAP4 the P3
+ * field is reserved, but clearing it does not harm.
+ */
clrsetbits_le32(®,
(OMAP_P1_MODE_CLEAR |
OMAP_P2_MODE_CLEAR |
OMAP_P3_MODE_CLEAR),
OMAP4_UHH_HOSTCONFIG_APP_START_CLK);
- /* Clear port mode fields for PHY mode */
-
+ /* Warning: HSIC mode for Port 1 not usable on OMAP5432 */
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[0]))
setbits_le32(®, OMAP_P1_MODE_HSIC);
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[1]))
setbits_le32(®, OMAP_P2_MODE_HSIC);
+ /* Warning: HSIC mode for Port 3 possible on OMAP5 only */
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[2]))
setbits_le32(®, OMAP_P3_MODE_HSIC);
}
@@ -270,6 +405,7 @@ int omap_ehci_hcd_init(int index, struct
omap_usbhs_board_data *usbhs_pdata,
if (is_ehci_hsic_mode(usbhs_pdata->port_mode[i]))
omap_usbhs_hsic_init(i);
+ /* Release ULPI PHY reset and let PLL lock (may need more delay...) */
omap_ehci_phy_reset(0, 10);
/*
--
1.7.9.5
2
1