avionic design with actual uboot and tooling

submodule of avionic design uboot bootloader and with included tools to
get you started , read readme.md and readme-tk1-loader.md
This commit is contained in:
2026-03-03 21:46:32 +02:00
parent fe3ba02c96
commit 68d74d3181
11967 changed files with 2221897 additions and 0 deletions

197
u-boot/drivers/spi/Kconfig Normal file
View File

@@ -0,0 +1,197 @@
menu "SPI Support"
config DM_SPI
bool "Enable Driver Model for SPI drivers"
depends on DM
help
Enable driver model for SPI. The SPI slave interface
(spi_setup_slave(), spi_xfer(), etc.) is then implemented by
the SPI uclass. Drivers provide methods to access the SPI
buses that they control. The uclass interface is defined in
include/spi.h. The existing spi_slave structure is attached
as 'parent data' to every slave on each bus. Slaves
typically use driver-private data instead of extending the
spi_slave structure.
if DM_SPI
config ALTERA_SPI
bool "Altera SPI driver"
help
Enable the Altera SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Altera
IP core. Please find details on the "Embedded Peripherals IP
User Guide" of Altera.
config ATH79_SPI
bool "Atheros SPI driver"
depends on ARCH_ATH79
help
Enable the Atheros ar7xxx/ar9xxx SoC SPI driver, it was used
to access SPI NOR flash and other SPI peripherals. This driver
uses driver model and requires a device tree binding to operate.
please refer to doc/device-tree-bindings/spi/spi-ath79.txt.
config CADENCE_QSPI
bool "Cadence QSPI driver"
help
Enable the Cadence Quad-SPI (QSPI) driver. This driver can be
used to access the SPI NOR flash on platforms embedding this
Cadence IP core.
config DESIGNWARE_SPI
bool "Designware SPI driver"
help
Enable the Designware SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Designware
IP core.
config EXYNOS_SPI
bool "Samsung Exynos SPI driver"
help
Enable the Samsung Exynos SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Samsung
Exynos IP core.
config FSL_DSPI
bool "Freescale DSPI driver"
help
Enable the Freescale DSPI driver. This driver can be used to
access the SPI NOR flash and SPI Data flash on platforms embedding
this Freescale DSPI IP core. LS102xA and Colibri VF50/VF61 platforms
use this driver.
config FSL_QSPI
bool "Freescale QSPI driver"
help
Enable the Freescale Quad-SPI (QSPI) driver. This driver can be
used to access the SPI NOR flash on platforms embedding this
Freescale IP core.
config ICH_SPI
bool "Intel ICH SPI driver"
help
Enable the Intel ICH SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Intel
ICH IP core.
config PIC32_SPI
bool "Microchip PIC32 SPI driver"
depends on MACH_PIC32
help
Enable the Microchip PIC32 SPI driver. This driver can be used
to access the SPI NOR flash, MMC-over-SPI on platforms based on
Microchip PIC32 family devices.
config ROCKCHIP_SPI
bool "Rockchip SPI driver"
help
Enable the Rockchip SPI driver, used to access SPI NOR flash and
other SPI peripherals (such as the Chrome OS EC) on Rockchip SoCs.
This uses driver model and requires a device tree binding to
operate.
config SANDBOX_SPI
bool "Sandbox SPI driver"
depends on SANDBOX && DM
help
Enable SPI support for sandbox. This is an emulation of a real SPI
bus. Devices can be attached to the bus using the device tree
which specifies the driver to use. As an example, see this device
tree fragment from sandbox.dts. It shows that the SPI bus has a
single flash device on chip select 0 which is emulated by the driver
for "sandbox,spi-flash", which is in drivers/mtd/spi/sandbox.c.
spi@0 {
#address-cells = <1>;
#size-cells = <0>;
reg = <0>;
compatible = "sandbox,spi";
cs-gpios = <0>, <&gpio_a 0>;
flash@0 {
reg = <0>;
compatible = "spansion,m25p16", "sandbox,spi-flash";
spi-max-frequency = <40000000>;
sandbox,filename = "spi.bin";
};
};
config TEGRA114_SPI
bool "nVidia Tegra114 SPI driver"
help
Enable the nVidia Tegra114 SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this nVidia Tegra114
IP core.
This controller is different than the older SoCs SPI controller and
also register interface get changed with this controller.
config TEGRA20_SFLASH
bool "nVidia Tegra20 Serial Flash controller driver"
help
Enable the nVidia Tegra20 Serial Flash controller driver. This driver
can be used to access the SPI NOR flash on platforms embedding this
nVidia Tegra20 IP core.
config TEGRA20_SLINK
bool "nVidia Tegra20/Tegra30 SLINK driver"
help
Enable the nVidia Tegra20/Tegra30 SLINK driver. This driver can
be used to access the SPI NOR flash on platforms embedding this
nVidia Tegra20/Tegra30 IP cores.
config TEGRA210_QSPI
bool "nVidia Tegra210 QSPI driver"
help
Enable the Tegra Quad-SPI (QSPI) driver for T210. This driver
be used to access SPI chips on platforms embedding this
NVIDIA Tegra210 IP core.
config XILINX_SPI
bool "Xilinx SPI driver"
help
Enable the Xilinx SPI driver from the Xilinx EDK. This SPI
controller support 8 bit SPI transfers only, with or w/o FIFO.
For more info on Xilinx SPI Register Definitions and Overview
see driver file - drivers/spi/xilinx_spi.c
config ZYNQ_SPI
bool "Zynq SPI driver"
depends on ARCH_ZYNQ || ARCH_ZYNQMP
help
Enable the Zynq SPI driver. This driver can be used to
access the SPI NOR flash on platforms embedding this Zynq
SPI IP core.
config ZYNQ_QSPI
bool "Zynq QSPI driver"
depends on ARCH_ZYNQ
help
Enable the Zynq Quad-SPI (QSPI) driver. This driver can be
used to access the SPI NOR flash on platforms embedding this
Zynq QSPI IP core. This IP is used to connect the flash in
4-bit qspi, 8-bit dual stacked and shared 4-bit dual parallel.
config OMAP3_SPI
bool "McSPI driver for OMAP"
help
SPI master controller for OMAP24XX and later Multichannel SPI
(McSPI). This driver be used to access SPI chips on platforms
embedding this OMAP3 McSPI IP core.
endif # if DM_SPI
config FSL_ESPI
bool "Freescale eSPI driver"
help
Enable the Freescale eSPI driver. This driver can be used to
access the SPI interface and SPI NOR flash on platforms embedding
this Freescale eSPI IP core.
config TI_QSPI
bool "TI QSPI driver"
help
Enable the TI Quad-SPI (QSPI) driver for DRA7xx and AM43xx evms.
This driver support spi flash single, quad and memory reads.
endmenu # menu "SPI Support"

View File

@@ -0,0 +1,55 @@
#
# (C) Copyright 2000-2007
# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
#
# SPDX-License-Identifier: GPL-2.0+
#
# There are many options which enable SPI, so make this library available
ifdef CONFIG_DM_SPI
obj-y += spi-uclass.o
obj-$(CONFIG_SANDBOX) += spi-emul-uclass.o
obj-$(CONFIG_SOFT_SPI) += soft_spi.o
else
obj-y += spi.o
obj-$(CONFIG_SOFT_SPI) += soft_spi_legacy.o
endif
obj-$(CONFIG_ALTERA_SPI) += altera_spi.o
obj-$(CONFIG_ARMADA100_SPI) += armada100_spi.o
obj-$(CONFIG_ATH79_SPI) += ath79_spi.o
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
obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o
obj-$(CONFIG_EP93XX_SPI) += ep93xx_spi.o
obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o
obj-$(CONFIG_FSL_DSPI) += fsl_dspi.o
obj-$(CONFIG_FSL_ESPI) += fsl_espi.o
obj-$(CONFIG_FSL_QSPI) += fsl_qspi.o
obj-$(CONFIG_ICH_SPI) += ich.o
obj-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o
obj-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
obj-$(CONFIG_MXC_SPI) += mxc_spi.o
obj-$(CONFIG_MXS_SPI) += mxs_spi.o
obj-$(CONFIG_OMAP3_SPI) += omap3_spi.o
obj-$(CONFIG_PIC32_SPI) += pic32_spi.o
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
obj-$(CONFIG_SH_SPI) += sh_spi.o
obj-$(CONFIG_SH_QSPI) += sh_qspi.o
obj-$(CONFIG_TEGRA114_SPI) += tegra114_spi.o
obj-$(CONFIG_TEGRA20_SFLASH) += tegra20_sflash.o
obj-$(CONFIG_TEGRA20_SLINK) += tegra20_slink.o
obj-$(CONFIG_TEGRA210_QSPI) += tegra210_qspi.o
obj-$(CONFIG_TI_QSPI) += ti_qspi.o
obj-$(CONFIG_XILINX_SPI) += xilinx_spi.o
obj-$(CONFIG_ZYNQ_SPI) += zynq_spi.o
obj-$(CONFIG_ZYNQ_QSPI) += zynq_qspi.o

View File

@@ -0,0 +1,210 @@
/*
* Altera SPI driver
*
* based on bfin_spi.c
* Copyright (c) 2005-2008 Analog Devices Inc.
* Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <fdtdec.h>
#include <spi.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
#define ALTERA_SPI_STATUS_RRDY_MSK BIT(7)
#define ALTERA_SPI_CONTROL_SSO_MSK BIT(10)
#ifndef CONFIG_ALTERA_SPI_IDLE_VAL
#define CONFIG_ALTERA_SPI_IDLE_VAL 0xff
#endif
struct altera_spi_regs {
u32 rxdata;
u32 txdata;
u32 status;
u32 control;
u32 _reserved;
u32 slave_sel;
};
struct altera_spi_platdata {
struct altera_spi_regs *regs;
};
struct altera_spi_priv {
struct altera_spi_regs *regs;
};
static void spi_cs_activate(struct udevice *dev, uint cs)
{
struct udevice *bus = dev->parent;
struct altera_spi_priv *priv = dev_get_priv(bus);
struct altera_spi_regs *const regs = priv->regs;
writel(1 << cs, &regs->slave_sel);
writel(ALTERA_SPI_CONTROL_SSO_MSK, &regs->control);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct altera_spi_priv *priv = dev_get_priv(bus);
struct altera_spi_regs *const regs = priv->regs;
writel(0, &regs->control);
writel(0, &regs->slave_sel);
}
static int altera_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct altera_spi_priv *priv = dev_get_priv(bus);
struct altera_spi_regs *const regs = priv->regs;
writel(0, &regs->control);
writel(0, &regs->slave_sel);
return 0;
}
static int altera_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct altera_spi_priv *priv = dev_get_priv(bus);
struct altera_spi_regs *const regs = priv->regs;
writel(0, &regs->slave_sel);
return 0;
}
static int altera_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct altera_spi_priv *priv = dev_get_priv(bus);
struct altera_spi_regs *const regs = priv->regs;
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
/* assume spi core configured to do 8 bit transfers */
unsigned int bytes = bitlen / 8;
const unsigned char *txp = dout;
unsigned char *rxp = din;
uint32_t reg, data, start;
debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
bus->seq, slave_plat->cs, bitlen, bytes, flags);
if (bitlen == 0)
goto done;
if (bitlen % 8) {
flags |= SPI_XFER_END;
goto done;
}
/* empty read buffer */
if (readl(&regs->status) & ALTERA_SPI_STATUS_RRDY_MSK)
readl(&regs->rxdata);
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev, slave_plat->cs);
while (bytes--) {
if (txp)
data = *txp++;
else
data = CONFIG_ALTERA_SPI_IDLE_VAL;
debug("%s: tx:%x ", __func__, data);
writel(data, &regs->txdata);
start = get_timer(0);
while (1) {
reg = readl(&regs->status);
if (reg & ALTERA_SPI_STATUS_RRDY_MSK)
break;
if (get_timer(start) > (CONFIG_SYS_HZ / 1000)) {
debug("%s: Transmission timed out!\n", __func__);
return -1;
}
}
data = readl(&regs->rxdata);
if (rxp)
*rxp++ = data & 0xff;
debug("rx:%x\n", data);
}
done:
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
return 0;
}
static int altera_spi_set_speed(struct udevice *bus, uint speed)
{
return 0;
}
static int altera_spi_set_mode(struct udevice *bus, uint mode)
{
return 0;
}
static int altera_spi_probe(struct udevice *bus)
{
struct altera_spi_platdata *plat = dev_get_platdata(bus);
struct altera_spi_priv *priv = dev_get_priv(bus);
priv->regs = plat->regs;
return 0;
}
static int altera_spi_ofdata_to_platdata(struct udevice *bus)
{
struct altera_spi_platdata *plat = dev_get_platdata(bus);
plat->regs = map_physmem(dev_get_addr(bus),
sizeof(struct altera_spi_regs),
MAP_NOCACHE);
return 0;
}
static const struct dm_spi_ops altera_spi_ops = {
.claim_bus = altera_spi_claim_bus,
.release_bus = altera_spi_release_bus,
.xfer = altera_spi_xfer,
.set_speed = altera_spi_set_speed,
.set_mode = altera_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id altera_spi_ids[] = {
{ .compatible = "altr,spi-1.0" },
{}
};
U_BOOT_DRIVER(altera_spi) = {
.name = "altera_spi",
.id = UCLASS_SPI,
.of_match = altera_spi_ids,
.ops = &altera_spi_ops,
.ofdata_to_platdata = altera_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct altera_spi_platdata),
.priv_auto_alloc_size = sizeof(struct altera_spi_priv),
.probe = altera_spi_probe,
};

View File

@@ -0,0 +1,203 @@
/*
* (C) Copyright 2011
* eInfochips Ltd. <www.einfochips.com>
* Written-by: Ajay Bhargav <ajay.bhargav@einfochips.com>
*
* (C) Copyright 2009
* Marvell Semiconductor <www.marvell.com>
* Based on SSP driver
* Written-by: Lei Wen <leiwen@marvell.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
#include <asm/arch/spi.h>
#include <asm/gpio.h>
#define to_armd_spi_slave(s) container_of(s, struct armd_spi_slave, slave)
struct armd_spi_slave {
struct spi_slave slave;
struct ssp_reg *spi_reg;
u32 cr0, cr1;
u32 int_cr1;
u32 clear_sr;
const void *tx;
void *rx;
int gpio_cs_inverted;
};
static int spi_armd_write(struct armd_spi_slave *pss)
{
int wait_timeout = SSP_FLUSH_NUM;
while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_TNF))
;
if (!wait_timeout) {
debug("%s: timeout error\n", __func__);
return -1;
}
if (pss->tx != NULL) {
writel(*(u8 *)pss->tx, &pss->spi_reg->ssdr);
++pss->tx;
} else {
writel(0, &pss->spi_reg->ssdr);
}
return 0;
}
static int spi_armd_read(struct armd_spi_slave *pss)
{
int wait_timeout = SSP_FLUSH_NUM;
while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_RNE))
;
if (!wait_timeout) {
debug("%s: timeout error\n", __func__);
return -1;
}
if (pss->rx != NULL) {
*(u8 *)pss->rx = readl(&pss->spi_reg->ssdr);
++pss->rx;
} else {
readl(&pss->spi_reg->ssdr);
}
return 0;
}
static int spi_armd_flush(struct armd_spi_slave *pss)
{
unsigned long limit = SSP_FLUSH_NUM;
do {
while (readl(&pss->spi_reg->sssr) & SSSR_RNE)
readl(&pss->spi_reg->ssdr);
} while ((readl(&pss->spi_reg->sssr) & SSSR_BSY) && limit--);
writel(SSSR_ROR, &pss->spi_reg->sssr);
return limit;
}
void spi_cs_activate(struct spi_slave *slave)
{
struct armd_spi_slave *pss = to_armd_spi_slave(slave);
gpio_set_value(slave->cs, pss->gpio_cs_inverted);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct armd_spi_slave *pss = to_armd_spi_slave(slave);
gpio_set_value(slave->cs, !pss->gpio_cs_inverted);
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct armd_spi_slave *pss;
pss = spi_alloc_slave(struct armd_spi_slave, bus, cs);
if (!pss)
return NULL;
pss->spi_reg = (struct ssp_reg *)SSP_REG_BASE(CONFIG_SYS_SSP_PORT);
pss->cr0 = SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) | SSCR0_SSE;
pss->cr1 = (SSCR1_RXTRESH(RX_THRESH_DEF) & SSCR1_RFT) |
(SSCR1_TXTRESH(TX_THRESH_DEF) & SSCR1_TFT);
pss->cr1 &= ~(SSCR1_SPO | SSCR1_SPH);
pss->cr1 |= (((mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0)
| (((mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0);
pss->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE;
pss->clear_sr = SSSR_ROR | SSSR_TINT;
pss->gpio_cs_inverted = mode & SPI_CS_HIGH;
gpio_set_value(cs, !pss->gpio_cs_inverted);
return &pss->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct armd_spi_slave *pss = to_armd_spi_slave(slave);
free(pss);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct armd_spi_slave *pss = to_armd_spi_slave(slave);
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
if (spi_armd_flush(pss) == 0)
return -1;
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct armd_spi_slave *pss = to_armd_spi_slave(slave);
uint bytes = bitlen / 8;
unsigned long limit;
int ret = 0;
if (bitlen == 0)
goto done;
/* we can only do 8 bit transfers */
if (bitlen % 8) {
flags |= SPI_XFER_END;
goto done;
}
pss->tx = dout;
pss->rx = din;
if (flags & SPI_XFER_BEGIN) {
spi_cs_activate(slave);
writel(pss->cr1 | pss->int_cr1, &pss->spi_reg->sscr1);
writel(TIMEOUT_DEF, &pss->spi_reg->ssto);
writel(pss->cr0, &pss->spi_reg->sscr0);
}
while (bytes--) {
limit = SSP_FLUSH_NUM;
ret = spi_armd_write(pss);
if (ret)
break;
while ((readl(&pss->spi_reg->sssr) & SSSR_BSY) && limit--)
udelay(1);
ret = spi_armd_read(pss);
if (ret)
break;
}
done:
if (flags & SPI_XFER_END) {
/* Stop SSP */
writel(pss->clear_sr, &pss->spi_reg->sssr);
clrbits_le32(&pss->spi_reg->sscr1, pss->int_cr1);
writel(0, &pss->spi_reg->ssto);
spi_cs_deactivate(slave);
}
return ret;
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spi.h>
#include <dm.h>
#include <div64.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/addrspace.h>
#include <asm/types.h>
#include <dm/pinctrl.h>
#include <mach/ar71xx_regs.h>
/* CLOCK_DIVIDER = 3 (SPI clock = 200 / 8 ~ 25 MHz) */
#define ATH79_SPI_CLK_DIV(x) (((x) >> 1) - 1)
#define ATH79_SPI_RRW_DELAY_FACTOR 12000
#define ATH79_SPI_MHZ (1000 * 1000)
struct ath79_spi_priv {
void __iomem *regs;
u32 rrw_delay;
};
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct ath79_spi_priv *priv = dev_get_priv(bus);
writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct ath79_spi_priv *priv = dev_get_priv(bus);
writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC);
writel(0, priv->regs + AR71XX_SPI_REG_FS);
}
static int ath79_spi_claim_bus(struct udevice *dev)
{
return 0;
}
static int ath79_spi_release_bus(struct udevice *dev)
{
return 0;
}
static int ath79_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct ath79_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
u8 *rx = din;
const u8 *tx = dout;
u8 curbyte, curbitlen, restbits;
u32 bytes = bitlen / 8;
u32 out, in;
u64 tick;
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
restbits = (bitlen % 8);
if (restbits)
bytes++;
out = AR71XX_SPI_IOC_CS_ALL & ~(AR71XX_SPI_IOC_CS(slave->cs));
while (bytes > 0) {
bytes--;
curbyte = 0;
if (tx)
curbyte = *tx++;
if (restbits && !bytes) {
curbitlen = restbits;
curbyte <<= 8 - restbits;
} else {
curbitlen = 8;
}
for (curbyte <<= (8 - curbitlen); curbitlen; curbitlen--) {
if (curbyte & 0x80)
out |= AR71XX_SPI_IOC_DO;
else
out &= ~(AR71XX_SPI_IOC_DO);
writel(out, priv->regs + AR71XX_SPI_REG_IOC);
/* delay for low level */
if (priv->rrw_delay) {
tick = get_ticks() + priv->rrw_delay;
while (get_ticks() < tick)
/*NOP*/;
}
writel(out | AR71XX_SPI_IOC_CLK,
priv->regs + AR71XX_SPI_REG_IOC);
/* delay for high level */
if (priv->rrw_delay) {
tick = get_ticks() + priv->rrw_delay;
while (get_ticks() < tick)
/*NOP*/;
}
curbyte <<= 1;
}
if (!bytes)
writel(out, priv->regs + AR71XX_SPI_REG_IOC);
in = readl(priv->regs + AR71XX_SPI_REG_RDS);
if (rx) {
if (restbits && !bytes)
*rx++ = (in << (8 - restbits));
else
*rx++ = in;
}
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
return 0;
}
static int ath79_spi_set_speed(struct udevice *bus, uint speed)
{
struct ath79_spi_priv *priv = dev_get_priv(bus);
u32 val, div = 0;
u64 time;
if (speed)
div = get_bus_freq(0) / speed;
if (div > 63)
div = 63;
if (div < 5)
div = 5;
/* calculate delay */
time = get_tbclk();
do_div(time, speed / 2);
val = get_bus_freq(0) / ATH79_SPI_MHZ;
val = ATH79_SPI_RRW_DELAY_FACTOR / val;
if (time > val)
priv->rrw_delay = time - val + 1;
else
priv->rrw_delay = 0;
writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
clrsetbits_be32(priv->regs + AR71XX_SPI_REG_CTRL,
AR71XX_SPI_CTRL_DIV_MASK,
ATH79_SPI_CLK_DIV(div));
writel(0, priv->regs + AR71XX_SPI_REG_FS);
return 0;
}
static int ath79_spi_set_mode(struct udevice *bus, uint mode)
{
return 0;
}
static int ath79_spi_probe(struct udevice *bus)
{
struct ath79_spi_priv *priv = dev_get_priv(bus);
fdt_addr_t addr;
addr = dev_get_addr(bus);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = map_physmem(addr,
AR71XX_SPI_SIZE,
MAP_NOCACHE);
/* Init SPI Hardware, disable remap, set clock */
writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
writel(AR71XX_SPI_CTRL_RD | ATH79_SPI_CLK_DIV(8),
priv->regs + AR71XX_SPI_REG_CTRL);
writel(0, priv->regs + AR71XX_SPI_REG_FS);
return 0;
}
static int ath79_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
{
/* Always allow activity on CS 0/1/2 */
if (cs >= 3)
return -ENODEV;
return 0;
}
static const struct dm_spi_ops ath79_spi_ops = {
.claim_bus = ath79_spi_claim_bus,
.release_bus = ath79_spi_release_bus,
.xfer = ath79_spi_xfer,
.set_speed = ath79_spi_set_speed,
.set_mode = ath79_spi_set_mode,
.cs_info = ath79_cs_info,
};
static const struct udevice_id ath79_spi_ids[] = {
{ .compatible = "qca,ar7100-spi" },
{}
};
U_BOOT_DRIVER(ath79_spi) = {
.name = "ath79_spi",
.id = UCLASS_SPI,
.of_match = ath79_spi_ids,
.ops = &ath79_spi_ops,
.priv_auto_alloc_size = sizeof(struct ath79_spi_priv),
.probe = ath79_spi_probe,
};

View File

@@ -0,0 +1,184 @@
/*
* Driver for ATMEL DataFlash support
* Author : Hamid Ikdoumi (Atmel)
*
* SPDX-License-Identifier: GPL-2.0+
*/
/*
* This driver desperately needs rework:
*
* - use structure SoC access
* - get rid of including asm/arch/at91_spi.h
* - remove asm/arch/at91_spi.h
* - get rid of all CONFIG_ATMEL_LEGACY defines and uses
*
* 02-Aug-2010 Reinhard Meyer <uboot@emk-elektronik.de>
*/
#include <common.h>
#ifndef CONFIG_ATMEL_LEGACY
# define CONFIG_ATMEL_LEGACY
#endif
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/clk.h>
#include <asm/arch/hardware.h>
#include "atmel_spi.h"
#include <asm/arch/gpio.h>
#include <asm/arch/at91_pio.h>
#include <asm/arch/at91_spi.h>
#include <dataflash.h>
#define AT91_SPI_PCS0_DATAFLASH_CARD 0xE /* Chip Select 0: NPCS0%1110 */
#define AT91_SPI_PCS1_DATAFLASH_CARD 0xD /* Chip Select 1: NPCS1%1101 */
#define AT91_SPI_PCS2_DATAFLASH_CARD 0xB /* Chip Select 2: NPCS2%1011 */
#define AT91_SPI_PCS3_DATAFLASH_CARD 0x7 /* Chip Select 3: NPCS3%0111 */
void AT91F_SpiInit(void)
{
/* Reset the SPI */
writel(AT91_SPI_SWRST, ATMEL_BASE_SPI0 + AT91_SPI_CR);
/* Configure SPI in Master Mode with No CS selected !!! */
writel(AT91_SPI_MSTR | AT91_SPI_MODFDIS | AT91_SPI_PCS,
ATMEL_BASE_SPI0 + AT91_SPI_MR);
/* Configure CS0 */
writel(AT91_SPI_NCPHA |
(AT91_SPI_DLYBS & DATAFLASH_TCSS) |
(AT91_SPI_DLYBCT & DATAFLASH_TCHS) |
((get_mck_clk_rate() / AT91_SPI_CLK) << 8),
ATMEL_BASE_SPI0 + AT91_SPI_CSR(0));
#ifdef CONFIG_SYS_DATAFLASH_LOGIC_ADDR_CS1
/* Configure CS1 */
writel(AT91_SPI_NCPHA |
(AT91_SPI_DLYBS & DATAFLASH_TCSS) |
(AT91_SPI_DLYBCT & DATAFLASH_TCHS) |
((get_mck_clk_rate() / AT91_SPI_CLK) << 8),
ATMEL_BASE_SPI0 + AT91_SPI_CSR(1));
#endif
#ifdef CONFIG_SYS_DATAFLASH_LOGIC_ADDR_CS2
/* Configure CS2 */
writel(AT91_SPI_NCPHA |
(AT91_SPI_DLYBS & DATAFLASH_TCSS) |
(AT91_SPI_DLYBCT & DATAFLASH_TCHS) |
((get_mck_clk_rate() / AT91_SPI_CLK) << 8),
ATMEL_BASE_SPI0 + AT91_SPI_CSR(2));
#endif
#ifdef CONFIG_SYS_DATAFLASH_LOGIC_ADDR_CS3
/* Configure CS3 */
writel(AT91_SPI_NCPHA |
(AT91_SPI_DLYBS & DATAFLASH_TCSS) |
(AT91_SPI_DLYBCT & DATAFLASH_TCHS) |
((get_mck_clk_rate() / AT91_SPI_CLK) << 8),
ATMEL_BASE_SPI0 + AT91_SPI_CSR(3));
#endif
/* SPI_Enable */
writel(AT91_SPI_SPIEN, ATMEL_BASE_SPI0 + AT91_SPI_CR);
while (!(readl(ATMEL_BASE_SPI0 + AT91_SPI_SR) & AT91_SPI_SPIENS))
;
/*
* Add tempo to get SPI in a safe state.
* Should not be needed for new silicon (Rev B)
*/
udelay(500000);
readl(ATMEL_BASE_SPI0 + AT91_SPI_SR);
readl(ATMEL_BASE_SPI0 + AT91_SPI_RDR);
}
void AT91F_SpiEnable(int cs)
{
unsigned long mode;
mode = readl(ATMEL_BASE_SPI0 + AT91_SPI_MR);
mode &= ~AT91_SPI_PCS;
switch (cs) {
case 0:
mode |= AT91_SPI_PCS0_DATAFLASH_CARD << 16;
break;
case 1:
mode |= AT91_SPI_PCS1_DATAFLASH_CARD << 16;
break;
case 2:
mode |= AT91_SPI_PCS2_DATAFLASH_CARD << 16;
break;
case 3:
mode |= AT91_SPI_PCS3_DATAFLASH_CARD << 16;
break;
}
writel(mode, ATMEL_BASE_SPI0 + AT91_SPI_MR);
/* SPI_Enable */
writel(AT91_SPI_SPIEN, ATMEL_BASE_SPI0 + AT91_SPI_CR);
}
unsigned int AT91F_SpiWrite1(AT91PS_DataflashDesc pDesc);
unsigned int AT91F_SpiWrite(AT91PS_DataflashDesc pDesc)
{
unsigned int timeout;
unsigned int timebase;
pDesc->state = BUSY;
writel(AT91_SPI_TXTDIS + AT91_SPI_RXTDIS,
ATMEL_BASE_SPI0 + AT91_SPI_PTCR);
/* Initialize the Transmit and Receive Pointer */
writel((unsigned int)pDesc->rx_cmd_pt,
ATMEL_BASE_SPI0 + AT91_SPI_RPR);
writel((unsigned int)pDesc->tx_cmd_pt,
ATMEL_BASE_SPI0 + AT91_SPI_TPR);
/* Intialize the Transmit and Receive Counters */
writel(pDesc->rx_cmd_size, ATMEL_BASE_SPI0 + AT91_SPI_RCR);
writel(pDesc->tx_cmd_size, ATMEL_BASE_SPI0 + AT91_SPI_TCR);
if (pDesc->tx_data_size != 0) {
/* Initialize the Next Transmit and Next Receive Pointer */
writel((unsigned int)pDesc->rx_data_pt,
ATMEL_BASE_SPI0 + AT91_SPI_RNPR);
writel((unsigned int)pDesc->tx_data_pt,
ATMEL_BASE_SPI0 + AT91_SPI_TNPR);
/* Intialize the Next Transmit and Next Receive Counters */
writel(pDesc->rx_data_size,
ATMEL_BASE_SPI0 + AT91_SPI_RNCR);
writel(pDesc->tx_data_size,
ATMEL_BASE_SPI0 + AT91_SPI_TNCR);
}
/* arm simple, non interrupt dependent timer */
timebase = get_timer(0);
timeout = 0;
writel(AT91_SPI_TXTEN + AT91_SPI_RXTEN,
ATMEL_BASE_SPI0 + AT91_SPI_PTCR);
while (!(readl(ATMEL_BASE_SPI0 + AT91_SPI_SR) & AT91_SPI_RXBUFF) &&
((timeout = get_timer(timebase)) < CONFIG_SYS_SPI_WRITE_TOUT))
;
writel(AT91_SPI_TXTDIS + AT91_SPI_RXTDIS,
ATMEL_BASE_SPI0 + AT91_SPI_PTCR);
pDesc->state = IDLE;
if (timeout >= CONFIG_SYS_SPI_WRITE_TOUT) {
printf("Error Timeout\n\r");
return DATAFLASH_ERROR;
}
return DATAFLASH_OK;
}

View File

@@ -0,0 +1,211 @@
/*
* Copyright (C) 2007 Atmel Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/clk.h>
#include <asm/arch/hardware.h>
#include "atmel_spi.h"
static int spi_has_wdrbt(struct atmel_spi_slave *slave)
{
unsigned int ver;
ver = spi_readl(slave, VERSION);
return (ATMEL_SPI_VERSION_REV(ver) >= 0x210);
}
void spi_init()
{
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct atmel_spi_slave *as;
unsigned int scbr;
u32 csrx;
void *regs;
if (!spi_cs_is_valid(bus, cs))
return NULL;
switch (bus) {
case 0:
regs = (void *)ATMEL_BASE_SPI0;
break;
#ifdef ATMEL_BASE_SPI1
case 1:
regs = (void *)ATMEL_BASE_SPI1;
break;
#endif
#ifdef ATMEL_BASE_SPI2
case 2:
regs = (void *)ATMEL_BASE_SPI2;
break;
#endif
#ifdef ATMEL_BASE_SPI3
case 3:
regs = (void *)ATMEL_BASE_SPI3;
break;
#endif
default:
return NULL;
}
scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz;
if (scbr > ATMEL_SPI_CSRx_SCBR_MAX)
/* Too low max SCK rate */
return NULL;
if (scbr < 1)
scbr = 1;
csrx = ATMEL_SPI_CSRx_SCBR(scbr);
csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8);
if (!(mode & SPI_CPHA))
csrx |= ATMEL_SPI_CSRx_NCPHA;
if (mode & SPI_CPOL)
csrx |= ATMEL_SPI_CSRx_CPOL;
as = spi_alloc_slave(struct atmel_spi_slave, bus, cs);
if (!as)
return NULL;
as->regs = regs;
as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
| ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
if (spi_has_wdrbt(as))
as->mr |= ATMEL_SPI_MR_WDRBT;
spi_writel(as, CSR(cs), csrx);
return &as->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct atmel_spi_slave *as = to_atmel_spi(slave);
free(as);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct atmel_spi_slave *as = to_atmel_spi(slave);
/* Enable the SPI hardware */
spi_writel(as, CR, ATMEL_SPI_CR_SPIEN);
/*
* Select the slave. This should set SCK to the correct
* initial state, etc.
*/
spi_writel(as, MR, as->mr);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct atmel_spi_slave *as = to_atmel_spi(slave);
/* Disable the SPI hardware */
spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct atmel_spi_slave *as = to_atmel_spi(slave);
unsigned int len_tx;
unsigned int len_rx;
unsigned int len;
u32 status;
const u8 *txp = dout;
u8 *rxp = din;
u8 value;
if (bitlen == 0)
/* Finish any previously submitted transfers */
goto out;
/*
* TODO: The controller can do non-multiple-of-8 bit
* transfers, but this driver currently doesn't support it.
*
* It's also not clear how such transfers are supposed to be
* represented as a stream of bytes...this is a limitation of
* the current SPI interface.
*/
if (bitlen % 8) {
/* Errors always terminate an ongoing transfer */
flags |= SPI_XFER_END;
goto out;
}
len = bitlen / 8;
/*
* The controller can do automatic CS control, but it is
* somewhat quirky, and it doesn't really buy us much anyway
* in the context of U-Boot.
*/
if (flags & SPI_XFER_BEGIN) {
spi_cs_activate(slave);
/*
* sometimes the RDR is not empty when we get here,
* in theory that should not happen, but it DOES happen.
* Read it here to be on the safe side.
* That also clears the OVRES flag. Required if the
* following loop exits due to OVRES!
*/
spi_readl(as, RDR);
}
for (len_tx = 0, len_rx = 0; len_rx < len; ) {
status = spi_readl(as, SR);
if (status & ATMEL_SPI_SR_OVRES)
return -1;
if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) {
if (txp)
value = *txp++;
else
value = 0;
spi_writel(as, TDR, value);
len_tx++;
}
if (status & ATMEL_SPI_SR_RDRF) {
value = spi_readl(as, RDR);
if (rxp)
*rxp++ = value;
len_rx++;
}
}
out:
if (flags & SPI_XFER_END) {
/*
* Wait until the transfer is completely done before
* we deactivate CS.
*/
do {
status = spi_readl(as, SR);
} while (!(status & ATMEL_SPI_SR_TXEMPTY));
spi_cs_deactivate(slave);
}
return 0;
}

View File

@@ -0,0 +1,100 @@
/*
* Register definitions for the Atmel AT32/AT91 SPI Controller
*/
/* Register offsets */
#define ATMEL_SPI_CR 0x0000
#define ATMEL_SPI_MR 0x0004
#define ATMEL_SPI_RDR 0x0008
#define ATMEL_SPI_TDR 0x000c
#define ATMEL_SPI_SR 0x0010
#define ATMEL_SPI_IER 0x0014
#define ATMEL_SPI_IDR 0x0018
#define ATMEL_SPI_IMR 0x001c
#define ATMEL_SPI_CSR(x) (0x0030 + 4 * (x))
#define ATMEL_SPI_VERSION 0x00fc
/* Bits in CR */
#define ATMEL_SPI_CR_SPIEN BIT(0)
#define ATMEL_SPI_CR_SPIDIS BIT(1)
#define ATMEL_SPI_CR_SWRST BIT(7)
#define ATMEL_SPI_CR_LASTXFER BIT(24)
/* Bits in MR */
#define ATMEL_SPI_MR_MSTR BIT(0)
#define ATMEL_SPI_MR_PS BIT(1)
#define ATMEL_SPI_MR_PCSDEC BIT(2)
#define ATMEL_SPI_MR_FDIV BIT(3)
#define ATMEL_SPI_MR_MODFDIS BIT(4)
#define ATMEL_SPI_MR_WDRBT BIT(5)
#define ATMEL_SPI_MR_LLB BIT(7)
#define ATMEL_SPI_MR_PCS(x) (((x) & 15) << 16)
#define ATMEL_SPI_MR_DLYBCS(x) ((x) << 24)
/* Bits in RDR */
#define ATMEL_SPI_RDR_RD(x) (x)
#define ATMEL_SPI_RDR_PCS(x) ((x) << 16)
/* Bits in TDR */
#define ATMEL_SPI_TDR_TD(x) (x)
#define ATMEL_SPI_TDR_PCS(x) ((x) << 16)
#define ATMEL_SPI_TDR_LASTXFER BIT(24)
/* Bits in SR/IER/IDR/IMR */
#define ATMEL_SPI_SR_RDRF BIT(0)
#define ATMEL_SPI_SR_TDRE BIT(1)
#define ATMEL_SPI_SR_MODF BIT(2)
#define ATMEL_SPI_SR_OVRES BIT(3)
#define ATMEL_SPI_SR_ENDRX BIT(4)
#define ATMEL_SPI_SR_ENDTX BIT(5)
#define ATMEL_SPI_SR_RXBUFF BIT(6)
#define ATMEL_SPI_SR_TXBUFE BIT(7)
#define ATMEL_SPI_SR_NSSR BIT(8)
#define ATMEL_SPI_SR_TXEMPTY BIT(9)
#define ATMEL_SPI_SR_SPIENS BIT(16)
/* Bits in CSRx */
#define ATMEL_SPI_CSRx_CPOL BIT(0)
#define ATMEL_SPI_CSRx_NCPHA BIT(1)
#define ATMEL_SPI_CSRx_CSAAT BIT(3)
#define ATMEL_SPI_CSRx_BITS(x) ((x) << 4)
#define ATMEL_SPI_CSRx_SCBR(x) ((x) << 8)
#define ATMEL_SPI_CSRx_SCBR_MAX GENMASK(7, 0)
#define ATMEL_SPI_CSRx_DLYBS(x) ((x) << 16)
#define ATMEL_SPI_CSRx_DLYBCT(x) ((x) << 24)
/* Bits in VERSION */
#define ATMEL_SPI_VERSION_REV(x) ((x) & 0xfff)
#define ATMEL_SPI_VERSION_MFN(x) ((x) << 16)
/* Constants for CSRx:BITS */
#define ATMEL_SPI_BITS_8 0
#define ATMEL_SPI_BITS_9 1
#define ATMEL_SPI_BITS_10 2
#define ATMEL_SPI_BITS_11 3
#define ATMEL_SPI_BITS_12 4
#define ATMEL_SPI_BITS_13 5
#define ATMEL_SPI_BITS_14 6
#define ATMEL_SPI_BITS_15 7
#define ATMEL_SPI_BITS_16 8
struct atmel_spi_slave {
struct spi_slave slave;
void *regs;
u32 mr;
};
static inline struct atmel_spi_slave *to_atmel_spi(struct spi_slave *slave)
{
return container_of(slave, struct atmel_spi_slave, slave);
}
/* Register access macros */
#define spi_readl(as, reg) \
readl(as->regs + ATMEL_SPI_##reg)
#define spi_writel(as, reg, value) \
writel(value, as->regs + ATMEL_SPI_##reg)
#if !defined(CONFIG_SYS_SPI_WRITE_TOUT)
#define CONFIG_SYS_SPI_WRITE_TOUT (5 * CONFIG_SYS_HZ)
#endif

View File

@@ -0,0 +1,309 @@
/*
* Driver for Blackfin On-Chip SPI device
*
* Copyright (c) 2005-2010 Analog Devices Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
/*#define DEBUG*/
#include <common.h>
#include <console.h>
#include <malloc.h>
#include <spi.h>
#include <asm/blackfin.h>
#include <asm/clock.h>
#include <asm/gpio.h>
#include <asm/portmux.h>
#include <asm/mach-common/bits/spi.h>
struct bfin_spi_slave {
struct spi_slave slave;
void *mmr_base;
u16 ctl, baud, flg;
};
#define MAKE_SPI_FUNC(mmr, off) \
static inline void write_##mmr(struct bfin_spi_slave *bss, u16 val) { bfin_write16(bss->mmr_base + off, val); } \
static inline u16 read_##mmr(struct bfin_spi_slave *bss) { return bfin_read16(bss->mmr_base + off); }
MAKE_SPI_FUNC(SPI_CTL, 0x00)
MAKE_SPI_FUNC(SPI_FLG, 0x04)
MAKE_SPI_FUNC(SPI_STAT, 0x08)
MAKE_SPI_FUNC(SPI_TDBR, 0x0c)
MAKE_SPI_FUNC(SPI_RDBR, 0x10)
MAKE_SPI_FUNC(SPI_BAUD, 0x14)
#define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave)
#define gpio_cs(cs) ((cs) - MAX_CTRL_CS)
#ifdef CONFIG_BFIN_SPI_GPIO_CS
# define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS)
#else
# define is_gpio_cs(cs) 0
#endif
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
if (is_gpio_cs(cs))
return gpio_is_valid(gpio_cs(cs));
else
return (cs >= 1 && cs <= MAX_CTRL_CS);
}
void spi_cs_activate(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
if (is_gpio_cs(slave->cs)) {
unsigned int cs = gpio_cs(slave->cs);
gpio_set_value(cs, bss->flg);
debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
} else {
write_SPI_FLG(bss,
(read_SPI_FLG(bss) &
~((!bss->flg << 8) << slave->cs)) |
(1 << slave->cs));
debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss));
}
SSYNC();
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
if (is_gpio_cs(slave->cs)) {
unsigned int cs = gpio_cs(slave->cs);
gpio_set_value(cs, !bss->flg);
debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
} else {
u16 flg;
/* make sure we force the cs to deassert rather than let the
* pin float back up. otherwise, exact timings may not be
* met some of the time leading to random behavior (ugh).
*/
flg = read_SPI_FLG(bss) | ((!bss->flg << 8) << slave->cs);
write_SPI_FLG(bss, flg);
SSYNC();
debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss));
flg &= ~(1 << slave->cs);
write_SPI_FLG(bss, flg);
debug("%s: SPI_FLG:%x\n", __func__, read_SPI_FLG(bss));
}
SSYNC();
}
void spi_init()
{
}
#ifdef SPI_CTL
# define SPI0_CTL SPI_CTL
#endif
#define SPI_PINS(n) \
[n] = { 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 }
static unsigned short pins[][5] = {
#ifdef SPI0_CTL
SPI_PINS(0),
#endif
#ifdef SPI1_CTL
SPI_PINS(1),
#endif
#ifdef SPI2_CTL
SPI_PINS(2),
#endif
};
#define SPI_CS_PINS(n) \
[n] = { \
P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \
P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \
P_SPI##n##_SSEL7, \
}
static const unsigned short cs_pins[][7] = {
#ifdef SPI0_CTL
SPI_CS_PINS(0),
#endif
#ifdef SPI1_CTL
SPI_CS_PINS(1),
#endif
#ifdef SPI2_CTL
SPI_CS_PINS(2),
#endif
};
void spi_set_speed(struct spi_slave *slave, uint hz)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
ulong clk;
u32 baud;
clk = get_spi_clk();
/* baud should be rounded up */
baud = DIV_ROUND_UP(clk, 2 * hz);
if (baud < 2)
baud = 2;
else if (baud > (u16)-1)
baud = -1;
bss->baud = baud;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct bfin_spi_slave *bss;
u32 mmr_base;
if (!spi_cs_is_valid(bus, cs))
return NULL;
switch (bus) {
#ifdef SPI0_CTL
case 0:
mmr_base = SPI0_CTL; break;
#endif
#ifdef SPI1_CTL
case 1:
mmr_base = SPI1_CTL; break;
#endif
#ifdef SPI2_CTL
case 2:
mmr_base = SPI2_CTL; break;
#endif
default:
debug("%s: invalid bus %u\n", __func__, bus);
return NULL;
}
bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs);
if (!bss)
return NULL;
bss->mmr_base = (void *)mmr_base;
bss->ctl = SPE | MSTR | TDBR_CORE;
if (mode & SPI_CPHA) bss->ctl |= CPHA;
if (mode & SPI_CPOL) bss->ctl |= CPOL;
if (mode & SPI_LSB_FIRST) bss->ctl |= LSBF;
bss->flg = mode & SPI_CS_HIGH ? 1 : 0;
spi_set_speed(&bss->slave, max_hz);
debug("%s: bus:%i cs:%i mmr:%x ctl:%x baud:%i flg:%i\n", __func__,
bus, cs, mmr_base, bss->ctl, bss->baud, bss->flg);
return &bss->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
free(bss);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
if (is_gpio_cs(slave->cs)) {
unsigned int cs = gpio_cs(slave->cs);
gpio_request(cs, "bfin-spi");
gpio_direction_output(cs, !bss->flg);
pins[slave->bus][0] = P_DONTCARE;
} else
pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1];
peripheral_request_list(pins[slave->bus], "bfin-spi");
write_SPI_CTL(bss, bss->ctl);
write_SPI_BAUD(bss, bss->baud);
SSYNC();
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
peripheral_free_list(pins[slave->bus]);
if (is_gpio_cs(slave->cs))
gpio_free(gpio_cs(slave->cs));
write_SPI_CTL(bss, 0);
SSYNC();
}
#ifndef CONFIG_BFIN_SPI_IDLE_VAL
# define CONFIG_BFIN_SPI_IDLE_VAL 0xff
#endif
static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx,
uint bytes)
{
/* discard invalid data and clear RXS */
read_SPI_RDBR(bss);
/* todo: take advantage of hardware fifos */
while (bytes--) {
u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL);
debug("%s: tx:%x ", __func__, value);
write_SPI_TDBR(bss, value);
SSYNC();
while ((read_SPI_STAT(bss) & TXS))
if (ctrlc())
return -1;
while (!(read_SPI_STAT(bss) & SPIF))
if (ctrlc())
return -1;
while (!(read_SPI_STAT(bss) & RXS))
if (ctrlc())
return -1;
value = read_SPI_RDBR(bss);
if (rx)
*rx++ = value;
debug("rx:%x\n", value);
}
return 0;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
const u8 *tx = dout;
u8 *rx = din;
uint bytes = bitlen / 8;
int ret = 0;
debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
slave->bus, slave->cs, bitlen, bytes, flags);
if (bitlen == 0)
goto done;
/* we can only do 8 bit transfers */
if (bitlen % 8) {
flags |= SPI_XFER_END;
goto done;
}
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
ret = spi_pio_xfer(bss, tx, rx, bytes);
done:
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return ret;
}

View File

@@ -0,0 +1,305 @@
/*
* Analog Devices SPI3 controller driver
*
* Copyright (c) 2011 Analog Devices Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <common.h>
#include <console.h>
#include <malloc.h>
#include <spi.h>
#include <asm/blackfin.h>
#include <asm/clock.h>
#include <asm/gpio.h>
#include <asm/portmux.h>
#include <asm/mach-common/bits/spi6xx.h>
struct bfin_spi_slave {
struct spi_slave slave;
u32 control, clock;
struct bfin_spi_regs *regs;
int cs_pol;
};
#define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave)
#define gpio_cs(cs) ((cs) - MAX_CTRL_CS)
#ifdef CONFIG_BFIN_SPI_GPIO_CS
# define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS)
#else
# define is_gpio_cs(cs) 0
#endif
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
if (is_gpio_cs(cs))
return gpio_is_valid(gpio_cs(cs));
else
return (cs >= 1 && cs <= MAX_CTRL_CS);
}
void spi_cs_activate(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
if (is_gpio_cs(slave->cs)) {
unsigned int cs = gpio_cs(slave->cs);
gpio_set_value(cs, bss->cs_pol);
} else {
u32 ssel;
ssel = bfin_read32(&bss->regs->ssel);
ssel |= 1 << slave->cs;
if (bss->cs_pol)
ssel |= BIT(8) << slave->cs;
else
ssel &= ~(BIT(8) << slave->cs);
bfin_write32(&bss->regs->ssel, ssel);
}
SSYNC();
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
if (is_gpio_cs(slave->cs)) {
unsigned int cs = gpio_cs(slave->cs);
gpio_set_value(cs, !bss->cs_pol);
} else {
u32 ssel;
ssel = bfin_read32(&bss->regs->ssel);
if (bss->cs_pol)
ssel &= ~(BIT(8) << slave->cs);
else
ssel |= BIT(8) << slave->cs;
/* deassert cs */
bfin_write32(&bss->regs->ssel, ssel);
SSYNC();
/* disable cs */
ssel &= ~(1 << slave->cs);
bfin_write32(&bss->regs->ssel, ssel);
}
SSYNC();
}
void spi_init()
{
}
#define SPI_PINS(n) \
{ 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 }
static unsigned short pins[][5] = {
#ifdef SPI0_REGBASE
[0] = SPI_PINS(0),
#endif
#ifdef SPI1_REGBASE
[1] = SPI_PINS(1),
#endif
#ifdef SPI2_REGBASE
[2] = SPI_PINS(2),
#endif
};
#define SPI_CS_PINS(n) \
{ \
P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \
P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \
P_SPI##n##_SSEL7, \
}
static const unsigned short cs_pins[][7] = {
#ifdef SPI0_REGBASE
[0] = SPI_CS_PINS(0),
#endif
#ifdef SPI1_REGBASE
[1] = SPI_CS_PINS(1),
#endif
#ifdef SPI2_REGBASE
[2] = SPI_CS_PINS(2),
#endif
};
void spi_set_speed(struct spi_slave *slave, uint hz)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
ulong clk;
u32 clock;
clk = get_spi_clk();
clock = clk / hz;
if (clock)
clock--;
bss->clock = clock;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct bfin_spi_slave *bss;
u32 reg_base;
if (!spi_cs_is_valid(bus, cs))
return NULL;
switch (bus) {
#ifdef SPI0_REGBASE
case 0:
reg_base = SPI0_REGBASE;
break;
#endif
#ifdef SPI1_REGBASE
case 1:
reg_base = SPI1_REGBASE;
break;
#endif
#ifdef SPI2_REGBASE
case 2:
reg_base = SPI2_REGBASE;
break;
#endif
default:
debug("%s: invalid bus %u\n", __func__, bus);
return NULL;
}
bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs);
if (!bss)
return NULL;
bss->regs = (struct bfin_spi_regs *)reg_base;
bss->control = SPI_CTL_EN | SPI_CTL_MSTR;
if (mode & SPI_CPHA)
bss->control |= SPI_CTL_CPHA;
if (mode & SPI_CPOL)
bss->control |= SPI_CTL_CPOL;
if (mode & SPI_LSB_FIRST)
bss->control |= SPI_CTL_LSBF;
bss->control &= ~SPI_CTL_ASSEL;
bss->cs_pol = mode & SPI_CS_HIGH ? 1 : 0;
spi_set_speed(&bss->slave, max_hz);
return &bss->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
free(bss);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
if (is_gpio_cs(slave->cs)) {
unsigned int cs = gpio_cs(slave->cs);
gpio_request(cs, "bfin-spi");
gpio_direction_output(cs, !bss->cs_pol);
pins[slave->bus][0] = P_DONTCARE;
} else
pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1];
peripheral_request_list(pins[slave->bus], "bfin-spi");
bfin_write32(&bss->regs->control, bss->control);
bfin_write32(&bss->regs->clock, bss->clock);
bfin_write32(&bss->regs->delay, 0x0);
bfin_write32(&bss->regs->rx_control, SPI_RXCTL_REN);
bfin_write32(&bss->regs->tx_control, SPI_TXCTL_TEN | SPI_TXCTL_TTI);
SSYNC();
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
peripheral_free_list(pins[slave->bus]);
if (is_gpio_cs(slave->cs))
gpio_free(gpio_cs(slave->cs));
bfin_write32(&bss->regs->rx_control, 0x0);
bfin_write32(&bss->regs->tx_control, 0x0);
bfin_write32(&bss->regs->control, 0x0);
SSYNC();
}
#ifndef CONFIG_BFIN_SPI_IDLE_VAL
# define CONFIG_BFIN_SPI_IDLE_VAL 0xff
#endif
static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx,
uint bytes)
{
/* discard invalid rx data and empty rfifo */
while (!(bfin_read32(&bss->regs->status) & SPI_STAT_RFE))
bfin_read32(&bss->regs->rfifo);
while (bytes--) {
u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL);
debug("%s: tx:%x ", __func__, value);
bfin_write32(&bss->regs->tfifo, value);
SSYNC();
while (bfin_read32(&bss->regs->status) & SPI_STAT_RFE)
if (ctrlc())
return -1;
value = bfin_read32(&bss->regs->rfifo);
if (rx)
*rx++ = value;
debug("rx:%x\n", value);
}
return 0;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct bfin_spi_slave *bss = to_bfin_spi_slave(slave);
const u8 *tx = dout;
u8 *rx = din;
uint bytes = bitlen / 8;
int ret = 0;
debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
slave->bus, slave->cs, bitlen, bytes, flags);
if (bitlen == 0)
goto done;
/* we can only do 8 bit transfers */
if (bitlen % 8) {
flags |= SPI_XFER_END;
goto done;
}
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
ret = spi_pio_xfer(bss, tx, rx, bytes);
done:
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return ret;
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright (C) 2012
* Altera Corporation <www.altera.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <asm/errno.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
DECLARE_GLOBAL_DATA_PTR;
static int cadence_spi_write_speed(struct udevice *bus, uint hz)
{
struct cadence_spi_platdata *plat = bus->platdata;
struct cadence_spi_priv *priv = dev_get_priv(bus);
cadence_qspi_apb_config_baudrate_div(priv->regbase,
CONFIG_CQSPI_REF_CLK, hz);
/* Reconfigure delay timing if speed is changed. */
cadence_qspi_apb_delay(priv->regbase, CONFIG_CQSPI_REF_CLK, hz,
plat->tshsl_ns, plat->tsd2d_ns,
plat->tchsh_ns, plat->tslch_ns);
return 0;
}
/* Calibration sequence to determine the read data capture delay register */
static int spi_calibration(struct udevice *bus, uint hz)
{
struct cadence_spi_priv *priv = dev_get_priv(bus);
void *base = priv->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) */
cadence_spi_write_speed(bus, 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 */
cadence_spi_write_speed(bus, 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);
debug("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 */
priv->qspi_calibrated_hz = hz;
priv->qspi_calibrated_cs = spi_chip_select(bus);
return 0;
}
static int cadence_spi_set_speed(struct udevice *bus, uint hz)
{
struct cadence_spi_platdata *plat = bus->platdata;
struct cadence_spi_priv *priv = dev_get_priv(bus);
int err;
if (hz > plat->max_hz)
hz = plat->max_hz;
/* Disable QSPI */
cadence_qspi_apb_controller_disable(priv->regbase);
/*
* Calibration required for different current SCLK speed, requested
* SCLK speed or chip select
*/
if (priv->previous_hz != hz ||
priv->qspi_calibrated_hz != hz ||
priv->qspi_calibrated_cs != spi_chip_select(bus)) {
err = spi_calibration(bus, hz);
if (err)
return err;
/* prevent calibration run when same as previous request */
priv->previous_hz = hz;
}
/* Enable QSPI */
cadence_qspi_apb_controller_enable(priv->regbase);
debug("%s: speed=%d\n", __func__, hz);
return 0;
}
static int cadence_spi_probe(struct udevice *bus)
{
struct cadence_spi_platdata *plat = bus->platdata;
struct cadence_spi_priv *priv = dev_get_priv(bus);
priv->regbase = plat->regbase;
priv->ahbbase = plat->ahbbase;
if (!priv->qspi_is_init) {
cadence_qspi_apb_controller_init(plat);
priv->qspi_is_init = 1;
}
return 0;
}
static int cadence_spi_set_mode(struct udevice *bus, uint mode)
{
struct cadence_spi_priv *priv = dev_get_priv(bus);
unsigned int clk_pol = (mode & SPI_CPOL) ? 1 : 0;
unsigned int clk_pha = (mode & SPI_CPHA) ? 1 : 0;
/* Disable QSPI */
cadence_qspi_apb_controller_disable(priv->regbase);
/* Set SPI mode */
cadence_qspi_apb_set_clk_mode(priv->regbase, clk_pol, clk_pha);
/* Enable QSPI */
cadence_qspi_apb_controller_enable(priv->regbase);
return 0;
}
static int cadence_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct cadence_spi_platdata *plat = bus->platdata;
struct cadence_spi_priv *priv = dev_get_priv(bus);
void *base = priv->regbase;
u8 *cmd_buf = priv->cmd_buf;
size_t data_bytes;
int err = 0;
u32 mode = CQSPI_STIG_WRITE;
if (flags & SPI_XFER_BEGIN) {
/* copy command to local buffer */
priv->cmd_len = bitlen / 8;
memcpy(cmd_buf, dout, priv->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;
}
debug("%s: len=%d [bytes]\n", __func__, data_bytes);
/* Set Chip select */
cadence_qspi_apb_chipselect(base, spi_chip_select(dev),
CONFIG_CQSPI_DECODER);
if ((flags & SPI_XFER_END) || (flags == 0)) {
if (priv->cmd_len == 0) {
printf("QSPI: Error, command is empty.\n");
return -1;
}
if (din && data_bytes) {
/* read */
/* Use STIG if no address. */
if (!CQSPI_IS_ADDR(priv->cmd_len))
mode = CQSPI_STIG_READ;
else
mode = CQSPI_INDIRECT_READ;
} else if (dout && !(flags & SPI_XFER_BEGIN)) {
/* write */
if (!CQSPI_IS_ADDR(priv->cmd_len))
mode = CQSPI_STIG_WRITE;
else
mode = CQSPI_INDIRECT_WRITE;
}
switch (mode) {
case CQSPI_STIG_READ:
err = cadence_qspi_apb_command_read(
base, priv->cmd_len, cmd_buf,
data_bytes, din);
break;
case CQSPI_STIG_WRITE:
err = cadence_qspi_apb_command_write(base,
priv->cmd_len, cmd_buf,
data_bytes, dout);
break;
case CQSPI_INDIRECT_READ:
err = cadence_qspi_apb_indirect_read_setup(plat,
priv->cmd_len, cmd_buf);
if (!err) {
err = cadence_qspi_apb_indirect_read_execute
(plat, data_bytes, din);
}
break;
case CQSPI_INDIRECT_WRITE:
err = cadence_qspi_apb_indirect_write_setup
(plat, priv->cmd_len, cmd_buf);
if (!err) {
err = cadence_qspi_apb_indirect_write_execute
(plat, data_bytes, dout);
}
break;
default:
err = -1;
break;
}
if (flags & SPI_XFER_END) {
/* clear command buffer */
memset(cmd_buf, 0, sizeof(priv->cmd_buf));
priv->cmd_len = 0;
}
}
return err;
}
static int cadence_spi_ofdata_to_platdata(struct udevice *bus)
{
struct cadence_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
int subnode;
u32 data[4];
int ret;
/* 2 base addresses are needed, lets get them from the DT */
ret = fdtdec_get_int_array(blob, node, "reg", data, ARRAY_SIZE(data));
if (ret) {
printf("Error: Can't get base addresses (ret=%d)!\n", ret);
return -ENODEV;
}
plat->regbase = (void *)data[0];
plat->ahbbase = (void *)data[2];
/* All other paramters are embedded in the child node */
subnode = fdt_first_subnode(blob, node);
if (subnode < 0) {
printf("Error: subnode with SPI flash config missing!\n");
return -ENODEV;
}
/* Use 500 KHz as a suitable default */
plat->max_hz = fdtdec_get_uint(blob, subnode, "spi-max-frequency",
500000);
/* Read other parameters from DT */
plat->page_size = fdtdec_get_int(blob, subnode, "page-size", 256);
plat->block_size = fdtdec_get_int(blob, subnode, "block-size", 16);
plat->tshsl_ns = fdtdec_get_int(blob, subnode, "tshsl-ns", 200);
plat->tsd2d_ns = fdtdec_get_int(blob, subnode, "tsd2d-ns", 255);
plat->tchsh_ns = fdtdec_get_int(blob, subnode, "tchsh-ns", 20);
plat->tslch_ns = fdtdec_get_int(blob, subnode, "tslch-ns", 20);
plat->sram_size = fdtdec_get_int(blob, node, "sram-size", 128);
debug("%s: regbase=%p ahbbase=%p max-frequency=%d page-size=%d\n",
__func__, plat->regbase, plat->ahbbase, plat->max_hz,
plat->page_size);
return 0;
}
static const struct dm_spi_ops cadence_spi_ops = {
.xfer = cadence_spi_xfer,
.set_speed = cadence_spi_set_speed,
.set_mode = cadence_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id cadence_spi_ids[] = {
{ .compatible = "cadence,qspi" },
{ }
};
U_BOOT_DRIVER(cadence_spi) = {
.name = "cadence_spi",
.id = UCLASS_SPI,
.of_match = cadence_spi_ids,
.ops = &cadence_spi_ops,
.ofdata_to_platdata = cadence_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct cadence_spi_platdata),
.priv_auto_alloc_size = sizeof(struct cadence_spi_priv),
.probe = cadence_spi_probe,
};

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2012
* Altera Corporation <www.altera.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __CADENCE_QSPI_H__
#define __CADENCE_QSPI_H__
#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
struct cadence_spi_platdata {
unsigned int max_hz;
void *regbase;
void *ahbbase;
u32 page_size;
u32 block_size;
u32 tshsl_ns;
u32 tsd2d_ns;
u32 tchsh_ns;
u32 tslch_ns;
u32 sram_size;
};
struct cadence_spi_priv {
void *regbase;
void *ahbbase;
size_t cmd_len;
u8 cmd_buf[32];
size_t data_len;
int qspi_is_init;
unsigned int qspi_calibrated_hz;
unsigned int qspi_calibrated_cs;
unsigned int previous_hz;
};
/* Functions call declaration */
void cadence_qspi_apb_controller_init(struct cadence_spi_platdata *plat);
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(struct cadence_spi_platdata *plat,
unsigned int cmdlen, const u8 *cmdbuf);
int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat,
unsigned int rxlen, u8 *rxbuf);
int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat,
unsigned int cmdlen, const u8 *cmdbuf);
int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat,
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__ */

View File

@@ -0,0 +1,808 @@
/*
* Copyright (C) 2012 Altera Corporation <www.altera.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - Neither the name of the Altera Corporation nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL ALTERA CORPORATION BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <common.h>
#include <asm/io.h>
#include <asm/errno.h>
#include <wait_bit.h>
#include "cadence_qspi.h"
#define CQSPI_REG_POLL_US (1) /* 1us */
#define CQSPI_REG_RETRY (10000)
#define CQSPI_POLL_IDLE_RETRY (3)
#define CQSPI_FIFO_WIDTH (4)
#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 BIT(0)
#define CQSPI_REG_CONFIG_DIRECT_MASK BIT(7)
#define CQSPI_REG_CONFIG_DECODE_MASK BIT(9)
#define CQSPI_REG_CONFIG_XIP_IMM_MASK BIT(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 BIT(0)
#define CQSPI_REG_INDIRECTRD_CANCEL_MASK BIT(1)
#define CQSPI_REG_INDIRECTRD_INPROGRESS_MASK BIT(2)
#define CQSPI_REG_INDIRECTRD_DONE_MASK BIT(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 BIT(0)
#define CQSPI_REG_CMDCTRL_INPROGRESS_MASK BIT(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 BIT(0)
#define CQSPI_REG_INDIRECTWR_CANCEL_MASK BIT(1)
#define CQSPI_REG_INDIRECTWR_INPROGRESS_MASK BIT(2)
#define CQSPI_REG_INDIRECTWR_DONE_MASK BIT(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) \
((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_base) \
(((readl(reg_base + CQSPI_REG_SDRAMLEVEL)) >> \
CQSPI_REG_SDRAMLEVEL_RD_LSB) & CQSPI_REG_SDRAMLEVEL_RD_MASK)
#define CQSPI_GET_WR_SRAM_LEVEL(reg_base) \
(((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;
}
void cadence_qspi_apb_controller_enable(void *reg_base)
{
unsigned int reg;
reg = readl(reg_base + CQSPI_REG_CONFIG);
reg |= CQSPI_REG_CONFIG_ENABLE_MASK;
writel(reg, reg_base + CQSPI_REG_CONFIG);
return;
}
void cadence_qspi_apb_controller_disable(void *reg_base)
{
unsigned int reg;
reg = readl(reg_base + CQSPI_REG_CONFIG);
reg &= ~CQSPI_REG_CONFIG_ENABLE_MASK;
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 = 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);
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 = 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 {
if (ref_clk_hz % sclk_hz)
/* ensure generated SCLK doesn't exceed user
specified sclk_hz */
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;
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 = 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);
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 = 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;
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);
writel(reg, reg_base + CQSPI_REG_DELAY);
cadence_qspi_apb_controller_enable(reg_base);
return;
}
void cadence_qspi_apb_controller_init(struct cadence_spi_platdata *plat)
{
unsigned reg;
cadence_qspi_apb_controller_disable(plat->regbase);
/* Configure the device size and address bytes */
reg = readl(plat->regbase + 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 |= (plat->page_size << CQSPI_REG_SIZE_PAGE_LSB);
reg |= (plat->block_size << CQSPI_REG_SIZE_BLOCK_LSB);
writel(reg, plat->regbase + CQSPI_REG_SIZE);
/* Configure the remap address register, no remap */
writel(0, plat->regbase + CQSPI_REG_REMAP);
/* Indirect mode configurations */
writel((plat->sram_size/2), plat->regbase + CQSPI_REG_SRAMPARTITION);
/* Disable all interrupts */
writel(0, plat->regbase + CQSPI_REG_IRQMASK);
cadence_qspi_apb_controller_enable(plat->regbase);
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. */
writel(reg, reg_base + CQSPI_REG_CMDCTRL);
/* Start execute */
reg |= CQSPI_REG_CMDCTRL_EXECUTE_MASK;
writel(reg, reg_base + CQSPI_REG_CMDCTRL);
while (retry--) {
reg = 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 = readl(reg_base + CQSPI_REG_CMDREADDATALOWER);
/* Put the read value into rx_buf */
read_len = (rxlen > 4) ? 4 : rxlen;
memcpy(rxbuf, &reg, read_len);
rxbuf += read_len;
if (rxlen > 4) {
reg = readl(reg_base + CQSPI_REG_CMDREADDATAUPPER);
read_len = rxlen - read_len;
memcpy(rxbuf, &reg, 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);
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);
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);
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(struct cadence_spi_platdata *plat,
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 */
writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
/* 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);
writel(addr_value, plat->regbase + 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)
writel(0x0, plat->regbase + CQSPI_REG_MODE_BIT);
#else
writel(0xFF, plat->regbase + 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;
}
writel(rd_reg, plat->regbase + CQSPI_REG_RD_INSTR);
/* set device size */
reg = readl(plat->regbase + CQSPI_REG_SIZE);
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
reg |= (addr_bytes - 1);
writel(reg, plat->regbase + CQSPI_REG_SIZE);
return 0;
}
static u32 cadence_qspi_get_rd_sram_level(struct cadence_spi_platdata *plat)
{
u32 reg = readl(plat->regbase + CQSPI_REG_SDRAMLEVEL);
reg >>= CQSPI_REG_SDRAMLEVEL_RD_LSB;
return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK;
}
static int cadence_qspi_wait_for_data(struct cadence_spi_platdata *plat)
{
unsigned int timeout = 10000;
u32 reg;
while (timeout--) {
reg = cadence_qspi_get_rd_sram_level(plat);
if (reg)
return reg;
udelay(1);
}
return -ETIMEDOUT;
}
int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat,
unsigned int n_rx, u8 *rxbuf)
{
unsigned int remaining = n_rx;
unsigned int bytes_to_read = 0;
int ret;
writel(n_rx, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
/* Start the indirect read transfer */
writel(CQSPI_REG_INDIRECTRD_START_MASK,
plat->regbase + CQSPI_REG_INDIRECTRD);
while (remaining > 0) {
ret = cadence_qspi_wait_for_data(plat);
if (ret < 0) {
printf("Indirect write timed out (%i)\n", ret);
goto failrd;
}
bytes_to_read = ret;
while (bytes_to_read != 0) {
bytes_to_read *= CQSPI_FIFO_WIDTH;
bytes_to_read = bytes_to_read > remaining ?
remaining : bytes_to_read;
/* Handle non-4-byte aligned access to avoid data abort. */
if (((uintptr_t)rxbuf % 4) || (bytes_to_read % 4))
readsb(plat->ahbbase, rxbuf, bytes_to_read);
else
readsl(plat->ahbbase, rxbuf, bytes_to_read >> 2);
rxbuf += bytes_to_read;
remaining -= bytes_to_read;
bytes_to_read = cadence_qspi_get_rd_sram_level(plat);
}
}
/* Check indirect done status */
ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD,
CQSPI_REG_INDIRECTRD_DONE_MASK, 1, 10, 0);
if (ret) {
printf("Indirect read completion error (%i)\n", ret);
goto failrd;
}
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTRD_DONE_MASK,
plat->regbase + CQSPI_REG_INDIRECTRD);
return 0;
failrd:
/* Cancel the indirect read */
writel(CQSPI_REG_INDIRECTRD_CANCEL_MASK,
plat->regbase + CQSPI_REG_INDIRECTRD);
return ret;
}
/* Opcode + Address (3/4 bytes) */
int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat,
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 */
writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
/* Configure the opcode */
reg = cmdbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB;
writel(reg, plat->regbase + CQSPI_REG_WR_INSTR);
/* Setup write address. */
reg = cadence_qspi_apb_cmd2addr(&cmdbuf[1], addr_bytes);
writel(reg, plat->regbase + CQSPI_REG_INDIRECTWRSTARTADDR);
reg = readl(plat->regbase + CQSPI_REG_SIZE);
reg &= ~CQSPI_REG_SIZE_ADDRESS_MASK;
reg |= (addr_bytes - 1);
writel(reg, plat->regbase + CQSPI_REG_SIZE);
return 0;
}
int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat,
unsigned int n_tx, const u8 *txbuf)
{
unsigned int page_size = plat->page_size;
unsigned int remaining = n_tx;
unsigned int write_bytes;
int ret;
/* Configure the indirect read transfer bytes */
writel(n_tx, plat->regbase + CQSPI_REG_INDIRECTWRBYTES);
/* Start the indirect write transfer */
writel(CQSPI_REG_INDIRECTWR_START_MASK,
plat->regbase + CQSPI_REG_INDIRECTWR);
while (remaining > 0) {
write_bytes = remaining > page_size ? page_size : remaining;
/* Handle non-4-byte aligned access to avoid data abort. */
if (((uintptr_t)txbuf % 4) || (write_bytes % 4))
writesb(plat->ahbbase, txbuf, write_bytes);
else
writesl(plat->ahbbase, txbuf, write_bytes >> 2);
ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL,
CQSPI_REG_SDRAMLEVEL_WR_MASK <<
CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0);
if (ret) {
printf("Indirect write timed out (%i)\n", ret);
goto failwr;
}
txbuf += write_bytes;
remaining -= write_bytes;
}
/* Check indirect done status */
ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTWR,
CQSPI_REG_INDIRECTWR_DONE_MASK, 1, 10, 0);
if (ret) {
printf("Indirect write completion error (%i)\n", ret);
goto failwr;
}
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTWR_DONE_MASK,
plat->regbase + CQSPI_REG_INDIRECTWR);
return 0;
failwr:
/* Cancel the indirect write */
writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK,
plat->regbase + CQSPI_REG_INDIRECTWR);
return ret;
}
void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy)
{
unsigned int reg;
/* enter XiP mode immediately and enable direct mode */
reg = 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;
writel(reg, reg_base + CQSPI_REG_CONFIG);
/* keep the XiP mode */
writel(xip_dummy, reg_base + CQSPI_REG_MODE_BIT);
/* Enable mode bit at devrd */
reg = readl(reg_base + CQSPI_REG_RD_INSTR);
reg |= (1 << CQSPI_REG_RD_INSTR_MODE_EN_LSB);
writel(reg, reg_base + CQSPI_REG_RD_INSTR);
}

View File

@@ -0,0 +1,354 @@
/*
* Freescale Coldfire Queued SPI driver
*
* NOTE:
* This driver is written to transfer 8 bit at-a-time and uses the dedicated
* SPI slave select pins as bit-banged GPIO to work with spi_flash subsystem.
*
* Copyright (C) 2011 Ruggedcom, Inc.
* Richard Retanubun (richardretanubun@freescale.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/immap.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
#define to_cf_qspi_slave(s) container_of(s, struct cf_qspi_slave, slave)
struct cf_qspi_slave {
struct spi_slave slave; /* Specific bus:cs ID for each device */
qspi_t *regs; /* Pointer to SPI controller registers */
u16 qmr; /* QMR: Queued Mode Register */
u16 qwr; /* QWR: Queued Wrap Register */
u16 qcr; /* QCR: Queued Command Ram */
};
/* Register write wrapper functions */
static void write_qmr(volatile qspi_t *qspi, u16 val) { qspi->mr = val; }
static void write_qdlyr(volatile qspi_t *qspi, u16 val) { qspi->dlyr = val; }
static void write_qwr(volatile qspi_t *qspi, u16 val) { qspi->wr = val; }
static void write_qir(volatile qspi_t *qspi, u16 val) { qspi->ir = val; }
static void write_qar(volatile qspi_t *qspi, u16 val) { qspi->ar = val; }
static void write_qdr(volatile qspi_t *qspi, u16 val) { qspi->dr = val; }
/* Register read wrapper functions */
static u16 read_qdlyr(volatile qspi_t *qspi) { return qspi->dlyr; }
static u16 read_qwr(volatile qspi_t *qspi) { return qspi->wr; }
static u16 read_qir(volatile qspi_t *qspi) { return qspi->ir; }
static u16 read_qdr(volatile qspi_t *qspi) { return qspi->dr; }
/* These call points may be different for each ColdFire CPU */
extern void cfspi_port_conf(void);
static void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high);
static void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high);
int spi_claim_bus(struct spi_slave *slave)
{
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
__attribute__((weak))
void spi_init(void)
{
cfspi_port_conf();
}
__attribute__((weak))
void spi_cs_activate(struct spi_slave *slave)
{
struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
cfspi_cs_activate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV));
}
__attribute__((weak))
void spi_cs_deactivate(struct spi_slave *slave)
{
struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
cfspi_cs_deactivate(slave->bus, slave->cs, !(dev->qwr & QSPI_QWR_CSIV));
}
__attribute__((weak))
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
/* Only 1 bus and 4 chipselect per controller */
if (bus == 0 && (cs >= 0 && cs < 4))
return 1;
else
return 0;
}
void spi_free_slave(struct spi_slave *slave)
{
struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
free(dev);
}
/* Translate information given by spi_setup_slave to members of cf_qspi_slave */
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct cf_qspi_slave *dev = NULL;
if (!spi_cs_is_valid(bus, cs))
return NULL;
dev = spi_alloc_slave(struct cf_qspi_slave, bus, cs);
if (!dev)
return NULL;
/* Initialize to known value */
dev->regs = (qspi_t *)MMAP_QSPI;
dev->qmr = 0;
dev->qwr = 0;
dev->qcr = 0;
/* Map max_hz to QMR[BAUD] */
if (max_hz == 0) /* Go as fast as possible */
dev->qmr = 2u;
else /* Get the closest baud rate */
dev->qmr = clamp(((gd->bus_clk >> 2) + max_hz - 1)/max_hz,
2lu, 255lu);
/* Map mode to QMR[CPOL] and QMR[CPHA] */
if (mode & SPI_CPOL)
dev->qmr |= QSPI_QMR_CPOL;
if (mode & SPI_CPHA)
dev->qmr |= QSPI_QMR_CPHA;
/* Hardcode bit length to 8 bit per transter */
dev->qmr |= QSPI_QMR_BITS_8;
/* Set QMR[MSTR] to enable QSPI as master */
dev->qmr |= QSPI_QMR_MSTR;
/*
* Set QCR and QWR to default values for spi flash operation.
* If more custom QCR and QRW are needed, overload mode variable
*/
dev->qcr = (QSPI_QDR_CONT | QSPI_QDR_BITSE);
if (!(mode & SPI_CS_HIGH))
dev->qwr |= QSPI_QWR_CSIV;
return &dev->slave;
}
/* Transfer 8 bit at a time */
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct cf_qspi_slave *dev = to_cf_qspi_slave(slave);
volatile qspi_t *qspi = dev->regs;
u8 *txbuf = (u8 *)dout;
u8 *rxbuf = (u8 *)din;
u32 count = DIV_ROUND_UP(bitlen, 8);
u32 n, i = 0;
/* Sanitize arguments */
if (slave == NULL) {
printf("%s: NULL slave ptr\n", __func__);
return -1;
}
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
/* There is something to send, lets process it. spi_xfer is also called
* just to toggle chip select, so bitlen of 0 is valid */
if (count > 0) {
/*
* NOTE: Since chip select is driven as a bit-bang-ed GPIO
* using spi_cs_activate() and spi_cs_deactivate(),
* the chip select settings inside the controller
* (i.e. QCR[CONT] and QWR[CSIV]) are moot. The bits are set to
* keep the controller settings consistent with the actual
* operation of the bus.
*/
/* Write the slave device's settings for the controller.*/
write_qmr(qspi, dev->qmr);
write_qwr(qspi, dev->qwr);
/* Limit transfer to 16 at a time */
n = min(count, 16u);
do {
/* Setup queue end point */
write_qwr(qspi, ((read_qwr(qspi) & QSPI_QWR_ENDQP_MASK)
| QSPI_QWR_ENDQP((n-1))));
/* Write Command RAM */
write_qar(qspi, QSPI_QAR_CMD);
for (i = 0; i < n; ++i)
write_qdr(qspi, dev->qcr);
/* Write TxBuf, if none given, fill with ZEROes */
write_qar(qspi, QSPI_QAR_TRANS);
if (txbuf) {
for (i = 0; i < n; ++i)
write_qdr(qspi, *txbuf++);
} else {
for (i = 0; i < n; ++i)
write_qdr(qspi, 0);
}
/* Clear QIR[SPIF] by writing a 1 to it */
write_qir(qspi, read_qir(qspi) | QSPI_QIR_SPIF);
/* Set QDLYR[SPE] to start sending */
write_qdlyr(qspi, read_qdlyr(qspi) | QSPI_QDLYR_SPE);
/* Poll QIR[SPIF] for transfer completion */
while ((read_qir(qspi) & QSPI_QIR_SPIF) != 1)
udelay(1);
/* If given read RxBuf, load data to it */
if (rxbuf) {
write_qar(qspi, QSPI_QAR_RECV);
for (i = 0; i < n; ++i)
*rxbuf++ = read_qdr(qspi);
}
/* Decrement count */
count -= n;
} while (count);
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return 0;
}
/* Each MCF CPU may have different pin assignments for chip selects. */
#if defined(CONFIG_M5271)
/* Assert chip select, val = [1|0] , dir = out, mode = GPIO */
void cfspi_cs_activate(uint bus, uint cs, uint cs_active_high)
{
debug("%s: bus %d cs %d cs_active_high %d\n",
__func__, bus, cs, cs_active_high);
switch (cs) {
case 0: /* QSPI_CS[0] = PQSPI[3] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08);
else
mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7);
mbar_writeByte(MCF_GPIO_PDDR_QSPI,
mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x08);
mbar_writeByte(MCF_GPIO_PAR_QSPI,
mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF);
break;
case 1: /* QSPI_CS[1] = PQSPI[4] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10);
else
mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF);
mbar_writeByte(MCF_GPIO_PDDR_QSPI,
mbar_readByte(MCF_GPIO_PDDR_QSPI) | 0x10);
mbar_writeByte(MCF_GPIO_PAR_QSPI,
mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F);
break;
case 2: /* QSPI_CS[2] = PTIMER[7] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80);
else
mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F);
mbar_writeByte(MCF_GPIO_PDDR_TIMER,
mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x80);
mbar_writeShort(MCF_GPIO_PAR_TIMER,
mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF);
break;
case 3: /* QSPI_CS[3] = PTIMER[3] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08);
else
mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7);
mbar_writeByte(MCF_GPIO_PDDR_TIMER,
mbar_readByte(MCF_GPIO_PDDR_TIMER) | 0x08);
mbar_writeShort(MCF_GPIO_PAR_TIMER,
mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F);
break;
}
}
/* Deassert chip select, val = [1|0], dir = in, mode = GPIO
* direction set as IN to undrive the pin, external pullup/pulldown will bring
* bus to deassert state.
*/
void cfspi_cs_deactivate(uint bus, uint cs, uint cs_active_high)
{
debug("%s: bus %d cs %d cs_active_high %d\n",
__func__, bus, cs, cs_active_high);
switch (cs) {
case 0: /* QSPI_CS[0] = PQSPI[3] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xF7);
else
mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x08);
mbar_writeByte(MCF_GPIO_PDDR_QSPI,
mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xF7);
mbar_writeByte(MCF_GPIO_PAR_QSPI,
mbar_readByte(MCF_GPIO_PAR_QSPI) & 0xDF);
break;
case 1: /* QSPI_CS[1] = PQSPI[4] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PCLRR_QSPI, 0xEF);
else
mbar_writeByte(MCF_GPIO_PPDSDR_QSPI, 0x10);
mbar_writeByte(MCF_GPIO_PDDR_QSPI,
mbar_readByte(MCF_GPIO_PDDR_QSPI) & 0xEF);
mbar_writeByte(MCF_GPIO_PAR_QSPI,
mbar_readByte(MCF_GPIO_PAR_QSPI) & 0x3F);
break;
case 2: /* QSPI_CS[2] = PTIMER[7] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0x7F);
else
mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x80);
mbar_writeByte(MCF_GPIO_PDDR_TIMER,
mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0x7F);
mbar_writeShort(MCF_GPIO_PAR_TIMER,
mbar_readShort(MCF_GPIO_PAR_TIMER) & 0x3FFF);
break;
case 3: /* QSPI_CS[3] = PTIMER[3] */
if (cs_active_high)
mbar_writeByte(MCF_GPIO_PCLRR_TIMER, 0xF7);
else
mbar_writeByte(MCF_GPIO_PPDSDR_TIMER, 0x08);
mbar_writeByte(MCF_GPIO_PDDR_TIMER,
mbar_readByte(MCF_GPIO_PDDR_TIMER) & 0xF7);
mbar_writeShort(MCF_GPIO_PAR_TIMER,
mbar_readShort(MCF_GPIO_PAR_TIMER) & 0xFF3F);
break;
}
}
#endif /* CONFIG_M5271 */

348
u-boot/drivers/spi/cf_spi.c Normal file
View File

@@ -0,0 +1,348 @@
/*
*
* (C) Copyright 2000-2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* Copyright (C) 2004-2009 Freescale Semiconductor, Inc.
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spi.h>
#include <malloc.h>
#include <asm/immap.h>
struct cf_spi_slave {
struct spi_slave slave;
uint baudrate;
int charbit;
};
extern void cfspi_port_conf(void);
extern int cfspi_claim_bus(uint bus, uint cs);
extern void cfspi_release_bus(uint bus, uint cs);
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_SPI_IDLE_VAL
#if defined(CONFIG_SPI_MMC)
#define CONFIG_SPI_IDLE_VAL 0xFFFF
#else
#define CONFIG_SPI_IDLE_VAL 0x0
#endif
#endif
#if defined(CONFIG_CF_DSPI)
/* DSPI specific mode */
#define SPI_MODE_MOD 0x00200000
#define SPI_DBLRATE 0x00100000
static inline struct cf_spi_slave *to_cf_spi_slave(struct spi_slave *slave)
{
return container_of(slave, struct cf_spi_slave, slave);
}
static void cfspi_init(void)
{
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
cfspi_port_conf(); /* port configuration */
dspi->mcr = DSPI_MCR_MSTR | DSPI_MCR_CSIS7 | DSPI_MCR_CSIS6 |
DSPI_MCR_CSIS5 | DSPI_MCR_CSIS4 | DSPI_MCR_CSIS3 |
DSPI_MCR_CSIS2 | DSPI_MCR_CSIS1 | DSPI_MCR_CSIS0 |
DSPI_MCR_CRXF | DSPI_MCR_CTXF;
/* Default setting in platform configuration */
#ifdef CONFIG_SYS_DSPI_CTAR0
dspi->ctar[0] = CONFIG_SYS_DSPI_CTAR0;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR1
dspi->ctar[1] = CONFIG_SYS_DSPI_CTAR1;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR2
dspi->ctar[2] = CONFIG_SYS_DSPI_CTAR2;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR3
dspi->ctar[3] = CONFIG_SYS_DSPI_CTAR3;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR4
dspi->ctar[4] = CONFIG_SYS_DSPI_CTAR4;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR5
dspi->ctar[5] = CONFIG_SYS_DSPI_CTAR5;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR6
dspi->ctar[6] = CONFIG_SYS_DSPI_CTAR6;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR7
dspi->ctar[7] = CONFIG_SYS_DSPI_CTAR7;
#endif
}
static void cfspi_tx(u32 ctrl, u16 data)
{
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
while ((dspi->sr & 0x0000F000) >= 4) ;
dspi->tfr = (ctrl | data);
}
static u16 cfspi_rx(void)
{
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
while ((dspi->sr & 0x000000F0) == 0) ;
return (dspi->rfr & 0xFFFF);
}
static int cfspi_xfer(struct spi_slave *slave, uint bitlen, const void *dout,
void *din, ulong flags)
{
struct cf_spi_slave *cfslave = to_cf_spi_slave(slave);
u16 *spi_rd16 = NULL, *spi_wr16 = NULL;
u8 *spi_rd = NULL, *spi_wr = NULL;
static u32 ctrl = 0;
uint len = bitlen >> 3;
if (cfslave->charbit == 16) {
bitlen >>= 1;
spi_wr16 = (u16 *) dout;
spi_rd16 = (u16 *) din;
} else {
spi_wr = (u8 *) dout;
spi_rd = (u8 *) din;
}
if ((flags & SPI_XFER_BEGIN) == SPI_XFER_BEGIN)
ctrl |= DSPI_TFR_CONT;
ctrl = (ctrl & 0xFF000000) | ((1 << slave->cs) << 16);
if (len > 1) {
int tmp_len = len - 1;
while (tmp_len--) {
if (dout != NULL) {
if (cfslave->charbit == 16)
cfspi_tx(ctrl, *spi_wr16++);
else
cfspi_tx(ctrl, *spi_wr++);
cfspi_rx();
}
if (din != NULL) {
cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL);
if (cfslave->charbit == 16)
*spi_rd16++ = cfspi_rx();
else
*spi_rd++ = cfspi_rx();
}
}
len = 1; /* remaining byte */
}
if ((flags & SPI_XFER_END) == SPI_XFER_END)
ctrl &= ~DSPI_TFR_CONT;
if (len) {
if (dout != NULL) {
if (cfslave->charbit == 16)
cfspi_tx(ctrl, *spi_wr16);
else
cfspi_tx(ctrl, *spi_wr);
cfspi_rx();
}
if (din != NULL) {
cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL);
if (cfslave->charbit == 16)
*spi_rd16 = cfspi_rx();
else
*spi_rd = cfspi_rx();
}
} else {
/* dummy read */
cfspi_tx(ctrl, CONFIG_SPI_IDLE_VAL);
cfspi_rx();
}
return 0;
}
static struct spi_slave *cfspi_setup_slave(struct cf_spi_slave *cfslave,
uint mode)
{
/*
* bit definition for mode:
* bit 31 - 28: Transfer size 3 to 16 bits
* 27 - 26: PCS to SCK delay prescaler
* 25 - 24: After SCK delay prescaler
* 23 - 22: Delay after transfer prescaler
* 21 : Allow overwrite for bit 31-22 and bit 20-8
* 20 : Double baud rate
* 19 - 16: PCS to SCK delay scaler
* 15 - 12: After SCK delay scaler
* 11 - 8: Delay after transfer scaler
* 7 - 0: SPI_CPHA, SPI_CPOL, SPI_LSB_FIRST
*/
volatile dspi_t *dspi = (dspi_t *) MMAP_DSPI;
int prescaler[] = { 2, 3, 5, 7 };
int scaler[] = {
2, 4, 6, 8,
16, 32, 64, 128,
256, 512, 1024, 2048,
4096, 8192, 16384, 32768
};
int i, j, pbrcnt, brcnt, diff, tmp, dbr = 0;
int best_i, best_j, bestmatch = 0x7FFFFFFF, baud_speed;
u32 bus_setup = 0;
tmp = (prescaler[3] * scaler[15]);
/* Maximum and minimum baudrate it can handle */
if ((cfslave->baudrate > (gd->bus_clk >> 1)) ||
(cfslave->baudrate < (gd->bus_clk / tmp))) {
printf("Exceed baudrate limitation: Max %d - Min %d\n",
(int)(gd->bus_clk >> 1), (int)(gd->bus_clk / tmp));
return NULL;
}
/* Activate Double Baud when it exceed 1/4 the bus clk */
if ((CONFIG_SYS_DSPI_CTAR0 & DSPI_CTAR_DBR) ||
(cfslave->baudrate > (gd->bus_clk / (prescaler[0] * scaler[0])))) {
bus_setup |= DSPI_CTAR_DBR;
dbr = 1;
}
if (mode & SPI_CPOL)
bus_setup |= DSPI_CTAR_CPOL;
if (mode & SPI_CPHA)
bus_setup |= DSPI_CTAR_CPHA;
if (mode & SPI_LSB_FIRST)
bus_setup |= DSPI_CTAR_LSBFE;
/* Overwrite default value set in platform configuration file */
if (mode & SPI_MODE_MOD) {
if ((mode & 0xF0000000) == 0)
bus_setup |=
dspi->ctar[cfslave->slave.bus] & 0x78000000;
else
bus_setup |= ((mode & 0xF0000000) >> 1);
/*
* Check to see if it is enabled by default in platform
* config, or manual setting passed by mode parameter
*/
if (mode & SPI_DBLRATE) {
bus_setup |= DSPI_CTAR_DBR;
dbr = 1;
}
bus_setup |= (mode & 0x0FC00000) >> 4; /* PSCSCK, PASC, PDT */
bus_setup |= (mode & 0x000FFF00) >> 4; /* CSSCK, ASC, DT */
} else
bus_setup |= (dspi->ctar[cfslave->slave.bus] & 0x78FCFFF0);
cfslave->charbit =
((dspi->ctar[cfslave->slave.bus] & 0x78000000) ==
0x78000000) ? 16 : 8;
pbrcnt = sizeof(prescaler) / sizeof(int);
brcnt = sizeof(scaler) / sizeof(int);
/* baudrate calculation - to closer value, may not be exact match */
for (best_i = 0, best_j = 0, i = 0; i < pbrcnt; i++) {
baud_speed = gd->bus_clk / prescaler[i];
for (j = 0; j < brcnt; j++) {
tmp = (baud_speed / scaler[j]) * (1 + dbr);
if (tmp > cfslave->baudrate)
diff = tmp - cfslave->baudrate;
else
diff = cfslave->baudrate - tmp;
if (diff < bestmatch) {
bestmatch = diff;
best_i = i;
best_j = j;
}
}
}
bus_setup |= (DSPI_CTAR_PBR(best_i) | DSPI_CTAR_BR(best_j));
dspi->ctar[cfslave->slave.bus] = bus_setup;
return &cfslave->slave;
}
#endif /* CONFIG_CF_DSPI */
#ifdef CONFIG_CF_QSPI
/* 52xx, 53xx */
#endif /* CONFIG_CF_QSPI */
#ifdef CONFIG_CMD_SPI
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
if (((cs >= 0) && (cs < 8)) && ((bus >= 0) && (bus < 8)))
return 1;
else
return 0;
}
void spi_init_f(void)
{
}
void spi_init_r(void)
{
}
void spi_init(void)
{
cfspi_init();
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct cf_spi_slave *cfslave;
if (!spi_cs_is_valid(bus, cs))
return NULL;
cfslave = spi_alloc_slave(struct cf_spi_slave, bus, cs);
if (!cfslave)
return NULL;
cfslave->baudrate = max_hz;
/* specific setup */
return cfspi_setup_slave(cfslave, mode);
}
void spi_free_slave(struct spi_slave *slave)
{
struct cf_spi_slave *cfslave = to_cf_spi_slave(slave);
free(cfslave);
}
int spi_claim_bus(struct spi_slave *slave)
{
return cfspi_claim_bus(slave->bus, slave->cs);
}
void spi_release_bus(struct spi_slave *slave)
{
cfspi_release_bus(slave->bus, slave->cs);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
return cfspi_xfer(slave, bitlen, dout, din, flags);
}
#endif /* CONFIG_CMD_SPI */

View File

@@ -0,0 +1,427 @@
/*
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* Driver for SPI controller on DaVinci. Based on atmel_spi.c
* by Atmel Corporation
*
* Copyright (C) 2007 Atmel Corporation
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
/* SPIGCR0 */
#define SPIGCR0_SPIENA_MASK 0x1
#define SPIGCR0_SPIRST_MASK 0x0
/* SPIGCR0 */
#define SPIGCR1_CLKMOD_MASK BIT(1)
#define SPIGCR1_MASTER_MASK BIT(0)
#define SPIGCR1_SPIENA_MASK BIT(24)
/* SPIPC0 */
#define SPIPC0_DIFUN_MASK BIT(11) /* SIMO */
#define SPIPC0_DOFUN_MASK BIT(10) /* SOMI */
#define SPIPC0_CLKFUN_MASK BIT(9) /* CLK */
#define SPIPC0_EN0FUN_MASK BIT(0)
/* SPIFMT0 */
#define SPIFMT_SHIFTDIR_SHIFT 20
#define SPIFMT_POLARITY_SHIFT 17
#define SPIFMT_PHASE_SHIFT 16
#define SPIFMT_PRESCALE_SHIFT 8
/* SPIDAT1 */
#define SPIDAT1_CSHOLD_SHIFT 28
#define SPIDAT1_CSNR_SHIFT 16
/* SPIDELAY */
#define SPI_C2TDELAY_SHIFT 24
#define SPI_T2CDELAY_SHIFT 16
/* SPIBUF */
#define SPIBUF_RXEMPTY_MASK BIT(31)
#define SPIBUF_TXFULL_MASK BIT(29)
/* SPIDEF */
#define SPIDEF_CSDEF0_MASK BIT(0)
#define SPI0_BUS 0
#define SPI0_BASE CONFIG_SYS_SPI_BASE
/*
* Define default SPI0_NUM_CS as 1 for existing platforms that uses this
* driver. Platform can configure number of CS using CONFIG_SYS_SPI0_NUM_CS
* if more than one CS is supported and by defining CONFIG_SYS_SPI0.
*/
#ifndef CONFIG_SYS_SPI0
#define SPI0_NUM_CS 1
#else
#define SPI0_NUM_CS CONFIG_SYS_SPI0_NUM_CS
#endif
/*
* define CONFIG_SYS_SPI1 when platform has spi-1 device (bus #1) and
* CONFIG_SYS_SPI1_NUM_CS defines number of CS on this bus
*/
#ifdef CONFIG_SYS_SPI1
#define SPI1_BUS 1
#define SPI1_NUM_CS CONFIG_SYS_SPI1_NUM_CS
#define SPI1_BASE CONFIG_SYS_SPI1_BASE
#endif
/*
* define CONFIG_SYS_SPI2 when platform has spi-2 device (bus #2) and
* CONFIG_SYS_SPI2_NUM_CS defines number of CS on this bus
*/
#ifdef CONFIG_SYS_SPI2
#define SPI2_BUS 2
#define SPI2_NUM_CS CONFIG_SYS_SPI2_NUM_CS
#define SPI2_BASE CONFIG_SYS_SPI2_BASE
#endif
/* davinci spi register set */
struct davinci_spi_regs {
dv_reg gcr0; /* 0x00 */
dv_reg gcr1; /* 0x04 */
dv_reg int0; /* 0x08 */
dv_reg lvl; /* 0x0c */
dv_reg flg; /* 0x10 */
dv_reg pc0; /* 0x14 */
dv_reg pc1; /* 0x18 */
dv_reg pc2; /* 0x1c */
dv_reg pc3; /* 0x20 */
dv_reg pc4; /* 0x24 */
dv_reg pc5; /* 0x28 */
dv_reg rsvd[3];
dv_reg dat0; /* 0x38 */
dv_reg dat1; /* 0x3c */
dv_reg buf; /* 0x40 */
dv_reg emu; /* 0x44 */
dv_reg delay; /* 0x48 */
dv_reg def; /* 0x4c */
dv_reg fmt0; /* 0x50 */
dv_reg fmt1; /* 0x54 */
dv_reg fmt2; /* 0x58 */
dv_reg fmt3; /* 0x5c */
dv_reg intvec0; /* 0x60 */
dv_reg intvec1; /* 0x64 */
};
/* davinci spi slave */
struct davinci_spi_slave {
struct spi_slave slave;
struct davinci_spi_regs *regs;
unsigned int freq;
};
static inline struct davinci_spi_slave *to_davinci_spi(struct spi_slave *slave)
{
return container_of(slave, struct davinci_spi_slave, slave);
}
/*
* This functions needs to act like a macro to avoid pipeline reloads in the
* loops below. Use always_inline. This gains us about 160KiB/s and the bloat
* appears to be zero bytes (da830).
*/
__attribute__((always_inline))
static inline u32 davinci_spi_xfer_data(struct davinci_spi_slave *ds, u32 data)
{
u32 buf_reg_val;
/* send out data */
writel(data, &ds->regs->dat1);
/* wait for the data to clock in/out */
while ((buf_reg_val = readl(&ds->regs->buf)) & SPIBUF_RXEMPTY_MASK)
;
return buf_reg_val;
}
static int davinci_spi_read(struct spi_slave *slave, unsigned int len,
u8 *rxp, unsigned long flags)
{
struct davinci_spi_slave *ds = to_davinci_spi(slave);
unsigned int data1_reg_val;
/* enable CS hold, CS[n] and clear the data bits */
data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) |
(slave->cs << SPIDAT1_CSNR_SHIFT));
/* wait till TXFULL is deasserted */
while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK)
;
/* preload the TX buffer to avoid clock starvation */
writel(data1_reg_val, &ds->regs->dat1);
/* keep reading 1 byte until only 1 byte left */
while ((len--) > 1)
*rxp++ = davinci_spi_xfer_data(ds, data1_reg_val);
/* clear CS hold when we reach the end */
if (flags & SPI_XFER_END)
data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT);
/* read the last byte */
*rxp = davinci_spi_xfer_data(ds, data1_reg_val);
return 0;
}
static int davinci_spi_write(struct spi_slave *slave, unsigned int len,
const u8 *txp, unsigned long flags)
{
struct davinci_spi_slave *ds = to_davinci_spi(slave);
unsigned int data1_reg_val;
/* enable CS hold and clear the data bits */
data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) |
(slave->cs << SPIDAT1_CSNR_SHIFT));
/* wait till TXFULL is deasserted */
while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK)
;
/* preload the TX buffer to avoid clock starvation */
if (len > 2) {
writel(data1_reg_val | *txp++, &ds->regs->dat1);
len--;
}
/* keep writing 1 byte until only 1 byte left */
while ((len--) > 1)
davinci_spi_xfer_data(ds, data1_reg_val | *txp++);
/* clear CS hold when we reach the end */
if (flags & SPI_XFER_END)
data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT);
/* write the last byte */
davinci_spi_xfer_data(ds, data1_reg_val | *txp);
return 0;
}
#ifndef CONFIG_SPI_HALF_DUPLEX
static int davinci_spi_read_write(struct spi_slave *slave, unsigned int len,
u8 *rxp, const u8 *txp, unsigned long flags)
{
struct davinci_spi_slave *ds = to_davinci_spi(slave);
unsigned int data1_reg_val;
/* enable CS hold and clear the data bits */
data1_reg_val = ((1 << SPIDAT1_CSHOLD_SHIFT) |
(slave->cs << SPIDAT1_CSNR_SHIFT));
/* wait till TXFULL is deasserted */
while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK)
;
/* keep reading and writing 1 byte until only 1 byte left */
while ((len--) > 1)
*rxp++ = davinci_spi_xfer_data(ds, data1_reg_val | *txp++);
/* clear CS hold when we reach the end */
if (flags & SPI_XFER_END)
data1_reg_val &= ~(1 << SPIDAT1_CSHOLD_SHIFT);
/* read and write the last byte */
*rxp = davinci_spi_xfer_data(ds, data1_reg_val | *txp);
return 0;
}
#endif
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
int ret = 0;
switch (bus) {
case SPI0_BUS:
if (cs < SPI0_NUM_CS)
ret = 1;
break;
#ifdef CONFIG_SYS_SPI1
case SPI1_BUS:
if (cs < SPI1_NUM_CS)
ret = 1;
break;
#endif
#ifdef CONFIG_SYS_SPI2
case SPI2_BUS:
if (cs < SPI2_NUM_CS)
ret = 1;
break;
#endif
default:
/* Invalid bus number. Do nothing */
break;
}
return ret;
}
void spi_cs_activate(struct spi_slave *slave)
{
/* do nothing */
}
void spi_cs_deactivate(struct spi_slave *slave)
{
/* do nothing */
}
void spi_init(void)
{
/* do nothing */
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct davinci_spi_slave *ds;
if (!spi_cs_is_valid(bus, cs))
return NULL;
ds = spi_alloc_slave(struct davinci_spi_slave, bus, cs);
if (!ds)
return NULL;
switch (bus) {
case SPI0_BUS:
ds->regs = (struct davinci_spi_regs *)SPI0_BASE;
break;
#ifdef CONFIG_SYS_SPI1
case SPI1_BUS:
ds->regs = (struct davinci_spi_regs *)SPI1_BASE;
break;
#endif
#ifdef CONFIG_SYS_SPI2
case SPI2_BUS:
ds->regs = (struct davinci_spi_regs *)SPI2_BASE;
break;
#endif
default: /* Invalid bus number */
return NULL;
}
ds->freq = max_hz;
return &ds->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct davinci_spi_slave *ds = to_davinci_spi(slave);
free(ds);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct davinci_spi_slave *ds = to_davinci_spi(slave);
unsigned int scalar;
/* Enable the SPI hardware */
writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0);
udelay(1000);
writel(SPIGCR0_SPIENA_MASK, &ds->regs->gcr0);
/* Set master mode, powered up and not activated */
writel(SPIGCR1_MASTER_MASK | SPIGCR1_CLKMOD_MASK, &ds->regs->gcr1);
/* CS, CLK, SIMO and SOMI are functional pins */
writel(((1 << slave->cs) | SPIPC0_CLKFUN_MASK |
SPIPC0_DOFUN_MASK | SPIPC0_DIFUN_MASK), &ds->regs->pc0);
/* setup format */
scalar = ((CONFIG_SYS_SPI_CLK / ds->freq) - 1) & 0xFF;
/*
* Use following format:
* character length = 8,
* clock signal delayed by half clk cycle,
* clock low in idle state - Mode 0,
* MSB shifted out first
*/
writel(8 | (scalar << SPIFMT_PRESCALE_SHIFT) |
(1 << SPIFMT_PHASE_SHIFT), &ds->regs->fmt0);
/*
* Including a minor delay. No science here. Should be good even with
* no delay
*/
writel((50 << SPI_C2TDELAY_SHIFT) |
(50 << SPI_T2CDELAY_SHIFT), &ds->regs->delay);
/* default chip select register */
writel(SPIDEF_CSDEF0_MASK, &ds->regs->def);
/* no interrupts */
writel(0, &ds->regs->int0);
writel(0, &ds->regs->lvl);
/* enable SPI */
writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct davinci_spi_slave *ds = to_davinci_spi(slave);
/* Disable the SPI hardware */
writel(SPIGCR0_SPIRST_MASK, &ds->regs->gcr0);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
unsigned int len;
if (bitlen == 0)
/* Finish any previously submitted transfers */
goto out;
/*
* It's not clear how non-8-bit-aligned transfers are supposed to be
* represented as a stream of bytes...this is a limitation of
* the current SPI interface - here we terminate on receiving such a
* transfer request.
*/
if (bitlen % 8) {
/* Errors always terminate an ongoing transfer */
flags |= SPI_XFER_END;
goto out;
}
len = bitlen / 8;
if (!dout)
return davinci_spi_read(slave, len, din, flags);
else if (!din)
return davinci_spi_write(slave, len, dout, flags);
#ifndef CONFIG_SPI_HALF_DUPLEX
else
return davinci_spi_read_write(slave, len, din, dout, flags);
#else
printf("SPI full duplex transaction requested with "
"CONFIG_SPI_HALF_DUPLEX defined.\n");
flags |= SPI_XFER_END;
#endif
out:
if (flags & SPI_XFER_END) {
u8 dummy = 0;
davinci_spi_write(slave, 1, &dummy, flags);
}
return 0;
}

View File

@@ -0,0 +1,425 @@
/*
* Designware master SPI core controller driver
*
* Copyright (C) 2014 Stefan Roese <sr@denx.de>
*
* Very loosely based on the Linux driver:
* drivers/spi/spi-dw.c, which is:
* Copyright (c) 2009, Intel Corporation.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <spi.h>
#include <fdtdec.h>
#include <linux/compat.h>
#include <asm/io.h>
#include <asm/arch/clock_manager.h>
DECLARE_GLOBAL_DATA_PTR;
/* Register offsets */
#define DW_SPI_CTRL0 0x00
#define DW_SPI_CTRL1 0x04
#define DW_SPI_SSIENR 0x08
#define DW_SPI_MWCR 0x0c
#define DW_SPI_SER 0x10
#define DW_SPI_BAUDR 0x14
#define DW_SPI_TXFLTR 0x18
#define DW_SPI_RXFLTR 0x1c
#define DW_SPI_TXFLR 0x20
#define DW_SPI_RXFLR 0x24
#define DW_SPI_SR 0x28
#define DW_SPI_IMR 0x2c
#define DW_SPI_ISR 0x30
#define DW_SPI_RISR 0x34
#define DW_SPI_TXOICR 0x38
#define DW_SPI_RXOICR 0x3c
#define DW_SPI_RXUICR 0x40
#define DW_SPI_MSTICR 0x44
#define DW_SPI_ICR 0x48
#define DW_SPI_DMACR 0x4c
#define DW_SPI_DMATDLR 0x50
#define DW_SPI_DMARDLR 0x54
#define DW_SPI_IDR 0x58
#define DW_SPI_VERSION 0x5c
#define DW_SPI_DR 0x60
/* Bit fields in CTRLR0 */
#define SPI_DFS_OFFSET 0
#define SPI_FRF_OFFSET 4
#define SPI_FRF_SPI 0x0
#define SPI_FRF_SSP 0x1
#define SPI_FRF_MICROWIRE 0x2
#define SPI_FRF_RESV 0x3
#define SPI_MODE_OFFSET 6
#define SPI_SCPH_OFFSET 6
#define SPI_SCOL_OFFSET 7
#define SPI_TMOD_OFFSET 8
#define SPI_TMOD_MASK (0x3 << SPI_TMOD_OFFSET)
#define SPI_TMOD_TR 0x0 /* xmit & recv */
#define SPI_TMOD_TO 0x1 /* xmit only */
#define SPI_TMOD_RO 0x2 /* recv only */
#define SPI_TMOD_EPROMREAD 0x3 /* eeprom read mode */
#define SPI_SLVOE_OFFSET 10
#define SPI_SRL_OFFSET 11
#define SPI_CFS_OFFSET 12
/* Bit fields in SR, 7 bits */
#define SR_MASK GENMASK(6, 0) /* cover 7 bits */
#define SR_BUSY BIT(0)
#define SR_TF_NOT_FULL BIT(1)
#define SR_TF_EMPT BIT(2)
#define SR_RF_NOT_EMPT BIT(3)
#define SR_RF_FULL BIT(4)
#define SR_TX_ERR BIT(5)
#define SR_DCOL BIT(6)
#define RX_TIMEOUT 1000 /* timeout in ms */
struct dw_spi_platdata {
s32 frequency; /* Default clock frequency, -1 for none */
void __iomem *regs;
};
struct dw_spi_priv {
void __iomem *regs;
unsigned int freq; /* Default frequency */
unsigned int mode;
int bits_per_word;
u8 cs; /* chip select pin */
u8 tmode; /* TR/TO/RO/EEPROM */
u8 type; /* SPI/SSP/MicroWire */
int len;
u32 fifo_len; /* depth of the FIFO buffer */
void *tx;
void *tx_end;
void *rx;
void *rx_end;
};
static inline u32 dw_readl(struct dw_spi_priv *priv, u32 offset)
{
return __raw_readl(priv->regs + offset);
}
static inline void dw_writel(struct dw_spi_priv *priv, u32 offset, u32 val)
{
__raw_writel(val, priv->regs + offset);
}
static inline u16 dw_readw(struct dw_spi_priv *priv, u32 offset)
{
return __raw_readw(priv->regs + offset);
}
static inline void dw_writew(struct dw_spi_priv *priv, u32 offset, u16 val)
{
__raw_writew(val, priv->regs + offset);
}
static int dw_spi_ofdata_to_platdata(struct udevice *bus)
{
struct dw_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->regs = (struct dw_spi *)dev_get_addr(bus);
/* Use 500KHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
debug("%s: regs=%p max-frequency=%d\n", __func__, plat->regs,
plat->frequency);
return 0;
}
static inline void spi_enable_chip(struct dw_spi_priv *priv, int enable)
{
dw_writel(priv, DW_SPI_SSIENR, (enable ? 1 : 0));
}
/* Restart the controller, disable all interrupts, clean rx fifo */
static void spi_hw_init(struct dw_spi_priv *priv)
{
spi_enable_chip(priv, 0);
dw_writel(priv, DW_SPI_IMR, 0xff);
spi_enable_chip(priv, 1);
/*
* Try to detect the FIFO depth if not set by interface driver,
* the depth could be from 2 to 256 from HW spec
*/
if (!priv->fifo_len) {
u32 fifo;
for (fifo = 1; fifo < 256; fifo++) {
dw_writew(priv, DW_SPI_TXFLTR, fifo);
if (fifo != dw_readw(priv, DW_SPI_TXFLTR))
break;
}
priv->fifo_len = (fifo == 1) ? 0 : fifo;
dw_writew(priv, DW_SPI_TXFLTR, 0);
}
debug("%s: fifo_len=%d\n", __func__, priv->fifo_len);
}
static int dw_spi_probe(struct udevice *bus)
{
struct dw_spi_platdata *plat = dev_get_platdata(bus);
struct dw_spi_priv *priv = dev_get_priv(bus);
priv->regs = plat->regs;
priv->freq = plat->frequency;
/* Currently only bits_per_word == 8 supported */
priv->bits_per_word = 8;
priv->tmode = 0; /* Tx & Rx */
/* Basic HW init */
spi_hw_init(priv);
return 0;
}
/* Return the max entries we can fill into tx fifo */
static inline u32 tx_max(struct dw_spi_priv *priv)
{
u32 tx_left, tx_room, rxtx_gap;
tx_left = (priv->tx_end - priv->tx) / (priv->bits_per_word >> 3);
tx_room = priv->fifo_len - dw_readw(priv, DW_SPI_TXFLR);
/*
* Another concern is about the tx/rx mismatch, we
* thought about using (priv->fifo_len - rxflr - txflr) as
* one maximum value for tx, but it doesn't cover the
* data which is out of tx/rx fifo and inside the
* shift registers. So a control from sw point of
* view is taken.
*/
rxtx_gap = ((priv->rx_end - priv->rx) - (priv->tx_end - priv->tx)) /
(priv->bits_per_word >> 3);
return min3(tx_left, tx_room, (u32)(priv->fifo_len - rxtx_gap));
}
/* Return the max entries we should read out of rx fifo */
static inline u32 rx_max(struct dw_spi_priv *priv)
{
u32 rx_left = (priv->rx_end - priv->rx) / (priv->bits_per_word >> 3);
return min_t(u32, rx_left, dw_readw(priv, DW_SPI_RXFLR));
}
static void dw_writer(struct dw_spi_priv *priv)
{
u32 max = tx_max(priv);
u16 txw = 0;
while (max--) {
/* Set the tx word if the transfer's original "tx" is not null */
if (priv->tx_end - priv->len) {
if (priv->bits_per_word == 8)
txw = *(u8 *)(priv->tx);
else
txw = *(u16 *)(priv->tx);
}
dw_writew(priv, DW_SPI_DR, txw);
debug("%s: tx=0x%02x\n", __func__, txw);
priv->tx += priv->bits_per_word >> 3;
}
}
static int dw_reader(struct dw_spi_priv *priv)
{
unsigned start = get_timer(0);
u32 max;
u16 rxw;
/* Wait for rx data to be ready */
while (rx_max(priv) == 0) {
if (get_timer(start) > RX_TIMEOUT)
return -ETIMEDOUT;
}
max = rx_max(priv);
while (max--) {
rxw = dw_readw(priv, DW_SPI_DR);
debug("%s: rx=0x%02x\n", __func__, rxw);
/*
* Care about rx only if the transfer's original "rx" is
* not null
*/
if (priv->rx_end - priv->len) {
if (priv->bits_per_word == 8)
*(u8 *)(priv->rx) = rxw;
else
*(u16 *)(priv->rx) = rxw;
}
priv->rx += priv->bits_per_word >> 3;
}
return 0;
}
static int poll_transfer(struct dw_spi_priv *priv)
{
int ret;
do {
dw_writer(priv);
ret = dw_reader(priv);
if (ret < 0)
return ret;
} while (priv->rx_end > priv->rx);
return 0;
}
static int dw_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct dw_spi_priv *priv = dev_get_priv(bus);
const u8 *tx = dout;
u8 *rx = din;
int ret = 0;
u32 cr0 = 0;
u32 cs;
/* spi core configured to do 8 bit transfers */
if (bitlen % 8) {
debug("Non byte aligned SPI transfer.\n");
return -1;
}
cr0 = (priv->bits_per_word - 1) | (priv->type << SPI_FRF_OFFSET) |
(priv->mode << SPI_MODE_OFFSET) |
(priv->tmode << SPI_TMOD_OFFSET);
if (rx && tx)
priv->tmode = SPI_TMOD_TR;
else if (rx)
priv->tmode = SPI_TMOD_RO;
else
priv->tmode = SPI_TMOD_TO;
cr0 &= ~SPI_TMOD_MASK;
cr0 |= (priv->tmode << SPI_TMOD_OFFSET);
priv->len = bitlen >> 3;
debug("%s: rx=%p tx=%p len=%d [bytes]\n", __func__, rx, tx, priv->len);
priv->tx = (void *)tx;
priv->tx_end = priv->tx + priv->len;
priv->rx = rx;
priv->rx_end = priv->rx + priv->len;
/* Disable controller before writing control registers */
spi_enable_chip(priv, 0);
debug("%s: cr0=%08x\n", __func__, cr0);
/* Reprogram cr0 only if changed */
if (dw_readw(priv, DW_SPI_CTRL0) != cr0)
dw_writew(priv, DW_SPI_CTRL0, cr0);
/*
* Configure the desired SS (slave select 0...3) in the controller
* The DW SPI controller will activate and deactivate this CS
* automatically. So no cs_activate() etc is needed in this driver.
*/
cs = spi_chip_select(dev);
dw_writel(priv, DW_SPI_SER, 1 << cs);
/* Enable controller after writing control registers */
spi_enable_chip(priv, 1);
/* Start transfer in a polling loop */
ret = poll_transfer(priv);
return ret;
}
static int dw_spi_set_speed(struct udevice *bus, uint speed)
{
struct dw_spi_platdata *plat = bus->platdata;
struct dw_spi_priv *priv = dev_get_priv(bus);
u16 clk_div;
if (speed > plat->frequency)
speed = plat->frequency;
/* Disable controller before writing control registers */
spi_enable_chip(priv, 0);
/* clk_div doesn't support odd number */
clk_div = cm_get_spi_controller_clk_hz() / speed;
clk_div = (clk_div + 1) & 0xfffe;
dw_writel(priv, DW_SPI_BAUDR, clk_div);
/* Enable controller after writing control registers */
spi_enable_chip(priv, 1);
priv->freq = speed;
debug("%s: regs=%p speed=%d clk_div=%d\n", __func__, priv->regs,
priv->freq, clk_div);
return 0;
}
static int dw_spi_set_mode(struct udevice *bus, uint mode)
{
struct dw_spi_priv *priv = dev_get_priv(bus);
/*
* Can't set mode yet. Since this depends on if rx, tx, or
* rx & tx is requested. So we have to defer this to the
* real transfer function.
*/
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops dw_spi_ops = {
.xfer = dw_spi_xfer,
.set_speed = dw_spi_set_speed,
.set_mode = dw_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id dw_spi_ids[] = {
{ .compatible = "snps,dw-apb-ssi" },
{ }
};
U_BOOT_DRIVER(dw_spi) = {
.name = "dw_spi",
.id = UCLASS_SPI,
.of_match = dw_spi_ids,
.ops = &dw_spi_ops,
.ofdata_to_platdata = dw_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct dw_spi_platdata),
.priv_auto_alloc_size = sizeof(struct dw_spi_priv),
.probe = dw_spi_probe,
};

View File

@@ -0,0 +1,272 @@
/*
* SPI Driver for EP93xx
*
* Copyright (C) 2013 Sergey Kostanabev <sergey.kostanbaev <at> fairwaves.ru>
*
* Inspired form linux kernel driver and atmel uboot driver
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
#include <asm/arch/ep93xx.h>
#define SSPBASE SPI_BASE
#define SSPCR0 0x0000
#define SSPCR0_MODE_SHIFT 6
#define SSPCR0_SCR_SHIFT 8
#define SSPCR0_SPH BIT(7)
#define SSPCR0_SPO BIT(6)
#define SSPCR0_FRF_SPI 0
#define SSPCR0_DSS_8BIT 7
#define SSPCR1 0x0004
#define SSPCR1_RIE BIT(0)
#define SSPCR1_TIE BIT(1)
#define SSPCR1_RORIE BIT(2)
#define SSPCR1_LBM BIT(3)
#define SSPCR1_SSE BIT(4)
#define SSPCR1_MS BIT(5)
#define SSPCR1_SOD BIT(6)
#define SSPDR 0x0008
#define SSPSR 0x000c
#define SSPSR_TFE BIT(0)
#define SSPSR_TNF BIT(1)
#define SSPSR_RNE BIT(2)
#define SSPSR_RFF BIT(3)
#define SSPSR_BSY BIT(4)
#define SSPCPSR 0x0010
#define SSPIIR 0x0014
#define SSPIIR_RIS BIT(0)
#define SSPIIR_TIS BIT(1)
#define SSPIIR_RORIS BIT(2)
#define SSPICR SSPIIR
#define SSPCLOCK 14745600
#define SSP_MAX_RATE (SSPCLOCK / 2)
#define SSP_MIN_RATE (SSPCLOCK / (254 * 256))
/* timeout in milliseconds */
#define SPI_TIMEOUT 5
/* maximum depth of RX/TX FIFO */
#define SPI_FIFO_SIZE 8
struct ep93xx_spi_slave {
struct spi_slave slave;
unsigned sspcr0;
unsigned sspcpsr;
};
static inline struct ep93xx_spi_slave *to_ep93xx_spi(struct spi_slave *slave)
{
return container_of(slave, struct ep93xx_spi_slave, slave);
}
void spi_init()
{
}
static inline void ep93xx_spi_write_u8(u16 reg, u8 value)
{
writel(value, (unsigned int *)(SSPBASE + reg));
}
static inline u8 ep93xx_spi_read_u8(u16 reg)
{
return readl((unsigned int *)(SSPBASE + reg));
}
static inline void ep93xx_spi_write_u16(u16 reg, u16 value)
{
writel(value, (unsigned int *)(SSPBASE + reg));
}
static inline u16 ep93xx_spi_read_u16(u16 reg)
{
return (u16)readl((unsigned int *)(SSPBASE + reg));
}
static int ep93xx_spi_init_hw(unsigned int rate, unsigned int mode,
struct ep93xx_spi_slave *slave)
{
unsigned cpsr, scr;
if (rate > SSP_MAX_RATE)
rate = SSP_MAX_RATE;
if (rate < SSP_MIN_RATE)
return -1;
/* Calculate divisors so that we can get speed according the
* following formula:
* rate = spi_clock_rate / (cpsr * (1 + scr))
*
* cpsr must be even number and starts from 2, scr can be any number
* between 0 and 255.
*/
for (cpsr = 2; cpsr <= 254; cpsr += 2) {
for (scr = 0; scr <= 255; scr++) {
if ((SSPCLOCK / (cpsr * (scr + 1))) <= rate) {
/* Set CHPA and CPOL, SPI format and 8bit */
unsigned sspcr0 = (scr << SSPCR0_SCR_SHIFT) |
SSPCR0_FRF_SPI | SSPCR0_DSS_8BIT;
if (mode & SPI_CPHA)
sspcr0 |= SSPCR0_SPH;
if (mode & SPI_CPOL)
sspcr0 |= SSPCR0_SPO;
slave->sspcr0 = sspcr0;
slave->sspcpsr = cpsr;
return 0;
}
}
}
return -1;
}
void spi_set_speed(struct spi_slave *slave, unsigned int hz)
{
struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
unsigned int mode = 0;
if (as->sspcr0 & SSPCR0_SPH)
mode |= SPI_CPHA;
if (as->sspcr0 & SSPCR0_SPO)
mode |= SPI_CPOL;
ep93xx_spi_init_hw(hz, mode, as);
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct ep93xx_spi_slave *as;
if (!spi_cs_is_valid(bus, cs))
return NULL;
as = spi_alloc_slave(struct ep93xx_spi_slave, bus, cs);
if (!as)
return NULL;
if (ep93xx_spi_init_hw(max_hz, mode, as)) {
free(as);
return NULL;
}
return &as->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
free(as);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct ep93xx_spi_slave *as = to_ep93xx_spi(slave);
/* Enable the SPI hardware */
ep93xx_spi_write_u8(SSPCR1, SSPCR1_SSE);
ep93xx_spi_write_u8(SSPCPSR, as->sspcpsr);
ep93xx_spi_write_u16(SSPCR0, as->sspcr0);
debug("Select CS:%d SSPCPSR=%02x SSPCR0=%04x\n",
slave->cs, as->sspcpsr, as->sspcr0);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
/* Disable the SPI hardware */
ep93xx_spi_write_u8(SSPCR1, 0);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
unsigned int len_tx;
unsigned int len_rx;
unsigned int len;
u32 status;
const u8 *txp = dout;
u8 *rxp = din;
u8 value;
debug("spi_xfer: slave %u:%u dout %p din %p bitlen %u\n",
slave->bus, slave->cs, (uint *)dout, (uint *)din, bitlen);
if (bitlen == 0)
/* Finish any previously submitted transfers */
goto out;
if (bitlen % 8) {
/* Errors always terminate an ongoing transfer */
flags |= SPI_XFER_END;
goto out;
}
len = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
/* Empty RX FIFO */
while ((ep93xx_spi_read_u8(SSPSR) & SSPSR_RNE))
ep93xx_spi_read_u8(SSPDR);
spi_cs_activate(slave);
}
for (len_tx = 0, len_rx = 0; len_rx < len; ) {
status = ep93xx_spi_read_u8(SSPSR);
if ((len_tx < len) && (status & SSPSR_TNF)) {
if (txp)
value = *txp++;
else
value = 0xff;
ep93xx_spi_write_u8(SSPDR, value);
len_tx++;
}
if (status & SSPSR_RNE) {
value = ep93xx_spi_read_u8(SSPDR);
if (rxp)
*rxp++ = value;
len_rx++;
}
}
out:
if (flags & SPI_XFER_END) {
/*
* Wait until the transfer is completely done before
* we deactivate CS.
*/
do {
status = ep93xx_spi_read_u8(SSPSR);
} while (status & SSPSR_BSY);
spi_cs_deactivate(slave);
}
return 0;
}

View File

@@ -0,0 +1,431 @@
/*
* (C) Copyright 2012 SAMSUNG Electronics
* Padmavathi Venna <padma.v@samsung.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <spi.h>
#include <fdtdec.h>
#include <asm/arch/clk.h>
#include <asm/arch/clock.h>
#include <asm/arch/cpu.h>
#include <asm/arch/gpio.h>
#include <asm/arch/pinmux.h>
#include <asm/arch/spi.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
struct exynos_spi_platdata {
enum periph_id periph_id;
s32 frequency; /* Default clock frequency, -1 for none */
struct exynos_spi *regs;
uint deactivate_delay_us; /* Delay to wait after deactivate */
};
struct exynos_spi_priv {
struct exynos_spi *regs;
unsigned int freq; /* Default frequency */
unsigned int mode;
enum periph_id periph_id; /* Peripheral ID for this device */
unsigned int fifo_size;
int skip_preamble;
ulong last_transaction_us; /* Time of last transaction end */
};
/**
* Flush spi tx, rx fifos and reset the SPI controller
*
* @param regs Pointer to SPI registers
*/
static void spi_flush_fifo(struct exynos_spi *regs)
{
clrsetbits_le32(&regs->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
setbits_le32(&regs->ch_cfg, SPI_TX_CH_ON | SPI_RX_CH_ON);
}
static void spi_get_fifo_levels(struct exynos_spi *regs,
int *rx_lvl, int *tx_lvl)
{
uint32_t spi_sts = readl(&regs->spi_sts);
*rx_lvl = (spi_sts >> SPI_RX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
*tx_lvl = (spi_sts >> SPI_TX_LVL_OFFSET) & SPI_FIFO_LVL_MASK;
}
/**
* If there's something to transfer, do a software reset and set a
* transaction size.
*
* @param regs SPI peripheral registers
* @param count Number of bytes to transfer
* @param step Number of bytes to transfer in each packet (1 or 4)
*/
static void spi_request_bytes(struct exynos_spi *regs, int count, int step)
{
debug("%s: regs=%p, count=%d, step=%d\n", __func__, regs, count, step);
/* For word address we need to swap bytes */
if (step == 4) {
setbits_le32(&regs->mode_cfg,
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
count /= 4;
setbits_le32(&regs->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN |
SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP |
SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP);
} else {
/* Select byte access and clear the swap configuration */
clrbits_le32(&regs->mode_cfg,
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
writel(0, &regs->swap_cfg);
}
assert(count && count < (1 << 16));
setbits_le32(&regs->ch_cfg, SPI_CH_RST);
clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
writel(count | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
}
static int spi_rx_tx(struct exynos_spi_priv *priv, int todo,
void **dinp, void const **doutp, unsigned long flags)
{
struct exynos_spi *regs = priv->regs;
uchar *rxp = *dinp;
const uchar *txp = *doutp;
int rx_lvl, tx_lvl;
uint out_bytes, in_bytes;
int toread;
unsigned start = get_timer(0);
int stopping;
int step;
out_bytes = in_bytes = todo;
stopping = priv->skip_preamble && (flags & SPI_XFER_END) &&
!(priv->mode & SPI_SLAVE);
/*
* Try to transfer words if we can. This helps read performance at
* SPI clock speeds above about 20MHz.
*/
step = 1;
if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) &&
!priv->skip_preamble)
step = 4;
/*
* If there's something to send, do a software reset and set a
* transaction size.
*/
spi_request_bytes(regs, todo, step);
/*
* Bytes are transmitted/received in pairs. Wait to receive all the
* data because then transmission will be done as well.
*/
toread = in_bytes;
while (in_bytes) {
int temp;
/* Keep the fifos full/empty. */
spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
/*
* Don't completely fill the txfifo, since we don't want our
* rxfifo to overflow, and it may already contain data.
*/
while (tx_lvl < priv->fifo_size/2 && out_bytes) {
if (!txp)
temp = -1;
else if (step == 4)
temp = *(uint32_t *)txp;
else
temp = *txp;
writel(temp, &regs->tx_data);
out_bytes -= step;
if (txp)
txp += step;
tx_lvl += step;
}
if (rx_lvl >= step) {
while (rx_lvl >= step) {
temp = readl(&regs->rx_data);
if (priv->skip_preamble) {
if (temp == SPI_PREAMBLE_END_BYTE) {
priv->skip_preamble = 0;
stopping = 0;
}
} else {
if (rxp || stopping) {
if (step == 4)
*(uint32_t *)rxp = temp;
else
*rxp = temp;
rxp += step;
}
in_bytes -= step;
}
toread -= step;
rx_lvl -= step;
}
} else if (!toread) {
/*
* We have run out of input data, but haven't read
* enough bytes after the preamble yet. Read some more,
* and make sure that we transmit dummy bytes too, to
* keep things going.
*/
assert(!out_bytes);
out_bytes = in_bytes;
toread = in_bytes;
txp = NULL;
spi_request_bytes(regs, toread, step);
}
if (priv->skip_preamble && get_timer(start) > 100) {
debug("SPI timeout: in_bytes=%d, out_bytes=%d, ",
in_bytes, out_bytes);
return -ETIMEDOUT;
}
}
*dinp = rxp;
*doutp = txp;
return 0;
}
/**
* Activate the CS by driving it LOW
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct exynos_spi_platdata *pdata = dev_get_platdata(bus);
struct exynos_spi_priv *priv = dev_get_priv(bus);
/* If it's too soon to do another transaction, wait */
if (pdata->deactivate_delay_us &&
priv->last_transaction_us) {
ulong delay_us; /* The delay completed so far */
delay_us = timer_get_us() - priv->last_transaction_us;
if (delay_us < pdata->deactivate_delay_us)
udelay(pdata->deactivate_delay_us - delay_us);
}
clrbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT);
debug("Activate CS, bus '%s'\n", bus->name);
priv->skip_preamble = priv->mode & SPI_PREAMBLE;
}
/**
* Deactivate the CS by driving it HIGH
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct exynos_spi_platdata *pdata = dev_get_platdata(bus);
struct exynos_spi_priv *priv = dev_get_priv(bus);
setbits_le32(&priv->regs->cs_reg, SPI_SLAVE_SIG_INACT);
/* Remember time of this transaction so we can honour the bus delay */
if (pdata->deactivate_delay_us)
priv->last_transaction_us = timer_get_us();
debug("Deactivate CS, bus '%s'\n", bus->name);
}
static int exynos_spi_ofdata_to_platdata(struct udevice *bus)
{
struct exynos_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->regs = (struct exynos_spi *)dev_get_addr(bus);
plat->periph_id = pinmux_decode_periph_id(blob, node);
if (plat->periph_id == PERIPH_ID_NONE) {
debug("%s: Invalid peripheral ID %d\n", __func__,
plat->periph_id);
return -FDT_ERR_NOTFOUND;
}
/* Use 500KHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
debug("%s: regs=%p, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n",
__func__, plat->regs, plat->periph_id, plat->frequency,
plat->deactivate_delay_us);
return 0;
}
static int exynos_spi_probe(struct udevice *bus)
{
struct exynos_spi_platdata *plat = dev_get_platdata(bus);
struct exynos_spi_priv *priv = dev_get_priv(bus);
priv->regs = plat->regs;
if (plat->periph_id == PERIPH_ID_SPI1 ||
plat->periph_id == PERIPH_ID_SPI2)
priv->fifo_size = 64;
else
priv->fifo_size = 256;
priv->skip_preamble = 0;
priv->last_transaction_us = timer_get_us();
priv->freq = plat->frequency;
priv->periph_id = plat->periph_id;
return 0;
}
static int exynos_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct exynos_spi_priv *priv = dev_get_priv(bus);
exynos_pinmux_config(priv->periph_id, PINMUX_FLAG_NONE);
spi_flush_fifo(priv->regs);
writel(SPI_FB_DELAY_180, &priv->regs->fb_clk);
return 0;
}
static int exynos_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct exynos_spi_priv *priv = dev_get_priv(bus);
spi_flush_fifo(priv->regs);
return 0;
}
static int exynos_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct exynos_spi_priv *priv = dev_get_priv(bus);
int upto, todo;
int bytelen;
int ret = 0;
/* spi core configured to do 8 bit transfers */
if (bitlen % 8) {
debug("Non byte aligned SPI transfer.\n");
return -1;
}
/* Start the transaction, if necessary. */
if ((flags & SPI_XFER_BEGIN))
spi_cs_activate(dev);
/*
* Exynos SPI limits each transfer to 65535 transfers. To keep
* things simple, allow a maximum of 65532 bytes. We could allow
* more in word mode, but the performance difference is small.
*/
bytelen = bitlen / 8;
for (upto = 0; !ret && upto < bytelen; upto += todo) {
todo = min(bytelen - upto, (1 << 16) - 4);
ret = spi_rx_tx(priv, todo, &din, &dout, flags);
if (ret)
break;
}
/* Stop the transaction, if necessary. */
if ((flags & SPI_XFER_END) && !(priv->mode & SPI_SLAVE)) {
spi_cs_deactivate(dev);
if (priv->skip_preamble) {
assert(!priv->skip_preamble);
debug("Failed to complete premable transaction\n");
ret = -1;
}
}
return ret;
}
static int exynos_spi_set_speed(struct udevice *bus, uint speed)
{
struct exynos_spi_platdata *plat = bus->platdata;
struct exynos_spi_priv *priv = dev_get_priv(bus);
int ret;
if (speed > plat->frequency)
speed = plat->frequency;
ret = set_spi_clk(priv->periph_id, speed);
if (ret)
return ret;
priv->freq = speed;
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
return 0;
}
static int exynos_spi_set_mode(struct udevice *bus, uint mode)
{
struct exynos_spi_priv *priv = dev_get_priv(bus);
uint32_t reg;
reg = readl(&priv->regs->ch_cfg);
reg &= ~(SPI_CH_CPHA_B | SPI_CH_CPOL_L);
if (mode & SPI_CPHA)
reg |= SPI_CH_CPHA_B;
if (mode & SPI_CPOL)
reg |= SPI_CH_CPOL_L;
writel(reg, &priv->regs->ch_cfg);
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops exynos_spi_ops = {
.claim_bus = exynos_spi_claim_bus,
.release_bus = exynos_spi_release_bus,
.xfer = exynos_spi_xfer,
.set_speed = exynos_spi_set_speed,
.set_mode = exynos_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id exynos_spi_ids[] = {
{ .compatible = "samsung,exynos-spi" },
{ }
};
U_BOOT_DRIVER(exynos_spi) = {
.name = "exynos_spi",
.id = UCLASS_SPI,
.of_match = exynos_spi_ids,
.ops = &exynos_spi_ops,
.ofdata_to_platdata = exynos_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct exynos_spi_platdata),
.priv_auto_alloc_size = sizeof(struct exynos_spi_priv),
.probe = exynos_spi_probe,
};

View File

@@ -0,0 +1,737 @@
/*
* (C) Copyright 2000-2003
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* Copyright (C) 2004-2009, 2015 Freescale Semiconductor, Inc.
* TsiChung Liew (Tsi-Chung.Liew@freescale.com)
* Chao Fu (B44548@freescale.com)
* Haikun Wang (B53464@freescale.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <dm.h>
#include <errno.h>
#include <common.h>
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
#include <fdtdec.h>
#ifndef CONFIG_M68K
#include <asm/arch/clock.h>
#endif
#include <fsl_dspi.h>
DECLARE_GLOBAL_DATA_PTR;
/* fsl_dspi_platdata flags */
#define DSPI_FLAG_REGMAP_ENDIAN_BIG BIT(0)
/* idle data value */
#define DSPI_IDLE_VAL 0x0
/* max chipselect signals number */
#define FSL_DSPI_MAX_CHIPSELECT 6
/* default SCK frequency, unit: HZ */
#define FSL_DSPI_DEFAULT_SCK_FREQ 10000000
/* tx/rx data wait timeout value, unit: us */
#define DSPI_TXRX_WAIT_TIMEOUT 1000000
/* CTAR register pre-configure value */
#define DSPI_CTAR_DEFAULT_VALUE (DSPI_CTAR_TRSZ(7) | \
DSPI_CTAR_PCSSCK_1CLK | \
DSPI_CTAR_PASC(0) | \
DSPI_CTAR_PDT(0) | \
DSPI_CTAR_CSSCK(0) | \
DSPI_CTAR_ASC(0) | \
DSPI_CTAR_DT(0))
/* CTAR register pre-configure mask */
#define DSPI_CTAR_SET_MODE_MASK (DSPI_CTAR_TRSZ(15) | \
DSPI_CTAR_PCSSCK(3) | \
DSPI_CTAR_PASC(3) | \
DSPI_CTAR_PDT(3) | \
DSPI_CTAR_CSSCK(15) | \
DSPI_CTAR_ASC(15) | \
DSPI_CTAR_DT(15))
/**
* struct fsl_dspi_platdata - platform data for Freescale DSPI
*
* @flags: Flags for DSPI DSPI_FLAG_...
* @speed_hz: Default SCK frequency
* @num_chipselect: Number of DSPI chipselect signals
* @regs_addr: Base address of DSPI registers
*/
struct fsl_dspi_platdata {
uint flags;
uint speed_hz;
uint num_chipselect;
fdt_addr_t regs_addr;
};
/**
* struct fsl_dspi_priv - private data for Freescale DSPI
*
* @flags: Flags for DSPI DSPI_FLAG_...
* @mode: SPI mode to use for slave device (see SPI mode flags)
* @mcr_val: MCR register configure value
* @bus_clk: DSPI input clk frequency
* @speed_hz: Default SCK frequency
* @charbit: How many bits in every transfer
* @num_chipselect: Number of DSPI chipselect signals
* @ctar_val: CTAR register configure value of per chipselect slave device
* @regs: Point to DSPI register structure for I/O access
*/
struct fsl_dspi_priv {
uint flags;
uint mode;
uint mcr_val;
uint bus_clk;
uint speed_hz;
uint charbit;
uint num_chipselect;
uint ctar_val[FSL_DSPI_MAX_CHIPSELECT];
struct dspi *regs;
};
#ifndef CONFIG_DM_SPI
struct fsl_dspi {
struct spi_slave slave;
struct fsl_dspi_priv priv;
};
#endif
__weak void cpu_dspi_port_conf(void)
{
}
__weak int cpu_dspi_claim_bus(uint bus, uint cs)
{
return 0;
}
__weak void cpu_dspi_release_bus(uint bus, uint cs)
{
}
static uint dspi_read32(uint flags, uint *addr)
{
return flags & DSPI_FLAG_REGMAP_ENDIAN_BIG ?
in_be32(addr) : in_le32(addr);
}
static void dspi_write32(uint flags, uint *addr, uint val)
{
flags & DSPI_FLAG_REGMAP_ENDIAN_BIG ?
out_be32(addr, val) : out_le32(addr, val);
}
static void dspi_halt(struct fsl_dspi_priv *priv, u8 halt)
{
uint mcr_val;
mcr_val = dspi_read32(priv->flags, &priv->regs->mcr);
if (halt)
mcr_val |= DSPI_MCR_HALT;
else
mcr_val &= ~DSPI_MCR_HALT;
dspi_write32(priv->flags, &priv->regs->mcr, mcr_val);
}
static void fsl_dspi_init_mcr(struct fsl_dspi_priv *priv, uint cfg_val)
{
/* halt DSPI module */
dspi_halt(priv, 1);
dspi_write32(priv->flags, &priv->regs->mcr, cfg_val);
/* resume module */
dspi_halt(priv, 0);
priv->mcr_val = cfg_val;
}
static void fsl_dspi_cfg_cs_active_state(struct fsl_dspi_priv *priv,
uint cs, uint state)
{
uint mcr_val;
dspi_halt(priv, 1);
mcr_val = dspi_read32(priv->flags, &priv->regs->mcr);
if (state & SPI_CS_HIGH)
/* CSx inactive state is low */
mcr_val &= ~DSPI_MCR_PCSIS(cs);
else
/* CSx inactive state is high */
mcr_val |= DSPI_MCR_PCSIS(cs);
dspi_write32(priv->flags, &priv->regs->mcr, mcr_val);
dspi_halt(priv, 0);
}
static int fsl_dspi_cfg_ctar_mode(struct fsl_dspi_priv *priv,
uint cs, uint mode)
{
uint bus_setup;
bus_setup = dspi_read32(priv->flags, &priv->regs->ctar[0]);
bus_setup &= ~DSPI_CTAR_SET_MODE_MASK;
bus_setup |= priv->ctar_val[cs];
bus_setup &= ~(DSPI_CTAR_CPOL | DSPI_CTAR_CPHA | DSPI_CTAR_LSBFE);
if (mode & SPI_CPOL)
bus_setup |= DSPI_CTAR_CPOL;
if (mode & SPI_CPHA)
bus_setup |= DSPI_CTAR_CPHA;
if (mode & SPI_LSB_FIRST)
bus_setup |= DSPI_CTAR_LSBFE;
dspi_write32(priv->flags, &priv->regs->ctar[0], bus_setup);
priv->charbit =
((dspi_read32(priv->flags, &priv->regs->ctar[0]) &
DSPI_CTAR_TRSZ(15)) == DSPI_CTAR_TRSZ(15)) ? 16 : 8;
return 0;
}
static void fsl_dspi_clr_fifo(struct fsl_dspi_priv *priv)
{
uint mcr_val;
dspi_halt(priv, 1);
mcr_val = dspi_read32(priv->flags, &priv->regs->mcr);
/* flush RX and TX FIFO */
mcr_val |= (DSPI_MCR_CTXF | DSPI_MCR_CRXF);
dspi_write32(priv->flags, &priv->regs->mcr, mcr_val);
dspi_halt(priv, 0);
}
static void dspi_tx(struct fsl_dspi_priv *priv, u32 ctrl, u16 data)
{
int timeout = DSPI_TXRX_WAIT_TIMEOUT;
/* wait for empty entries in TXFIFO or timeout */
while (DSPI_SR_TXCTR(dspi_read32(priv->flags, &priv->regs->sr)) >= 4 &&
timeout--)
udelay(1);
if (timeout >= 0)
dspi_write32(priv->flags, &priv->regs->tfr, (ctrl | data));
else
debug("dspi_tx: waiting timeout!\n");
}
static u16 dspi_rx(struct fsl_dspi_priv *priv)
{
int timeout = DSPI_TXRX_WAIT_TIMEOUT;
/* wait for valid entries in RXFIFO or timeout */
while (DSPI_SR_RXCTR(dspi_read32(priv->flags, &priv->regs->sr)) == 0 &&
timeout--)
udelay(1);
if (timeout >= 0)
return (u16)DSPI_RFR_RXDATA(
dspi_read32(priv->flags, &priv->regs->rfr));
else {
debug("dspi_rx: waiting timeout!\n");
return (u16)(~0);
}
}
static int dspi_xfer(struct fsl_dspi_priv *priv, uint cs, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
u16 *spi_rd16 = NULL, *spi_wr16 = NULL;
u8 *spi_rd = NULL, *spi_wr = NULL;
static u32 ctrl;
uint len = bitlen >> 3;
if (priv->charbit == 16) {
bitlen >>= 1;
spi_wr16 = (u16 *)dout;
spi_rd16 = (u16 *)din;
} else {
spi_wr = (u8 *)dout;
spi_rd = (u8 *)din;
}
if ((flags & SPI_XFER_BEGIN) == SPI_XFER_BEGIN)
ctrl |= DSPI_TFR_CONT;
ctrl = ctrl & DSPI_TFR_CONT;
ctrl = ctrl | DSPI_TFR_CTAS(0) | DSPI_TFR_PCS(cs);
if (len > 1) {
int tmp_len = len - 1;
while (tmp_len--) {
if (dout != NULL) {
if (priv->charbit == 16)
dspi_tx(priv, ctrl, *spi_wr16++);
else
dspi_tx(priv, ctrl, *spi_wr++);
dspi_rx(priv);
}
if (din != NULL) {
dspi_tx(priv, ctrl, DSPI_IDLE_VAL);
if (priv->charbit == 16)
*spi_rd16++ = dspi_rx(priv);
else
*spi_rd++ = dspi_rx(priv);
}
}
len = 1; /* remaining byte */
}
if ((flags & SPI_XFER_END) == SPI_XFER_END)
ctrl &= ~DSPI_TFR_CONT;
if (len) {
if (dout != NULL) {
if (priv->charbit == 16)
dspi_tx(priv, ctrl, *spi_wr16);
else
dspi_tx(priv, ctrl, *spi_wr);
dspi_rx(priv);
}
if (din != NULL) {
dspi_tx(priv, ctrl, DSPI_IDLE_VAL);
if (priv->charbit == 16)
*spi_rd16 = dspi_rx(priv);
else
*spi_rd = dspi_rx(priv);
}
} else {
/* dummy read */
dspi_tx(priv, ctrl, DSPI_IDLE_VAL);
dspi_rx(priv);
}
return 0;
}
/**
* Calculate the divide value between input clk frequency and expected SCK frequency
* Formula: SCK = (clkrate/pbr) x ((1+dbr)/br)
* Dbr: use default value 0
*
* @pbr: return Baud Rate Prescaler value
* @br: return Baud Rate Scaler value
* @speed_hz: expected SCK frequency
* @clkrate: input clk frequency
*/
static int fsl_dspi_hz_to_spi_baud(int *pbr, int *br,
int speed_hz, uint clkrate)
{
/* Valid baud rate pre-scaler values */
int pbr_tbl[4] = {2, 3, 5, 7};
int brs[16] = {2, 4, 6, 8,
16, 32, 64, 128,
256, 512, 1024, 2048,
4096, 8192, 16384, 32768};
int temp, i = 0, j = 0;
temp = clkrate / speed_hz;
for (i = 0; i < ARRAY_SIZE(pbr_tbl); i++)
for (j = 0; j < ARRAY_SIZE(brs); j++) {
if (pbr_tbl[i] * brs[j] >= temp) {
*pbr = i;
*br = j;
return 0;
}
}
debug("Can not find valid baud rate,speed_hz is %d, ", speed_hz);
debug("clkrate is %d, we use the max prescaler value.\n", clkrate);
*pbr = ARRAY_SIZE(pbr_tbl) - 1;
*br = ARRAY_SIZE(brs) - 1;
return -EINVAL;
}
static int fsl_dspi_cfg_speed(struct fsl_dspi_priv *priv, uint speed)
{
int ret;
uint bus_setup;
int best_i, best_j, bus_clk;
bus_clk = priv->bus_clk;
debug("DSPI set_speed: expected SCK speed %u, bus_clk %u.\n",
speed, bus_clk);
bus_setup = dspi_read32(priv->flags, &priv->regs->ctar[0]);
bus_setup &= ~(DSPI_CTAR_DBR | DSPI_CTAR_PBR(0x3) | DSPI_CTAR_BR(0xf));
ret = fsl_dspi_hz_to_spi_baud(&best_i, &best_j, speed, bus_clk);
if (ret) {
speed = priv->speed_hz;
debug("DSPI set_speed use default SCK rate %u.\n", speed);
fsl_dspi_hz_to_spi_baud(&best_i, &best_j, speed, bus_clk);
}
bus_setup |= (DSPI_CTAR_PBR(best_i) | DSPI_CTAR_BR(best_j));
dspi_write32(priv->flags, &priv->regs->ctar[0], bus_setup);
priv->speed_hz = speed;
return 0;
}
#ifndef CONFIG_DM_SPI
void spi_init(void)
{
/* Nothing to do */
}
void spi_init_f(void)
{
/* Nothing to do */
}
void spi_init_r(void)
{
/* Nothing to do */
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
if (((cs >= 0) && (cs < 8)) && ((bus >= 0) && (bus < 8)))
return 1;
else
return 0;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct fsl_dspi *dspi;
uint mcr_cfg_val;
dspi = spi_alloc_slave(struct fsl_dspi, bus, cs);
if (!dspi)
return NULL;
cpu_dspi_port_conf();
#ifdef CONFIG_SYS_FSL_DSPI_BE
dspi->priv.flags |= DSPI_FLAG_REGMAP_ENDIAN_BIG;
#endif
dspi->priv.regs = (struct dspi *)MMAP_DSPI;
#ifdef CONFIG_M68K
dspi->priv.bus_clk = gd->bus_clk;
#else
dspi->priv.bus_clk = mxc_get_clock(MXC_DSPI_CLK);
#endif
dspi->priv.speed_hz = FSL_DSPI_DEFAULT_SCK_FREQ;
/* default: all CS signals inactive state is high */
mcr_cfg_val = DSPI_MCR_MSTR | DSPI_MCR_PCSIS_MASK |
DSPI_MCR_CRXF | DSPI_MCR_CTXF;
fsl_dspi_init_mcr(&dspi->priv, mcr_cfg_val);
for (i = 0; i < FSL_DSPI_MAX_CHIPSELECT; i++)
dspi->priv.ctar_val[i] = DSPI_CTAR_DEFAULT_VALUE;
#ifdef CONFIG_SYS_DSPI_CTAR0
if (FSL_DSPI_MAX_CHIPSELECT > 0)
dspi->priv.ctar_val[0] = CONFIG_SYS_DSPI_CTAR0;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR1
if (FSL_DSPI_MAX_CHIPSELECT > 1)
dspi->priv.ctar_val[1] = CONFIG_SYS_DSPI_CTAR1;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR2
if (FSL_DSPI_MAX_CHIPSELECT > 2)
dspi->priv.ctar_val[2] = CONFIG_SYS_DSPI_CTAR2;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR3
if (FSL_DSPI_MAX_CHIPSELECT > 3)
dspi->priv.ctar_val[3] = CONFIG_SYS_DSPI_CTAR3;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR4
if (FSL_DSPI_MAX_CHIPSELECT > 4)
dspi->priv.ctar_val[4] = CONFIG_SYS_DSPI_CTAR4;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR5
if (FSL_DSPI_MAX_CHIPSELECT > 5)
dspi->priv.ctar_val[5] = CONFIG_SYS_DSPI_CTAR5;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR6
if (FSL_DSPI_MAX_CHIPSELECT > 6)
dspi->priv.ctar_val[6] = CONFIG_SYS_DSPI_CTAR6;
#endif
#ifdef CONFIG_SYS_DSPI_CTAR7
if (FSL_DSPI_MAX_CHIPSELECT > 7)
dspi->priv.ctar_val[7] = CONFIG_SYS_DSPI_CTAR7;
#endif
fsl_dspi_cfg_speed(&dspi->priv, max_hz);
/* configure transfer mode */
fsl_dspi_cfg_ctar_mode(&dspi->priv, cs, mode);
/* configure active state of CSX */
fsl_dspi_cfg_cs_active_state(&dspi->priv, cs, mode);
return &dspi->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
free(slave);
}
int spi_claim_bus(struct spi_slave *slave)
{
uint sr_val;
struct fsl_dspi *dspi = (struct fsl_dspi *)slave;
cpu_dspi_claim_bus(slave->bus, slave->cs);
fsl_dspi_clr_fifo(&dspi->priv);
/* check module TX and RX status */
sr_val = dspi_read32(dspi->priv.flags, &dspi->priv.regs->sr);
if ((sr_val & DSPI_SR_TXRXS) != DSPI_SR_TXRXS) {
debug("DSPI RX/TX not ready!\n");
return -EIO;
}
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct fsl_dspi *dspi = (struct fsl_dspi *)slave;
dspi_halt(&dspi->priv, 1);
cpu_dspi_release_bus(slave->bus.slave->cs);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct fsl_dspi *dspi = (struct fsl_dspi *)slave;
return dspi_xfer(&dspi->priv, slave->cs, bitlen, dout, din, flags);
}
#else
static int fsl_dspi_child_pre_probe(struct udevice *dev)
{
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
struct fsl_dspi_priv *priv = dev_get_priv(dev->parent);
if (slave_plat->cs >= priv->num_chipselect) {
debug("DSPI invalid chipselect number %d(max %d)!\n",
slave_plat->cs, priv->num_chipselect - 1);
return -EINVAL;
}
priv->ctar_val[slave_plat->cs] = DSPI_CTAR_DEFAULT_VALUE;
debug("DSPI pre_probe slave device on CS %u, max_hz %u, mode 0x%x.\n",
slave_plat->cs, slave_plat->max_hz, slave_plat->mode);
return 0;
}
static int fsl_dspi_probe(struct udevice *bus)
{
struct fsl_dspi_platdata *plat = dev_get_platdata(bus);
struct fsl_dspi_priv *priv = dev_get_priv(bus);
struct dm_spi_bus *dm_spi_bus;
uint mcr_cfg_val;
dm_spi_bus = bus->uclass_priv;
/* cpu speical pin muxing configure */
cpu_dspi_port_conf();
/* get input clk frequency */
priv->regs = (struct dspi *)plat->regs_addr;
priv->flags = plat->flags;
#ifdef CONFIG_M68K
priv->bus_clk = gd->bus_clk;
#else
priv->bus_clk = mxc_get_clock(MXC_DSPI_CLK);
#endif
priv->num_chipselect = plat->num_chipselect;
priv->speed_hz = plat->speed_hz;
/* frame data length in bits, default 8bits */
priv->charbit = 8;
dm_spi_bus->max_hz = plat->speed_hz;
/* default: all CS signals inactive state is high */
mcr_cfg_val = DSPI_MCR_MSTR | DSPI_MCR_PCSIS_MASK |
DSPI_MCR_CRXF | DSPI_MCR_CTXF;
fsl_dspi_init_mcr(priv, mcr_cfg_val);
debug("%s probe done, bus-num %d.\n", bus->name, bus->seq);
return 0;
}
static int fsl_dspi_claim_bus(struct udevice *dev)
{
uint sr_val;
struct fsl_dspi_priv *priv;
struct udevice *bus = dev->parent;
struct dm_spi_slave_platdata *slave_plat =
dev_get_parent_platdata(dev);
priv = dev_get_priv(bus);
/* processor special prepartion work */
cpu_dspi_claim_bus(bus->seq, slave_plat->cs);
/* configure transfer mode */
fsl_dspi_cfg_ctar_mode(priv, slave_plat->cs, priv->mode);
/* configure active state of CSX */
fsl_dspi_cfg_cs_active_state(priv, slave_plat->cs,
priv->mode);
fsl_dspi_clr_fifo(priv);
/* check module TX and RX status */
sr_val = dspi_read32(priv->flags, &priv->regs->sr);
if ((sr_val & DSPI_SR_TXRXS) != DSPI_SR_TXRXS) {
debug("DSPI RX/TX not ready!\n");
return -EIO;
}
return 0;
}
static int fsl_dspi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct fsl_dspi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave_plat =
dev_get_parent_platdata(dev);
/* halt module */
dspi_halt(priv, 1);
/* processor special release work */
cpu_dspi_release_bus(bus->seq, slave_plat->cs);
return 0;
}
/**
* This function doesn't do anything except help with debugging
*/
static int fsl_dspi_bind(struct udevice *bus)
{
debug("%s assigned req_seq %d.\n", bus->name, bus->req_seq);
return 0;
}
static int fsl_dspi_ofdata_to_platdata(struct udevice *bus)
{
fdt_addr_t addr;
struct fsl_dspi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
if (fdtdec_get_bool(blob, node, "big-endian"))
plat->flags |= DSPI_FLAG_REGMAP_ENDIAN_BIG;
plat->num_chipselect =
fdtdec_get_int(blob, node, "num-cs", FSL_DSPI_MAX_CHIPSELECT);
addr = dev_get_addr(bus);
if (addr == FDT_ADDR_T_NONE) {
debug("DSPI: Can't get base address or size\n");
return -ENOMEM;
}
plat->regs_addr = addr;
plat->speed_hz = fdtdec_get_int(blob,
node, "spi-max-frequency", FSL_DSPI_DEFAULT_SCK_FREQ);
debug("DSPI: regs=%pa, max-frequency=%d, endianess=%s, num-cs=%d\n",
&plat->regs_addr, plat->speed_hz,
plat->flags & DSPI_FLAG_REGMAP_ENDIAN_BIG ? "be" : "le",
plat->num_chipselect);
return 0;
}
static int fsl_dspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct fsl_dspi_priv *priv;
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
struct udevice *bus;
bus = dev->parent;
priv = dev_get_priv(bus);
return dspi_xfer(priv, slave_plat->cs, bitlen, dout, din, flags);
}
static int fsl_dspi_set_speed(struct udevice *bus, uint speed)
{
struct fsl_dspi_priv *priv = dev_get_priv(bus);
return fsl_dspi_cfg_speed(priv, speed);
}
static int fsl_dspi_set_mode(struct udevice *bus, uint mode)
{
struct fsl_dspi_priv *priv = dev_get_priv(bus);
debug("DSPI set_mode: mode 0x%x.\n", mode);
/*
* We store some chipselect special configure value in priv->ctar_val,
* and we can't get the correct chipselect number here,
* so just store mode value.
* Do really configuration when claim_bus.
*/
priv->mode = mode;
return 0;
}
static const struct dm_spi_ops fsl_dspi_ops = {
.claim_bus = fsl_dspi_claim_bus,
.release_bus = fsl_dspi_release_bus,
.xfer = fsl_dspi_xfer,
.set_speed = fsl_dspi_set_speed,
.set_mode = fsl_dspi_set_mode,
};
static const struct udevice_id fsl_dspi_ids[] = {
{ .compatible = "fsl,vf610-dspi" },
{ }
};
U_BOOT_DRIVER(fsl_dspi) = {
.name = "fsl_dspi",
.id = UCLASS_SPI,
.of_match = fsl_dspi_ids,
.ops = &fsl_dspi_ops,
.ofdata_to_platdata = fsl_dspi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct fsl_dspi_platdata),
.priv_auto_alloc_size = sizeof(struct fsl_dspi_priv),
.probe = fsl_dspi_probe,
.child_pre_probe = fsl_dspi_child_pre_probe,
.bind = fsl_dspi_bind,
};
#endif

View File

@@ -0,0 +1,389 @@
/*
* eSPI controller driver.
*
* Copyright 2010-2011 Freescale Semiconductor, Inc.
* Author: Mingkai Hu (Mingkai.hu@freescale.com)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/immap_85xx.h>
struct fsl_spi_slave {
struct spi_slave slave;
ccsr_espi_t *espi;
unsigned int div16;
unsigned int pm;
int tx_timeout;
unsigned int mode;
size_t cmd_len;
u8 cmd_buf[16];
size_t data_len;
unsigned int max_transfer_length;
};
#define to_fsl_spi_slave(s) container_of(s, struct fsl_spi_slave, slave)
#define US_PER_SECOND 1000000UL
#define ESPI_MAX_CS_NUM 4
#define ESPI_FIFO_WIDTH_BIT 32
#define ESPI_EV_RNE BIT(9)
#define ESPI_EV_TNF BIT(8)
#define ESPI_EV_DON BIT(14)
#define ESPI_EV_TXE BIT(15)
#define ESPI_EV_RFCNT_SHIFT 24
#define ESPI_EV_RFCNT_MASK (0x3f << ESPI_EV_RFCNT_SHIFT)
#define ESPI_MODE_EN BIT(31) /* Enable interface */
#define ESPI_MODE_TXTHR(x) ((x) << 8) /* Tx FIFO threshold */
#define ESPI_MODE_RXTHR(x) ((x) << 0) /* Rx FIFO threshold */
#define ESPI_COM_CS(x) ((x) << 30)
#define ESPI_COM_TRANLEN(x) ((x) << 0)
#define ESPI_CSMODE_CI_INACTIVEHIGH BIT(31)
#define ESPI_CSMODE_CP_BEGIN_EDGCLK BIT(30)
#define ESPI_CSMODE_REV_MSB_FIRST BIT(29)
#define ESPI_CSMODE_DIV16 BIT(28)
#define ESPI_CSMODE_PM(x) ((x) << 24)
#define ESPI_CSMODE_POL_ASSERTED_LOW BIT(20)
#define ESPI_CSMODE_LEN(x) ((x) << 16)
#define ESPI_CSMODE_CSBEF(x) ((x) << 12)
#define ESPI_CSMODE_CSAFT(x) ((x) << 8)
#define ESPI_CSMODE_CSCG(x) ((x) << 3)
#define ESPI_CSMODE_INIT_VAL (ESPI_CSMODE_POL_ASSERTED_LOW | \
ESPI_CSMODE_CSBEF(0) | ESPI_CSMODE_CSAFT(0) | \
ESPI_CSMODE_CSCG(1))
#define ESPI_MAX_DATA_TRANSFER_LEN 0xFFF0
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct fsl_spi_slave *fsl;
sys_info_t sysinfo;
unsigned long spibrg = 0;
unsigned long spi_freq = 0;
unsigned char pm = 0;
if (!spi_cs_is_valid(bus, cs))
return NULL;
fsl = spi_alloc_slave(struct fsl_spi_slave, bus, cs);
if (!fsl)
return NULL;
fsl->espi = (void *)(CONFIG_SYS_MPC85xx_ESPI_ADDR);
fsl->mode = mode;
fsl->max_transfer_length = ESPI_MAX_DATA_TRANSFER_LEN;
/* Set eSPI BRG clock source */
get_sys_info(&sysinfo);
spibrg = sysinfo.freq_systembus / 2;
fsl->div16 = 0;
if ((spibrg / max_hz) > 32) {
fsl->div16 = ESPI_CSMODE_DIV16;
pm = spibrg / (max_hz * 16 * 2);
if (pm > 16) {
pm = 16;
debug("Requested speed is too low: %d Hz, %ld Hz "
"is used.\n", max_hz, spibrg / (32 * 16));
}
} else
pm = spibrg / (max_hz * 2);
if (pm)
pm--;
fsl->pm = pm;
if (fsl->div16)
spi_freq = spibrg / ((pm + 1) * 2 * 16);
else
spi_freq = spibrg / ((pm + 1) * 2);
/* set tx_timeout to 10 times of one espi FIFO entry go out */
fsl->tx_timeout = DIV_ROUND_UP((US_PER_SECOND * ESPI_FIFO_WIDTH_BIT
* 10), spi_freq);
return &fsl->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave);
free(fsl);
}
void spi_init(void)
{
}
int spi_claim_bus(struct spi_slave *slave)
{
struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave);
ccsr_espi_t *espi = fsl->espi;
unsigned char pm = fsl->pm;
unsigned int cs = slave->cs;
unsigned int mode = fsl->mode;
unsigned int div16 = fsl->div16;
int i;
debug("%s: bus:%i cs:%i\n", __func__, slave->bus, cs);
/* Enable eSPI interface */
out_be32(&espi->mode, ESPI_MODE_RXTHR(3)
| ESPI_MODE_TXTHR(4) | ESPI_MODE_EN);
out_be32(&espi->event, 0xffffffff); /* Clear all eSPI events */
out_be32(&espi->mask, 0x00000000); /* Mask all eSPI interrupts */
/* Init CS mode interface */
for (i = 0; i < ESPI_MAX_CS_NUM; i++)
out_be32(&espi->csmode[i], ESPI_CSMODE_INIT_VAL);
out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs]) &
~(ESPI_CSMODE_PM(0xF) | ESPI_CSMODE_DIV16
| ESPI_CSMODE_CI_INACTIVEHIGH | ESPI_CSMODE_CP_BEGIN_EDGCLK
| ESPI_CSMODE_REV_MSB_FIRST | ESPI_CSMODE_LEN(0xF)));
/* Set eSPI BRG clock source */
out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs])
| ESPI_CSMODE_PM(pm) | div16);
/* Set eSPI mode */
if (mode & SPI_CPHA)
out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs])
| ESPI_CSMODE_CP_BEGIN_EDGCLK);
if (mode & SPI_CPOL)
out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs])
| ESPI_CSMODE_CI_INACTIVEHIGH);
/* Character bit order: msb first */
out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs])
| ESPI_CSMODE_REV_MSB_FIRST);
/* Character length in bits, between 0x3~0xf, i.e. 4bits~16bits */
out_be32(&espi->csmode[cs], in_be32(&espi->csmode[cs])
| ESPI_CSMODE_LEN(7));
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
static void fsl_espi_tx(struct fsl_spi_slave *fsl, const void *dout)
{
ccsr_espi_t *espi = fsl->espi;
unsigned int tmpdout, event;
int tmp_tx_timeout;
if (dout)
tmpdout = *(u32 *)dout;
else
tmpdout = 0;
out_be32(&espi->tx, tmpdout);
out_be32(&espi->event, ESPI_EV_TNF);
debug("***spi_xfer:...%08x written\n", tmpdout);
tmp_tx_timeout = fsl->tx_timeout;
/* Wait for eSPI transmit to go out */
while (tmp_tx_timeout--) {
event = in_be32(&espi->event);
if (event & ESPI_EV_DON || event & ESPI_EV_TXE) {
out_be32(&espi->event, ESPI_EV_TXE);
break;
}
udelay(1);
}
if (tmp_tx_timeout < 0)
debug("***spi_xfer:...Tx timeout! event = %08x\n", event);
}
static int fsl_espi_rx(struct fsl_spi_slave *fsl, void *din, unsigned int bytes)
{
ccsr_espi_t *espi = fsl->espi;
unsigned int tmpdin, rx_times;
unsigned char *buf, *p_cursor;
if (bytes <= 0)
return 0;
rx_times = DIV_ROUND_UP(bytes, 4);
buf = (unsigned char *)malloc(4 * rx_times);
if (!buf) {
debug("SF: Failed to malloc memory.\n");
return -1;
}
p_cursor = buf;
while (rx_times--) {
tmpdin = in_be32(&espi->rx);
debug("***spi_xfer:...%08x readed\n", tmpdin);
*(u32 *)p_cursor = tmpdin;
p_cursor += 4;
}
if (din)
memcpy(din, buf, bytes);
free(buf);
out_be32(&espi->event, ESPI_EV_RNE);
return bytes;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *data_out,
void *data_in, unsigned long flags)
{
struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave);
ccsr_espi_t *espi = fsl->espi;
unsigned int event, rx_bytes;
const void *dout = NULL;
void *din = NULL;
int len = 0;
int num_blks, num_chunks, max_tran_len, tran_len;
int num_bytes;
unsigned char *buffer = NULL;
size_t buf_len;
u8 *cmd_buf = fsl->cmd_buf;
size_t cmd_len = fsl->cmd_len;
size_t data_len = bitlen / 8;
size_t rx_offset = 0;
int rf_cnt;
max_tran_len = fsl->max_transfer_length;
switch (flags) {
case SPI_XFER_BEGIN:
cmd_len = fsl->cmd_len = data_len;
memcpy(cmd_buf, data_out, cmd_len);
return 0;
case 0:
case SPI_XFER_END:
if (bitlen == 0) {
spi_cs_deactivate(slave);
return 0;
}
buf_len = 2 * cmd_len + min(data_len, (size_t)max_tran_len);
len = cmd_len + data_len;
rx_offset = cmd_len;
buffer = (unsigned char *)malloc(buf_len);
if (!buffer) {
debug("SF: Failed to malloc memory.\n");
return 1;
}
memcpy(buffer, cmd_buf, cmd_len);
if (data_in == NULL)
memcpy(buffer + cmd_len, data_out, data_len);
break;
case SPI_XFER_BEGIN | SPI_XFER_END:
len = data_len;
buffer = (unsigned char *)malloc(len * 2);
if (!buffer) {
debug("SF: Failed to malloc memory.\n");
return 1;
}
memcpy(buffer, data_out, len);
rx_offset = len;
cmd_len = 0;
break;
}
debug("spi_xfer: data_out %08X(%p) data_in %08X(%p) len %u\n",
*(uint *)data_out, data_out, *(uint *)data_in, data_in, len);
num_chunks = DIV_ROUND_UP(data_len, max_tran_len);
while (num_chunks--) {
if (data_in)
din = buffer + rx_offset;
dout = buffer;
tran_len = min(data_len, (size_t)max_tran_len);
num_blks = DIV_ROUND_UP(tran_len + cmd_len, 4);
num_bytes = (tran_len + cmd_len) % 4;
fsl->data_len = tran_len + cmd_len;
spi_cs_activate(slave);
/* Clear all eSPI events */
out_be32(&espi->event , 0xffffffff);
/* handle data in 32-bit chunks */
while (num_blks) {
event = in_be32(&espi->event);
if (event & ESPI_EV_TNF) {
fsl_espi_tx(fsl, dout);
/* Set up the next iteration */
if (len > 4) {
len -= 4;
dout += 4;
}
}
event = in_be32(&espi->event);
if (event & ESPI_EV_RNE) {
rf_cnt = ((event & ESPI_EV_RFCNT_MASK)
>> ESPI_EV_RFCNT_SHIFT);
if (rf_cnt >= 4)
rx_bytes = 4;
else if (num_blks == 1 && rf_cnt == num_bytes)
rx_bytes = num_bytes;
else
continue;
if (fsl_espi_rx(fsl, din, rx_bytes)
== rx_bytes) {
num_blks--;
if (din)
din = (unsigned char *)din
+ rx_bytes;
}
}
}
if (data_in) {
memcpy(data_in, buffer + 2 * cmd_len, tran_len);
if (*buffer == 0x0b) {
data_in += tran_len;
data_len -= tran_len;
*(int *)buffer += tran_len;
}
}
spi_cs_deactivate(slave);
}
free(buffer);
return 0;
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return bus == 0 && cs < ESPI_MAX_CS_NUM;
}
void spi_cs_activate(struct spi_slave *slave)
{
struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave);
ccsr_espi_t *espi = fsl->espi;
unsigned int com = 0;
size_t data_len = fsl->data_len;
com &= ~(ESPI_COM_CS(0x3) | ESPI_COM_TRANLEN(0xFFFF));
com |= ESPI_COM_CS(slave->cs);
com |= ESPI_COM_TRANLEN(data_len - 1);
out_be32(&espi->com, com);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct fsl_spi_slave *fsl = to_fsl_spi_slave(slave);
ccsr_espi_t *espi = fsl->espi;
/* clear the RXCNT and TXCNT */
out_be32(&espi->mode, in_be32(&espi->mode) & (~ESPI_MODE_EN));
out_be32(&espi->mode, in_be32(&espi->mode) | ESPI_MODE_EN);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,142 @@
/*
* Copyright 2013-2014 Freescale Semiconductor, Inc.
*
* Register definitions for Freescale QSPI
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef _FSL_QSPI_H_
#define _FSL_QSPI_H_
struct fsl_qspi_regs {
u32 mcr;
u32 rsvd0[1];
u32 ipcr;
u32 flshcr;
u32 buf0cr;
u32 buf1cr;
u32 buf2cr;
u32 buf3cr;
u32 bfgencr;
u32 soccr;
u32 rsvd1[2];
u32 buf0ind;
u32 buf1ind;
u32 buf2ind;
u32 rsvd2[49];
u32 sfar;
u32 rsvd3[1];
u32 smpr;
u32 rbsr;
u32 rbct;
u32 rsvd4[15];
u32 tbsr;
u32 tbdr;
u32 rsvd5[1];
u32 sr;
u32 fr;
u32 rser;
u32 spndst;
u32 sptrclr;
u32 rsvd6[4];
u32 sfa1ad;
u32 sfa2ad;
u32 sfb1ad;
u32 sfb2ad;
u32 rsvd7[28];
u32 rbdr[32];
u32 rsvd8[32];
u32 lutkey;
u32 lckcr;
u32 rsvd9[2];
u32 lut[64];
};
#define QSPI_IPCR_SEQID_SHIFT 24
#define QSPI_IPCR_SEQID_MASK (0xf << QSPI_IPCR_SEQID_SHIFT)
#define QSPI_MCR_END_CFD_SHIFT 2
#define QSPI_MCR_END_CFD_MASK (3 << QSPI_MCR_END_CFD_SHIFT)
#ifdef CONFIG_SYS_FSL_QSPI_AHB
/* AHB needs 64bit operation */
#define QSPI_MCR_END_CFD_LE (3 << QSPI_MCR_END_CFD_SHIFT)
#else
#define QSPI_MCR_END_CFD_LE (1 << QSPI_MCR_END_CFD_SHIFT)
#endif
#define QSPI_MCR_DDR_EN_SHIFT 7
#define QSPI_MCR_DDR_EN_MASK (1 << QSPI_MCR_DDR_EN_SHIFT)
#define QSPI_MCR_CLR_RXF_SHIFT 10
#define QSPI_MCR_CLR_RXF_MASK (1 << QSPI_MCR_CLR_RXF_SHIFT)
#define QSPI_MCR_CLR_TXF_SHIFT 11
#define QSPI_MCR_CLR_TXF_MASK (1 << QSPI_MCR_CLR_TXF_SHIFT)
#define QSPI_MCR_MDIS_SHIFT 14
#define QSPI_MCR_MDIS_MASK (1 << QSPI_MCR_MDIS_SHIFT)
#define QSPI_MCR_RESERVED_SHIFT 16
#define QSPI_MCR_RESERVED_MASK (0xf << QSPI_MCR_RESERVED_SHIFT)
#define QSPI_MCR_SWRSTHD_SHIFT 1
#define QSPI_MCR_SWRSTHD_MASK (1 << QSPI_MCR_SWRSTHD_SHIFT)
#define QSPI_MCR_SWRSTSD_SHIFT 0
#define QSPI_MCR_SWRSTSD_MASK (1 << QSPI_MCR_SWRSTSD_SHIFT)
#define QSPI_SMPR_HSENA_SHIFT 0
#define QSPI_SMPR_HSENA_MASK (1 << QSPI_SMPR_HSENA_SHIFT)
#define QSPI_SMPR_FSPHS_SHIFT 5
#define QSPI_SMPR_FSPHS_MASK (1 << QSPI_SMPR_FSPHS_SHIFT)
#define QSPI_SMPR_FSDLY_SHIFT 6
#define QSPI_SMPR_FSDLY_MASK (1 << QSPI_SMPR_FSDLY_SHIFT)
#define QSPI_SMPR_DDRSMP_SHIFT 16
#define QSPI_SMPR_DDRSMP_MASK (7 << QSPI_SMPR_DDRSMP_SHIFT)
#define QSPI_BUFXCR_INVALID_MSTRID 0xe
#define QSPI_BUF3CR_ALLMST_SHIFT 31
#define QSPI_BUF3CR_ALLMST_MASK (1 << QSPI_BUF3CR_ALLMST_SHIFT)
#define QSPI_BUF3CR_ADATSZ_SHIFT 8
#define QSPI_BUF3CR_ADATSZ_MASK (0xFF << QSPI_BUF3CR_ADATSZ_SHIFT)
#define QSPI_BFGENCR_SEQID_SHIFT 12
#define QSPI_BFGENCR_SEQID_MASK (0xf << QSPI_BFGENCR_SEQID_SHIFT)
#define QSPI_BFGENCR_PAR_EN_SHIFT 16
#define QSPI_BFGENCR_PAR_EN_MASK (1 << QSPI_BFGENCR_PAR_EN_SHIFT)
#define QSPI_RBSR_RDBFL_SHIFT 8
#define QSPI_RBSR_RDBFL_MASK (0x3f << QSPI_RBSR_RDBFL_SHIFT)
#define QSPI_RBCT_RXBRD_SHIFT 8
#define QSPI_RBCT_RXBRD_USEIPS (1 << QSPI_RBCT_RXBRD_SHIFT)
#define QSPI_SR_BUSY_SHIFT 0
#define QSPI_SR_BUSY_MASK (1 << QSPI_SR_BUSY_SHIFT)
#define QSPI_LCKCR_LOCK 0x1
#define QSPI_LCKCR_UNLOCK 0x2
#define LUT_KEY_VALUE 0x5af05af0
#define OPRND0_SHIFT 0
#define OPRND0(x) ((x) << OPRND0_SHIFT)
#define PAD0_SHIFT 8
#define PAD0(x) ((x) << PAD0_SHIFT)
#define INSTR0_SHIFT 10
#define INSTR0(x) ((x) << INSTR0_SHIFT)
#define OPRND1_SHIFT 16
#define OPRND1(x) ((x) << OPRND1_SHIFT)
#define PAD1_SHIFT 24
#define PAD1(x) ((x) << PAD1_SHIFT)
#define INSTR1_SHIFT 26
#define INSTR1(x) ((x) << INSTR1_SHIFT)
#define LUT_CMD 1
#define LUT_ADDR 2
#define LUT_DUMMY 3
#define LUT_READ 7
#define LUT_WRITE 8
#define LUT_PAD1 0
#define LUT_PAD2 1
#define LUT_PAD4 2
#define ADDR24BIT 0x18
#define ADDR32BIT 0x20
#endif /* _FSL_QSPI_H_ */

705
u-boot/drivers/spi/ich.c Normal file
View File

@@ -0,0 +1,705 @@
/*
* Copyright (c) 2011-12 The Chromium OS Authors.
*
* SPDX-License-Identifier: GPL-2.0+
*
* This file is derived from the flashrom project.
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <pch.h>
#include <pci.h>
#include <pci_ids.h>
#include <spi.h>
#include <asm/io.h>
#include "ich.h"
DECLARE_GLOBAL_DATA_PTR;
#ifdef DEBUG_TRACE
#define debug_trace(fmt, args...) debug(fmt, ##args)
#else
#define debug_trace(x, args...)
#endif
static u8 ich_readb(struct ich_spi_priv *priv, int reg)
{
u8 value = readb(priv->base + reg);
debug_trace("read %2.2x from %4.4x\n", value, reg);
return value;
}
static u16 ich_readw(struct ich_spi_priv *priv, int reg)
{
u16 value = readw(priv->base + reg);
debug_trace("read %4.4x from %4.4x\n", value, reg);
return value;
}
static u32 ich_readl(struct ich_spi_priv *priv, int reg)
{
u32 value = readl(priv->base + reg);
debug_trace("read %8.8x from %4.4x\n", value, reg);
return value;
}
static void ich_writeb(struct ich_spi_priv *priv, u8 value, int reg)
{
writeb(value, priv->base + reg);
debug_trace("wrote %2.2x to %4.4x\n", value, reg);
}
static void ich_writew(struct ich_spi_priv *priv, u16 value, int reg)
{
writew(value, priv->base + reg);
debug_trace("wrote %4.4x to %4.4x\n", value, reg);
}
static void ich_writel(struct ich_spi_priv *priv, u32 value, int reg)
{
writel(value, priv->base + reg);
debug_trace("wrote %8.8x to %4.4x\n", value, reg);
}
static void write_reg(struct ich_spi_priv *priv, const void *value,
int dest_reg, uint32_t size)
{
memcpy_toio(priv->base + dest_reg, value, size);
}
static void read_reg(struct ich_spi_priv *priv, int src_reg, void *value,
uint32_t size)
{
memcpy_fromio(value, priv->base + src_reg, size);
}
static void ich_set_bbar(struct ich_spi_priv *ctlr, uint32_t minaddr)
{
const uint32_t bbar_mask = 0x00ffff00;
uint32_t ichspi_bbar;
minaddr &= bbar_mask;
ichspi_bbar = ich_readl(ctlr, ctlr->bbar) & ~bbar_mask;
ichspi_bbar |= minaddr;
ich_writel(ctlr, ichspi_bbar, ctlr->bbar);
}
/* @return 1 if the SPI flash supports the 33MHz speed */
static int ich9_can_do_33mhz(struct udevice *dev)
{
u32 fdod, speed;
/* Observe SPI Descriptor Component Section 0 */
dm_pci_write_config32(dev->parent, 0xb0, 0x1000);
/* Extract the Write/Erase SPI Frequency from descriptor */
dm_pci_read_config32(dev->parent, 0xb4, &fdod);
/* Bits 23:21 have the fast read clock frequency, 0=20MHz, 1=33MHz */
speed = (fdod >> 21) & 7;
return speed == 1;
}
static int ich_init_controller(struct udevice *dev,
struct ich_spi_platdata *plat,
struct ich_spi_priv *ctlr)
{
ulong sbase_addr;
void *sbase;
/* SBASE is similar */
pch_get_spi_base(dev->parent, &sbase_addr);
sbase = (void *)sbase_addr;
debug("%s: sbase=%p\n", __func__, sbase);
if (plat->ich_version == ICHV_7) {
struct ich7_spi_regs *ich7_spi = sbase;
ich7_spi = (struct ich7_spi_regs *)sbase;
ctlr->ichspi_lock = readw(&ich7_spi->spis) & SPIS_LOCK;
ctlr->opmenu = offsetof(struct ich7_spi_regs, opmenu);
ctlr->menubytes = sizeof(ich7_spi->opmenu);
ctlr->optype = offsetof(struct ich7_spi_regs, optype);
ctlr->addr = offsetof(struct ich7_spi_regs, spia);
ctlr->data = offsetof(struct ich7_spi_regs, spid);
ctlr->databytes = sizeof(ich7_spi->spid);
ctlr->status = offsetof(struct ich7_spi_regs, spis);
ctlr->control = offsetof(struct ich7_spi_regs, spic);
ctlr->bbar = offsetof(struct ich7_spi_regs, bbar);
ctlr->preop = offsetof(struct ich7_spi_regs, preop);
ctlr->base = ich7_spi;
} else if (plat->ich_version == ICHV_9) {
struct ich9_spi_regs *ich9_spi = sbase;
ctlr->ichspi_lock = readw(&ich9_spi->hsfs) & HSFS_FLOCKDN;
ctlr->opmenu = offsetof(struct ich9_spi_regs, opmenu);
ctlr->menubytes = sizeof(ich9_spi->opmenu);
ctlr->optype = offsetof(struct ich9_spi_regs, optype);
ctlr->addr = offsetof(struct ich9_spi_regs, faddr);
ctlr->data = offsetof(struct ich9_spi_regs, fdata);
ctlr->databytes = sizeof(ich9_spi->fdata);
ctlr->status = offsetof(struct ich9_spi_regs, ssfs);
ctlr->control = offsetof(struct ich9_spi_regs, ssfc);
ctlr->speed = ctlr->control + 2;
ctlr->bbar = offsetof(struct ich9_spi_regs, bbar);
ctlr->preop = offsetof(struct ich9_spi_regs, preop);
ctlr->bcr = offsetof(struct ich9_spi_regs, bcr);
ctlr->pr = &ich9_spi->pr[0];
ctlr->base = ich9_spi;
} else {
debug("ICH SPI: Unrecognised ICH version %d\n",
plat->ich_version);
return -EINVAL;
}
/* Work out the maximum speed we can support */
ctlr->max_speed = 20000000;
if (plat->ich_version == ICHV_9 && ich9_can_do_33mhz(dev))
ctlr->max_speed = 33000000;
debug("ICH SPI: Version ID %d detected at %p, speed %ld\n",
plat->ich_version, ctlr->base, ctlr->max_speed);
ich_set_bbar(ctlr, 0);
return 0;
}
static inline void spi_use_out(struct spi_trans *trans, unsigned bytes)
{
trans->out += bytes;
trans->bytesout -= bytes;
}
static inline void spi_use_in(struct spi_trans *trans, unsigned bytes)
{
trans->in += bytes;
trans->bytesin -= bytes;
}
static void spi_setup_type(struct spi_trans *trans, int data_bytes)
{
trans->type = 0xFF;
/* Try to guess spi type from read/write sizes */
if (trans->bytesin == 0) {
if (trans->bytesout + data_bytes > 4)
/*
* If bytesin = 0 and bytesout > 4, we presume this is
* a write data operation, which is accompanied by an
* address.
*/
trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS;
else
trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS;
return;
}
if (trans->bytesout == 1) { /* and bytesin is > 0 */
trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS;
return;
}
if (trans->bytesout == 4) /* and bytesin is > 0 */
trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
/* Fast read command is called with 5 bytes instead of 4 */
if (trans->out[0] == SPI_OPCODE_FAST_READ && trans->bytesout == 5) {
trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
--trans->bytesout;
}
}
static int spi_setup_opcode(struct ich_spi_priv *ctlr, struct spi_trans *trans)
{
uint16_t optypes;
uint8_t opmenu[ctlr->menubytes];
trans->opcode = trans->out[0];
spi_use_out(trans, 1);
if (!ctlr->ichspi_lock) {
/* The lock is off, so just use index 0. */
ich_writeb(ctlr, trans->opcode, ctlr->opmenu);
optypes = ich_readw(ctlr, ctlr->optype);
optypes = (optypes & 0xfffc) | (trans->type & 0x3);
ich_writew(ctlr, optypes, ctlr->optype);
return 0;
} else {
/* The lock is on. See if what we need is on the menu. */
uint8_t optype;
uint16_t opcode_index;
/* Write Enable is handled as atomic prefix */
if (trans->opcode == SPI_OPCODE_WREN)
return 0;
read_reg(ctlr, ctlr->opmenu, opmenu, sizeof(opmenu));
for (opcode_index = 0; opcode_index < ctlr->menubytes;
opcode_index++) {
if (opmenu[opcode_index] == trans->opcode)
break;
}
if (opcode_index == ctlr->menubytes) {
printf("ICH SPI: Opcode %x not found\n",
trans->opcode);
return -EINVAL;
}
optypes = ich_readw(ctlr, ctlr->optype);
optype = (optypes >> (opcode_index * 2)) & 0x3;
if (trans->type == SPI_OPCODE_TYPE_WRITE_NO_ADDRESS &&
optype == SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS &&
trans->bytesout >= 3) {
/* We guessed wrong earlier. Fix it up. */
trans->type = optype;
}
if (optype != trans->type) {
printf("ICH SPI: Transaction doesn't fit type %d\n",
optype);
return -ENOSPC;
}
return opcode_index;
}
}
static int spi_setup_offset(struct spi_trans *trans)
{
/* Separate the SPI address and data */
switch (trans->type) {
case SPI_OPCODE_TYPE_READ_NO_ADDRESS:
case SPI_OPCODE_TYPE_WRITE_NO_ADDRESS:
return 0;
case SPI_OPCODE_TYPE_READ_WITH_ADDRESS:
case SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS:
trans->offset = ((uint32_t)trans->out[0] << 16) |
((uint32_t)trans->out[1] << 8) |
((uint32_t)trans->out[2] << 0);
spi_use_out(trans, 3);
return 1;
default:
printf("Unrecognized SPI transaction type %#x\n", trans->type);
return -EPROTO;
}
}
/*
* Wait for up to 6s til status register bit(s) turn 1 (in case wait_til_set
* below is true) or 0. In case the wait was for the bit(s) to set - write
* those bits back, which would cause resetting them.
*
* Return the last read status value on success or -1 on failure.
*/
static int ich_status_poll(struct ich_spi_priv *ctlr, u16 bitmask,
int wait_til_set)
{
int timeout = 600000; /* This will result in 6s */
u16 status = 0;
while (timeout--) {
status = ich_readw(ctlr, ctlr->status);
if (wait_til_set ^ ((status & bitmask) == 0)) {
if (wait_til_set) {
ich_writew(ctlr, status & bitmask,
ctlr->status);
}
return status;
}
udelay(10);
}
printf("ICH SPI: SCIP timeout, read %x, expected %x\n",
status, bitmask);
return -ETIMEDOUT;
}
static int ich_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct ich_spi_platdata *plat = dev_get_platdata(bus);
struct ich_spi_priv *ctlr = dev_get_priv(bus);
uint16_t control;
int16_t opcode_index;
int with_address;
int status;
int bytes = bitlen / 8;
struct spi_trans *trans = &ctlr->trans;
unsigned type = flags & (SPI_XFER_BEGIN | SPI_XFER_END);
int using_cmd = 0;
int ret;
/* We don't support writing partial bytes */
if (bitlen % 8) {
debug("ICH SPI: Accessing partial bytes not supported\n");
return -EPROTONOSUPPORT;
}
/* An empty end transaction can be ignored */
if (type == SPI_XFER_END && !dout && !din)
return 0;
if (type & SPI_XFER_BEGIN)
memset(trans, '\0', sizeof(*trans));
/* Dp we need to come back later to finish it? */
if (dout && type == SPI_XFER_BEGIN) {
if (bytes > ICH_MAX_CMD_LEN) {
debug("ICH SPI: Command length limit exceeded\n");
return -ENOSPC;
}
memcpy(trans->cmd, dout, bytes);
trans->cmd_len = bytes;
debug_trace("ICH SPI: Saved %d bytes\n", bytes);
return 0;
}
/*
* We process a 'middle' spi_xfer() call, which has no
* SPI_XFER_BEGIN/END, as an independent transaction as if it had
* an end. We therefore repeat the command. This is because ICH
* seems to have no support for this, or because interest (in digging
* out the details and creating a special case in the code) is low.
*/
if (trans->cmd_len) {
trans->out = trans->cmd;
trans->bytesout = trans->cmd_len;
using_cmd = 1;
debug_trace("ICH SPI: Using %d bytes\n", trans->cmd_len);
} else {
trans->out = dout;
trans->bytesout = dout ? bytes : 0;
}
trans->in = din;
trans->bytesin = din ? bytes : 0;
/* There has to always at least be an opcode */
if (!trans->bytesout) {
debug("ICH SPI: No opcode for transfer\n");
return -EPROTO;
}
ret = ich_status_poll(ctlr, SPIS_SCIP, 0);
if (ret < 0)
return ret;
if (plat->ich_version == ICHV_7)
ich_writew(ctlr, SPIS_CDS | SPIS_FCERR, ctlr->status);
else
ich_writeb(ctlr, SPIS_CDS | SPIS_FCERR, ctlr->status);
spi_setup_type(trans, using_cmd ? bytes : 0);
opcode_index = spi_setup_opcode(ctlr, trans);
if (opcode_index < 0)
return -EINVAL;
with_address = spi_setup_offset(trans);
if (with_address < 0)
return -EINVAL;
if (trans->opcode == SPI_OPCODE_WREN) {
/*
* Treat Write Enable as Atomic Pre-Op if possible
* in order to prevent the Management Engine from
* issuing a transaction between WREN and DATA.
*/
if (!ctlr->ichspi_lock)
ich_writew(ctlr, trans->opcode, ctlr->preop);
return 0;
}
if (ctlr->speed && ctlr->max_speed >= 33000000) {
int byte;
byte = ich_readb(ctlr, ctlr->speed);
if (ctlr->cur_speed >= 33000000)
byte |= SSFC_SCF_33MHZ;
else
byte &= ~SSFC_SCF_33MHZ;
ich_writeb(ctlr, byte, ctlr->speed);
}
/* See if we have used up the command data */
if (using_cmd && dout && bytes) {
trans->out = dout;
trans->bytesout = bytes;
debug_trace("ICH SPI: Moving to data, %d bytes\n", bytes);
}
/* Preset control fields */
control = ich_readw(ctlr, ctlr->control);
control &= ~SSFC_RESERVED;
control = SPIC_SCGO | ((opcode_index & 0x07) << 4);
/* Issue atomic preop cycle if needed */
if (ich_readw(ctlr, ctlr->preop))
control |= SPIC_ACS;
if (!trans->bytesout && !trans->bytesin) {
/* SPI addresses are 24 bit only */
if (with_address) {
ich_writel(ctlr, trans->offset & 0x00FFFFFF,
ctlr->addr);
}
/*
* This is a 'no data' command (like Write Enable), its
* bitesout size was 1, decremented to zero while executing
* spi_setup_opcode() above. Tell the chip to send the
* command.
*/
ich_writew(ctlr, control, ctlr->control);
/* wait for the result */
status = ich_status_poll(ctlr, SPIS_CDS | SPIS_FCERR, 1);
if (status < 0)
return status;
if (status & SPIS_FCERR) {
debug("ICH SPI: Command transaction error\n");
return -EIO;
}
return 0;
}
/*
* Check if this is a write command atempting to transfer more bytes
* than the controller can handle. Iterations for writes are not
* supported here because each SPI write command needs to be preceded
* and followed by other SPI commands, and this sequence is controlled
* by the SPI chip driver.
*/
if (trans->bytesout > ctlr->databytes) {
debug("ICH SPI: Too much to write. This should be prevented by the driver's max_write_size?\n");
return -EPROTO;
}
/*
* Read or write up to databytes bytes at a time until everything has
* been sent.
*/
while (trans->bytesout || trans->bytesin) {
uint32_t data_length;
/* SPI addresses are 24 bit only */
ich_writel(ctlr, trans->offset & 0x00FFFFFF, ctlr->addr);
if (trans->bytesout)
data_length = min(trans->bytesout, ctlr->databytes);
else
data_length = min(trans->bytesin, ctlr->databytes);
/* Program data into FDATA0 to N */
if (trans->bytesout) {
write_reg(ctlr, trans->out, ctlr->data, data_length);
spi_use_out(trans, data_length);
if (with_address)
trans->offset += data_length;
}
/* Add proper control fields' values */
control &= ~((ctlr->databytes - 1) << 8);
control |= SPIC_DS;
control |= (data_length - 1) << 8;
/* write it */
ich_writew(ctlr, control, ctlr->control);
/* Wait for Cycle Done Status or Flash Cycle Error */
status = ich_status_poll(ctlr, SPIS_CDS | SPIS_FCERR, 1);
if (status < 0)
return status;
if (status & SPIS_FCERR) {
debug("ICH SPI: Data transaction error %x\n", status);
return -EIO;
}
if (trans->bytesin) {
read_reg(ctlr, ctlr->data, trans->in, data_length);
spi_use_in(trans, data_length);
if (with_address)
trans->offset += data_length;
}
}
/* Clear atomic preop now that xfer is done */
ich_writew(ctlr, 0, ctlr->preop);
return 0;
}
/*
* This uses the SPI controller from the Intel Cougar Point and Panther Point
* PCH to write-protect portions of the SPI flash until reboot. The changes
* don't actually take effect until the HSFS[FLOCKDN] bit is set, but that's
* done elsewhere.
*/
int spi_write_protect_region(struct udevice *dev, uint32_t lower_limit,
uint32_t length, int hint)
{
struct udevice *bus = dev->parent;
struct ich_spi_priv *ctlr = dev_get_priv(bus);
uint32_t tmplong;
uint32_t upper_limit;
if (!ctlr->pr) {
printf("%s: operation not supported on this chipset\n",
__func__);
return -ENOSYS;
}
if (length == 0 ||
lower_limit > (0xFFFFFFFFUL - length) + 1 ||
hint < 0 || hint > 4) {
printf("%s(0x%x, 0x%x, %d): invalid args\n", __func__,
lower_limit, length, hint);
return -EPERM;
}
upper_limit = lower_limit + length - 1;
/*
* Determine bits to write, as follows:
* 31 Write-protection enable (includes erase operation)
* 30:29 reserved
* 28:16 Upper Limit (FLA address bits 24:12, with 11:0 == 0xfff)
* 15 Read-protection enable
* 14:13 reserved
* 12:0 Lower Limit (FLA address bits 24:12, with 11:0 == 0x000)
*/
tmplong = 0x80000000 |
((upper_limit & 0x01fff000) << 4) |
((lower_limit & 0x01fff000) >> 12);
printf("%s: writing 0x%08x to %p\n", __func__, tmplong,
&ctlr->pr[hint]);
ctlr->pr[hint] = tmplong;
return 0;
}
static int ich_spi_probe(struct udevice *dev)
{
struct ich_spi_platdata *plat = dev_get_platdata(dev);
struct ich_spi_priv *priv = dev_get_priv(dev);
uint8_t bios_cntl;
int ret;
ret = ich_init_controller(dev, plat, priv);
if (ret)
return ret;
/* Disable the BIOS write protect so write commands are allowed */
ret = pch_set_spi_protect(dev->parent, false);
if (ret == -ENOSYS) {
bios_cntl = ich_readb(priv, priv->bcr);
bios_cntl &= ~BIT(5); /* clear Enable InSMM_STS (EISS) */
bios_cntl |= 1; /* Write Protect Disable (WPD) */
ich_writeb(priv, bios_cntl, priv->bcr);
} else if (ret) {
debug("%s: Failed to disable write-protect: err=%d\n",
__func__, ret);
return ret;
}
priv->cur_speed = priv->max_speed;
return 0;
}
static int ich_spi_set_speed(struct udevice *bus, uint speed)
{
struct ich_spi_priv *priv = dev_get_priv(bus);
priv->cur_speed = speed;
return 0;
}
static int ich_spi_set_mode(struct udevice *bus, uint mode)
{
debug("%s: mode=%d\n", __func__, mode);
return 0;
}
static int ich_spi_child_pre_probe(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct ich_spi_platdata *plat = dev_get_platdata(bus);
struct ich_spi_priv *priv = dev_get_priv(bus);
struct spi_slave *slave = dev_get_parent_priv(dev);
/*
* Yes this controller can only write a small number of bytes at
* once! The limit is typically 64 bytes.
*/
slave->max_write_size = priv->databytes;
/*
* ICH 7 SPI controller only supports array read command
* and byte program command for SST flash
*/
if (plat->ich_version == ICHV_7) {
slave->mode_rx = SPI_RX_SLOW;
slave->mode = SPI_TX_BYTE;
}
return 0;
}
static int ich_spi_ofdata_to_platdata(struct udevice *dev)
{
struct ich_spi_platdata *plat = dev_get_platdata(dev);
int ret;
ret = fdt_node_check_compatible(gd->fdt_blob, dev->of_offset,
"intel,ich7-spi");
if (ret == 0) {
plat->ich_version = ICHV_7;
} else {
ret = fdt_node_check_compatible(gd->fdt_blob, dev->of_offset,
"intel,ich9-spi");
if (ret == 0)
plat->ich_version = ICHV_9;
}
return ret;
}
static const struct dm_spi_ops ich_spi_ops = {
.xfer = ich_spi_xfer,
.set_speed = ich_spi_set_speed,
.set_mode = ich_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id ich_spi_ids[] = {
{ .compatible = "intel,ich7-spi" },
{ .compatible = "intel,ich9-spi" },
{ }
};
U_BOOT_DRIVER(ich_spi) = {
.name = "ich_spi",
.id = UCLASS_SPI,
.of_match = ich_spi_ids,
.ops = &ich_spi_ops,
.ofdata_to_platdata = ich_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct ich_spi_platdata),
.priv_auto_alloc_size = sizeof(struct ich_spi_priv),
.child_pre_probe = ich_spi_child_pre_probe,
.probe = ich_spi_probe,
};

161
u-boot/drivers/spi/ich.h Normal file
View File

@@ -0,0 +1,161 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
*
* SPDX-License-Identifier: GPL-2.0+
*
* This file is derived from the flashrom project.
*/
#ifndef _ICH_H_
#define _ICH_H_
struct ich7_spi_regs {
uint16_t spis;
uint16_t spic;
uint32_t spia;
uint64_t spid[8];
uint64_t _pad;
uint32_t bbar;
uint16_t preop;
uint16_t optype;
uint8_t opmenu[8];
} __packed;
struct ich9_spi_regs {
uint32_t bfpr; /* 0x00 */
uint16_t hsfs;
uint16_t hsfc;
uint32_t faddr;
uint32_t _reserved0;
uint32_t fdata[16]; /* 0x10 */
uint32_t frap; /* 0x50 */
uint32_t freg[5];
uint32_t _reserved1[3];
uint32_t pr[5]; /* 0x74 */
uint32_t _reserved2[2];
uint8_t ssfs; /* 0x90 */
uint8_t ssfc[3];
uint16_t preop; /* 0x94 */
uint16_t optype;
uint8_t opmenu[8]; /* 0x98 */
uint32_t bbar;
uint8_t _reserved3[12];
uint32_t fdoc; /* 0xb0 */
uint32_t fdod;
uint8_t _reserved4[8];
uint32_t afc; /* 0xc0 */
uint32_t lvscc;
uint32_t uvscc;
uint8_t _reserved5[4];
uint32_t fpb; /* 0xd0 */
uint8_t _reserved6[28];
uint32_t srdl; /* 0xf0 */
uint32_t srdc;
uint32_t scs;
uint32_t bcr;
} __packed;
enum {
SPIS_SCIP = 0x0001,
SPIS_GRANT = 0x0002,
SPIS_CDS = 0x0004,
SPIS_FCERR = 0x0008,
SSFS_AEL = 0x0010,
SPIS_LOCK = 0x8000,
SPIS_RESERVED_MASK = 0x7ff0,
SSFS_RESERVED_MASK = 0x7fe2
};
enum {
SPIC_SCGO = 0x000002,
SPIC_ACS = 0x000004,
SPIC_SPOP = 0x000008,
SPIC_DBC = 0x003f00,
SPIC_DS = 0x004000,
SPIC_SME = 0x008000,
SSFC_SCF_MASK = 0x070000,
SSFC_RESERVED = 0xf80000,
/* Mask for speed byte, biuts 23:16 of SSFC */
SSFC_SCF_33MHZ = 0x01,
};
enum {
HSFS_FDONE = 0x0001,
HSFS_FCERR = 0x0002,
HSFS_AEL = 0x0004,
HSFS_BERASE_MASK = 0x0018,
HSFS_BERASE_SHIFT = 3,
HSFS_SCIP = 0x0020,
HSFS_FDOPSS = 0x2000,
HSFS_FDV = 0x4000,
HSFS_FLOCKDN = 0x8000
};
enum {
HSFC_FGO = 0x0001,
HSFC_FCYCLE_MASK = 0x0006,
HSFC_FCYCLE_SHIFT = 1,
HSFC_FDBC_MASK = 0x3f00,
HSFC_FDBC_SHIFT = 8,
HSFC_FSMIE = 0x8000
};
enum {
SPI_OPCODE_TYPE_READ_NO_ADDRESS = 0,
SPI_OPCODE_TYPE_WRITE_NO_ADDRESS = 1,
SPI_OPCODE_TYPE_READ_WITH_ADDRESS = 2,
SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS = 3
};
enum {
ICH_MAX_CMD_LEN = 5,
};
struct spi_trans {
uint8_t cmd[ICH_MAX_CMD_LEN];
int cmd_len;
const uint8_t *out;
uint32_t bytesout;
uint8_t *in;
uint32_t bytesin;
uint8_t type;
uint8_t opcode;
uint32_t offset;
};
#define SPI_OPCODE_WREN 0x06
#define SPI_OPCODE_FAST_READ 0x0b
enum ich_version {
ICHV_7,
ICHV_9,
};
struct ich_spi_platdata {
enum ich_version ich_version; /* Controller version, 7 or 9 */
};
struct ich_spi_priv {
int ichspi_lock;
int locked;
int opmenu;
int menubytes;
void *base; /* Base of register set */
int preop;
int optype;
int addr;
int data;
unsigned databytes;
int status;
int control;
int bbar;
int bcr;
uint32_t *pr; /* only for ich9 */
int speed; /* pointer to speed control */
ulong max_speed; /* Maximum bus speed in MHz */
ulong cur_speed; /* Current bus speed */
struct spi_trans trans; /* current transaction in progress */
};
#endif /* _ICH_H_ */

View File

@@ -0,0 +1,348 @@
/*
* (C) Copyright 2009
* Marvell Semiconductor <www.marvell.com>
* Written-by: Prafulla Wadaskar <prafulla@marvell.com>
*
* Derived from drivers/spi/mpc8xxx_spi.c
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
#include <asm/arch/soc.h>
#ifdef CONFIG_KIRKWOOD
#include <asm/arch/mpp.h>
#endif
#include <asm/arch-mvebu/spi.h>
static void _spi_cs_activate(struct kwspi_registers *reg)
{
setbits_le32(&reg->ctrl, KWSPI_CSN_ACT);
}
static void _spi_cs_deactivate(struct kwspi_registers *reg)
{
clrbits_le32(&reg->ctrl, KWSPI_CSN_ACT);
}
static int _spi_xfer(struct kwspi_registers *reg, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
unsigned int tmpdout, tmpdin;
int tm, isread = 0;
debug("spi_xfer: dout %p din %p bitlen %u\n", dout, din, bitlen);
if (flags & SPI_XFER_BEGIN)
_spi_cs_activate(reg);
/*
* handle data in 8-bit chunks
* TBD: 2byte xfer mode to be enabled
*/
clrsetbits_le32(&reg->cfg, KWSPI_XFERLEN_MASK, KWSPI_XFERLEN_1BYTE);
while (bitlen > 4) {
debug("loopstart bitlen %d\n", bitlen);
tmpdout = 0;
/* Shift data so it's msb-justified */
if (dout)
tmpdout = *(u32 *)dout & 0xff;
clrbits_le32(&reg->irq_cause, KWSPI_SMEMRDIRQ);
writel(tmpdout, &reg->dout); /* Write the data out */
debug("*** spi_xfer: ... %08x written, bitlen %d\n",
tmpdout, bitlen);
/*
* Wait for SPI transmit to get out
* or time out (1 second = 1000 ms)
* The NE event must be read and cleared first
*/
for (tm = 0, isread = 0; tm < KWSPI_TIMEOUT; ++tm) {
if (readl(&reg->irq_cause) & KWSPI_SMEMRDIRQ) {
isread = 1;
tmpdin = readl(&reg->din);
debug("spi_xfer: din %p..%08x read\n",
din, tmpdin);
if (din) {
*((u8 *)din) = (u8)tmpdin;
din += 1;
}
if (dout)
dout += 1;
bitlen -= 8;
}
if (isread)
break;
}
if (tm >= KWSPI_TIMEOUT)
printf("*** spi_xfer: Time out during SPI transfer\n");
debug("loopend bitlen %d\n", bitlen);
}
if (flags & SPI_XFER_END)
_spi_cs_deactivate(reg);
return 0;
}
#ifndef CONFIG_DM_SPI
static struct kwspi_registers *spireg =
(struct kwspi_registers *)MVEBU_SPI_BASE;
#ifdef CONFIG_KIRKWOOD
static u32 cs_spi_mpp_back[2];
#endif
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct spi_slave *slave;
u32 data;
#ifdef CONFIG_KIRKWOOD
static const u32 kwspi_mpp_config[2][2] = {
{ MPP0_SPI_SCn, 0 }, /* if cs == 0 */
{ MPP7_SPI_SCn, 0 } /* if cs != 0 */
};
#endif
if (!spi_cs_is_valid(bus, cs))
return NULL;
slave = spi_alloc_slave_base(bus, cs);
if (!slave)
return NULL;
writel(KWSPI_SMEMRDY, &spireg->ctrl);
/* calculate spi clock prescaller using max_hz */
data = ((CONFIG_SYS_TCLK / 2) / max_hz) + 0x10;
data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data;
data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data;
/* program spi clock prescaller using max_hz */
writel(KWSPI_ADRLEN_3BYTE | data, &spireg->cfg);
debug("data = 0x%08x\n", data);
writel(KWSPI_SMEMRDIRQ, &spireg->irq_cause);
writel(KWSPI_IRQMASK, &spireg->irq_mask);
#ifdef CONFIG_KIRKWOOD
/* program mpp registers to select SPI_CSn */
kirkwood_mpp_conf(kwspi_mpp_config[cs ? 1 : 0], cs_spi_mpp_back);
#endif
return slave;
}
void spi_free_slave(struct spi_slave *slave)
{
#ifdef CONFIG_KIRKWOOD
kirkwood_mpp_conf(cs_spi_mpp_back, NULL);
#endif
free(slave);
}
#if defined(CONFIG_SYS_KW_SPI_MPP)
u32 spi_mpp_backup[4];
#endif
__attribute__((weak)) int board_spi_claim_bus(struct spi_slave *slave)
{
return 0;
}
int spi_claim_bus(struct spi_slave *slave)
{
#if defined(CONFIG_SYS_KW_SPI_MPP)
u32 config;
u32 spi_mpp_config[4];
config = CONFIG_SYS_KW_SPI_MPP;
if (config & MOSI_MPP6)
spi_mpp_config[0] = MPP6_SPI_MOSI;
else
spi_mpp_config[0] = MPP1_SPI_MOSI;
if (config & SCK_MPP10)
spi_mpp_config[1] = MPP10_SPI_SCK;
else
spi_mpp_config[1] = MPP2_SPI_SCK;
if (config & MISO_MPP11)
spi_mpp_config[2] = MPP11_SPI_MISO;
else
spi_mpp_config[2] = MPP3_SPI_MISO;
spi_mpp_config[3] = 0;
spi_mpp_backup[3] = 0;
/* set new spi mpp and save current mpp config */
kirkwood_mpp_conf(spi_mpp_config, spi_mpp_backup);
#endif
return board_spi_claim_bus(slave);
}
__attribute__((weak)) void board_spi_release_bus(struct spi_slave *slave)
{
}
void spi_release_bus(struct spi_slave *slave)
{
#if defined(CONFIG_SYS_KW_SPI_MPP)
kirkwood_mpp_conf(spi_mpp_backup, NULL);
#endif
board_spi_release_bus(slave);
}
#ifndef CONFIG_SPI_CS_IS_VALID
/*
* you can define this function board specific
* define above CONFIG in board specific config file and
* provide the function in board specific src file
*/
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return bus == 0 && (cs == 0 || cs == 1);
}
#endif
void spi_init(void)
{
}
void spi_cs_activate(struct spi_slave *slave)
{
_spi_cs_activate(spireg);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
_spi_cs_deactivate(spireg);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
return _spi_xfer(spireg, bitlen, dout, din, flags);
}
#else
/* Here now the DM part */
struct mvebu_spi_platdata {
struct kwspi_registers *spireg;
};
struct mvebu_spi_priv {
struct kwspi_registers *spireg;
};
static int mvebu_spi_set_speed(struct udevice *bus, uint hz)
{
struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
struct kwspi_registers *reg = plat->spireg;
u32 data;
/* calculate spi clock prescaller using max_hz */
data = ((CONFIG_SYS_TCLK / 2) / hz) + 0x10;
data = data < KWSPI_CLKPRESCL_MIN ? KWSPI_CLKPRESCL_MIN : data;
data = data > KWSPI_CLKPRESCL_MASK ? KWSPI_CLKPRESCL_MASK : data;
/* program spi clock prescaler using max_hz */
writel(KWSPI_ADRLEN_3BYTE | data, &reg->cfg);
debug("data = 0x%08x\n", data);
return 0;
}
static int mvebu_spi_set_mode(struct udevice *bus, uint mode)
{
return 0;
}
static int mvebu_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
return _spi_xfer(plat->spireg, bitlen, dout, din, flags);
}
static int mvebu_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
/* Configure the chip-select in the CTRL register */
clrsetbits_le32(&plat->spireg->ctrl,
KWSPI_CS_MASK << KWSPI_CS_SHIFT,
spi_chip_select(dev) << KWSPI_CS_SHIFT);
return 0;
}
static int mvebu_spi_probe(struct udevice *bus)
{
struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
struct kwspi_registers *reg = plat->spireg;
writel(KWSPI_SMEMRDY, &reg->ctrl);
writel(KWSPI_SMEMRDIRQ, &reg->irq_cause);
writel(KWSPI_IRQMASK, &reg->irq_mask);
return 0;
}
static int mvebu_spi_ofdata_to_platdata(struct udevice *bus)
{
struct mvebu_spi_platdata *plat = dev_get_platdata(bus);
plat->spireg = (struct kwspi_registers *)dev_get_addr(bus);
return 0;
}
static const struct dm_spi_ops mvebu_spi_ops = {
.claim_bus = mvebu_spi_claim_bus,
.xfer = mvebu_spi_xfer,
.set_speed = mvebu_spi_set_speed,
.set_mode = mvebu_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id mvebu_spi_ids[] = {
{ .compatible = "marvell,armada-375-spi" },
{ .compatible = "marvell,armada-380-spi" },
{ .compatible = "marvell,armada-xp-spi" },
{ }
};
U_BOOT_DRIVER(mvebu_spi) = {
.name = "mvebu_spi",
.id = UCLASS_SPI,
.of_match = mvebu_spi_ids,
.ops = &mvebu_spi_ops,
.ofdata_to_platdata = mvebu_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct mvebu_spi_platdata),
.priv_auto_alloc_size = sizeof(struct mvebu_spi_priv),
.probe = mvebu_spi_probe,
};
#endif

View File

@@ -0,0 +1,144 @@
/*
* LPC32xx SSP interface (SPI mode)
*
* (C) Copyright 2014 DENX Software Engineering GmbH
* Written-by: Albert ARIBAUD <albert.aribaud@3adev.fr>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <linux/compat.h>
#include <asm/io.h>
#include <malloc.h>
#include <spi.h>
#include <asm/arch/clk.h>
/* SSP chip registers */
struct ssp_regs {
u32 cr0;
u32 cr1;
u32 data;
u32 sr;
u32 cpsr;
u32 imsc;
u32 ris;
u32 mis;
u32 icr;
u32 dmacr;
};
/* CR1 register defines */
#define SSP_CR1_SSP_ENABLE 0x0002
/* SR register defines */
#define SSP_SR_TNF 0x0002
/* SSP status RX FIFO not empty bit */
#define SSP_SR_RNE 0x0004
/* lpc32xx spi slave */
struct lpc32xx_spi_slave {
struct spi_slave slave;
struct ssp_regs *regs;
};
static inline struct lpc32xx_spi_slave *to_lpc32xx_spi_slave(
struct spi_slave *slave)
{
return container_of(slave, struct lpc32xx_spi_slave, slave);
}
/* spi_init is called during boot when CONFIG_CMD_SPI is defined */
void spi_init(void)
{
/*
* nothing to do: clocking was enabled in lpc32xx_ssp_enable()
* and configuration will be done in spi_setup_slave()
*/
}
/* the following is called in sequence by do_spi_xfer() */
struct spi_slave *spi_setup_slave(uint bus, uint cs, uint max_hz, uint mode)
{
struct lpc32xx_spi_slave *lslave;
/* we only set up SSP0 for now, so ignore bus */
if (mode & SPI_3WIRE) {
error("3-wire mode not supported");
return NULL;
}
if (mode & SPI_SLAVE) {
error("slave mode not supported\n");
return NULL;
}
if (mode & SPI_PREAMBLE) {
error("preamble byte skipping not supported\n");
return NULL;
}
lslave = spi_alloc_slave(struct lpc32xx_spi_slave, bus, cs);
if (!lslave) {
printf("SPI_error: Fail to allocate lpc32xx_spi_slave\n");
return NULL;
}
lslave->regs = (struct ssp_regs *)SSP0_BASE;
/*
* 8 bit frame, SPI fmt, 500kbps -> clock divider is 26.
* Set SCR to 0 and CPSDVSR to 26.
*/
writel(0x7, &lslave->regs->cr0); /* 8-bit chunks, SPI, 1 clk/bit */
writel(26, &lslave->regs->cpsr); /* SSP clock = HCLK/26 = 500kbps */
writel(0, &lslave->regs->imsc); /* do not raise any interrupts */
writel(0, &lslave->regs->icr); /* clear any pending interrupt */
writel(0, &lslave->regs->dmacr); /* do not do DMAs */
writel(SSP_CR1_SSP_ENABLE, &lslave->regs->cr1); /* enable SSP0 */
return &lslave->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
debug("(lpc32xx) spi_free_slave: 0x%08x\n", (u32)lslave);
free(lslave);
}
int spi_claim_bus(struct spi_slave *slave)
{
/* only one bus and slave so far, always available */
return 0;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct lpc32xx_spi_slave *lslave = to_lpc32xx_spi_slave(slave);
int bytelen = bitlen >> 3;
int idx_out = 0;
int idx_in = 0;
int start_time;
start_time = get_timer(0);
while ((idx_out < bytelen) || (idx_in < bytelen)) {
int status = readl(&lslave->regs->sr);
if ((idx_out < bytelen) && (status & SSP_SR_TNF))
writel(((u8 *)dout)[idx_out++], &lslave->regs->data);
if ((idx_in < bytelen) && (status & status & SSP_SR_RNE))
((u8 *)din)[idx_in++] = readl(&lslave->regs->data);
if (get_timer(start_time) >= CONFIG_LPC32XX_SSP_TIMEOUT)
return -1;
}
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
/* do nothing */
}

View File

@@ -0,0 +1,90 @@
/*
* (C) Copyright 2009
* Frank Bodammer <frank.bodammer@gcd-solutions.de>
* (C) Copyright 2009 Semihalf, Grzegorz Bernacki
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <malloc.h>
#include <spi.h>
#include <mpc5xxx.h>
void spi_init(void)
{
struct mpc5xxx_spi *spi = (struct mpc5xxx_spi *)MPC5XXX_SPI;
/*
* Its important to use the correct order when initializing the
* registers
*/
out_8(&spi->ddr, 0x0F); /* set all SPI pins as output */
out_8(&spi->pdr, 0x00); /* set SS low */
/* SPI is master, SS is general purpose output */
out_8(&spi->cr1, SPI_CR_MSTR | SPI_CR_SPE);
out_8(&spi->cr2, 0x00); /* normal operation */
out_8(&spi->brr, 0x77); /* baud rate: IPB clock / 2048 */
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct spi_slave *slave;
slave = spi_alloc_slave_base(bus, cs);
if (!slave)
return NULL;
return slave;
}
void spi_free_slave(struct spi_slave *slave)
{
free(slave);
}
int spi_claim_bus(struct spi_slave *slave)
{
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
return;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct mpc5xxx_spi *spi = (struct mpc5xxx_spi *)MPC5XXX_SPI;
int i, iter = bitlen >> 3;
const uchar *txp = dout;
uchar *rxp = din;
debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen);
if (flags & SPI_XFER_BEGIN)
setbits_8(&spi->pdr, SPI_PDR_SS);
for (i = 0; i < iter; i++) {
udelay(1000);
debug("spi_xfer: sending %x\n", txp[i]);
out_8(&spi->dr, txp[i]);
while (!(in_8(&spi->sr) & SPI_SR_SPIF)) {
udelay(1000);
if (in_8(&spi->sr) & SPI_SR_WCOL) {
rxp[i] = in_8(&spi->dr);
puts("spi_xfer: write collision\n");
return -1;
}
}
rxp[i] = in_8(&spi->dr);
debug("spi_xfer: received %x\n", rxp[i]);
}
if (flags & SPI_XFER_END)
clrbits_8(&spi->pdr, SPI_PDR_SS);
return 0;
}

View File

@@ -0,0 +1,166 @@
/*
* Copyright (c) 2006 Ben Warren, Qstreams Networks Inc.
* With help from the common/soft_spi and arch/powerpc/cpu/mpc8260 drivers
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/mpc8xxx_spi.h>
#define SPI_EV_NE (0x80000000 >> 22) /* Receiver Not Empty */
#define SPI_EV_NF (0x80000000 >> 23) /* Transmitter Not Full */
#define SPI_MODE_LOOP (0x80000000 >> 1) /* Loopback mode */
#define SPI_MODE_REV (0x80000000 >> 5) /* Reverse mode - MSB first */
#define SPI_MODE_MS (0x80000000 >> 6) /* Always master */
#define SPI_MODE_EN (0x80000000 >> 7) /* Enable interface */
#define SPI_TIMEOUT 1000
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct spi_slave *slave;
if (!spi_cs_is_valid(bus, cs))
return NULL;
slave = spi_alloc_slave_base(bus, cs);
if (!slave)
return NULL;
/*
* TODO: Some of the code in spi_init() should probably move
* here, or into spi_claim_bus() below.
*/
return slave;
}
void spi_free_slave(struct spi_slave *slave)
{
free(slave);
}
void spi_init(void)
{
volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi;
/*
* SPI pins on the MPC83xx are not muxed, so all we do is initialize
* some registers
*/
spi->mode = SPI_MODE_REV | SPI_MODE_MS | SPI_MODE_EN;
spi->mode = (spi->mode & 0xfff0ffff) | BIT(16); /* Use SYSCLK / 8
(16.67MHz typ.) */
spi->event = 0xffffffff; /* Clear all SPI events */
spi->mask = 0x00000000; /* Mask all SPI interrupts */
spi->com = 0; /* LST bit doesn't do anything, so disregard */
}
int spi_claim_bus(struct spi_slave *slave)
{
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
volatile spi8xxx_t *spi = &((immap_t *) (CONFIG_SYS_IMMR))->spi;
unsigned int tmpdout, tmpdin, event;
int numBlks = DIV_ROUND_UP(bitlen, 32);
int tm, isRead = 0;
unsigned char charSize = 32;
debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
slave->bus, slave->cs, *(uint *) dout, *(uint *) din, bitlen);
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
spi->event = 0xffffffff; /* Clear all SPI events */
/* handle data in 32-bit chunks */
while (numBlks--) {
tmpdout = 0;
charSize = (bitlen >= 32 ? 32 : bitlen);
/* Shift data so it's msb-justified */
tmpdout = *(u32 *) dout >> (32 - charSize);
/* The LEN field of the SPMODE register is set as follows:
*
* Bit length setting
* len <= 4 3
* 4 < len <= 16 len - 1
* len > 16 0
*/
spi->mode &= ~SPI_MODE_EN;
if (bitlen <= 16) {
if (bitlen <= 4)
spi->mode = (spi->mode & 0xff0fffff) |
(3 << 20);
else
spi->mode = (spi->mode & 0xff0fffff) |
((bitlen - 1) << 20);
} else {
spi->mode = (spi->mode & 0xff0fffff);
/* Set up the next iteration if sending > 32 bits */
bitlen -= 32;
dout += 4;
}
spi->mode |= SPI_MODE_EN;
spi->tx = tmpdout; /* Write the data out */
debug("*** spi_xfer: ... %08x written\n", tmpdout);
/*
* Wait for SPI transmit to get out
* or time out (1 second = 1000 ms)
* The NE event must be read and cleared first
*/
for (tm = 0, isRead = 0; tm < SPI_TIMEOUT; ++tm) {
event = spi->event;
if (event & SPI_EV_NE) {
tmpdin = spi->rx;
spi->event |= SPI_EV_NE;
isRead = 1;
*(u32 *) din = (tmpdin << (32 - charSize));
if (charSize == 32) {
/* Advance output buffer by 32 bits */
din += 4;
}
}
/*
* Only bail when we've had both NE and NF events.
* This will cause timeouts on RO devices, so maybe
* in the future put an arbitrary delay after writing
* the device. Arbitrary delays suck, though...
*/
if (isRead && (event & SPI_EV_NF))
break;
}
if (tm >= SPI_TIMEOUT)
puts("*** spi_xfer: Time out during SPI transfer");
debug("*** spi_xfer: transfer ended. Value=%08x\n", tmpdin);
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return 0;
}

View File

@@ -0,0 +1,466 @@
/*
* Copyright (C) 2008, Guennadi Liakhovetski <lg@denx.de>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <malloc.h>
#include <spi.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/clock.h>
#include <asm/imx-common/spi.h>
#ifdef CONFIG_MX27
/* i.MX27 has a completely wrong register layout and register definitions in the
* datasheet, the correct one is in the Freescale's Linux driver */
#error "i.MX27 CSPI not supported due to drastic differences in register definitions" \
"See linux mxc_spi driver from Freescale for details."
#endif
static unsigned long spi_bases[] = {
MXC_SPI_BASE_ADDRESSES
};
__weak int board_spi_cs_gpio(unsigned bus, unsigned cs)
{
return -1;
}
#define OUT MXC_GPIO_DIRECTION_OUT
#define reg_read readl
#define reg_write(a, v) writel(v, a)
#if !defined(CONFIG_SYS_SPI_MXC_WAIT)
#define CONFIG_SYS_SPI_MXC_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */
#endif
struct mxc_spi_slave {
struct spi_slave slave;
unsigned long base;
u32 ctrl_reg;
#if defined(MXC_ECSPI)
u32 cfg_reg;
#endif
int gpio;
int ss_pol;
unsigned int max_hz;
unsigned int mode;
};
static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave)
{
return container_of(slave, struct mxc_spi_slave, slave);
}
void spi_cs_activate(struct spi_slave *slave)
{
struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
if (mxcs->gpio > 0)
gpio_set_value(mxcs->gpio, mxcs->ss_pol);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
if (mxcs->gpio > 0)
gpio_set_value(mxcs->gpio,
!(mxcs->ss_pol));
}
u32 get_cspi_div(u32 div)
{
int i;
for (i = 0; i < 8; i++) {
if (div <= (4 << i))
return i;
}
return i;
}
#ifdef MXC_CSPI
static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs)
{
unsigned int ctrl_reg;
u32 clk_src;
u32 div;
unsigned int max_hz = mxcs->max_hz;
unsigned int mode = mxcs->mode;
clk_src = mxc_get_clock(MXC_CSPI_CLK);
div = DIV_ROUND_UP(clk_src, max_hz);
div = get_cspi_div(div);
debug("clk %d Hz, div %d, real clk %d Hz\n",
max_hz, div, clk_src / (4 << div));
ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) |
MXC_CSPICTRL_BITCOUNT(MXC_CSPICTRL_MAXBITS) |
MXC_CSPICTRL_DATARATE(div) |
MXC_CSPICTRL_EN |
#ifdef CONFIG_MX35
MXC_CSPICTRL_SSCTL |
#endif
MXC_CSPICTRL_MODE;
if (mode & SPI_CPHA)
ctrl_reg |= MXC_CSPICTRL_PHA;
if (mode & SPI_CPOL)
ctrl_reg |= MXC_CSPICTRL_POL;
if (mode & SPI_CS_HIGH)
ctrl_reg |= MXC_CSPICTRL_SSPOL;
mxcs->ctrl_reg = ctrl_reg;
return 0;
}
#endif
#ifdef MXC_ECSPI
static s32 spi_cfg_mxc(struct mxc_spi_slave *mxcs, unsigned int cs)
{
u32 clk_src = mxc_get_clock(MXC_CSPI_CLK);
s32 reg_ctrl, reg_config;
u32 ss_pol = 0, sclkpol = 0, sclkpha = 0, sclkctl = 0;
u32 pre_div = 0, post_div = 0;
struct cspi_regs *regs = (struct cspi_regs *)mxcs->base;
unsigned int max_hz = mxcs->max_hz;
unsigned int mode = mxcs->mode;
/*
* Reset SPI and set all CSs to master mode, if toggling
* between slave and master mode we might see a glitch
* on the clock line
*/
reg_ctrl = MXC_CSPICTRL_MODE_MASK;
reg_write(&regs->ctrl, reg_ctrl);
reg_ctrl |= MXC_CSPICTRL_EN;
reg_write(&regs->ctrl, reg_ctrl);
if (clk_src > max_hz) {
pre_div = (clk_src - 1) / max_hz;
/* fls(1) = 1, fls(0x80000000) = 32, fls(16) = 5 */
post_div = fls(pre_div);
if (post_div > 4) {
post_div -= 4;
if (post_div >= 16) {
printf("Error: no divider for the freq: %d\n",
max_hz);
return -1;
}
pre_div >>= post_div;
} else {
post_div = 0;
}
}
debug("pre_div = %d, post_div=%d\n", pre_div, post_div);
reg_ctrl = (reg_ctrl & ~MXC_CSPICTRL_SELCHAN(3)) |
MXC_CSPICTRL_SELCHAN(cs);
reg_ctrl = (reg_ctrl & ~MXC_CSPICTRL_PREDIV(0x0F)) |
MXC_CSPICTRL_PREDIV(pre_div);
reg_ctrl = (reg_ctrl & ~MXC_CSPICTRL_POSTDIV(0x0F)) |
MXC_CSPICTRL_POSTDIV(post_div);
if (mode & SPI_CS_HIGH)
ss_pol = 1;
if (mode & SPI_CPOL) {
sclkpol = 1;
sclkctl = 1;
}
if (mode & SPI_CPHA)
sclkpha = 1;
reg_config = reg_read(&regs->cfg);
/*
* Configuration register setup
* The MX51 supports different setup for each SS
*/
reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_SSPOL))) |
(ss_pol << (cs + MXC_CSPICON_SSPOL));
reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_POL))) |
(sclkpol << (cs + MXC_CSPICON_POL));
reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_CTL))) |
(sclkctl << (cs + MXC_CSPICON_CTL));
reg_config = (reg_config & ~(1 << (cs + MXC_CSPICON_PHA))) |
(sclkpha << (cs + MXC_CSPICON_PHA));
debug("reg_ctrl = 0x%x\n", reg_ctrl);
reg_write(&regs->ctrl, reg_ctrl);
debug("reg_config = 0x%x\n", reg_config);
reg_write(&regs->cfg, reg_config);
/* save config register and control register */
mxcs->ctrl_reg = reg_ctrl;
mxcs->cfg_reg = reg_config;
/* clear interrupt reg */
reg_write(&regs->intr, 0);
reg_write(&regs->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF);
return 0;
}
#endif
int spi_xchg_single(struct spi_slave *slave, unsigned int bitlen,
const u8 *dout, u8 *din, unsigned long flags)
{
struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
int nbytes = DIV_ROUND_UP(bitlen, 8);
u32 data, cnt, i;
struct cspi_regs *regs = (struct cspi_regs *)mxcs->base;
u32 ts;
int status;
debug("%s: bitlen %d dout 0x%x din 0x%x\n",
__func__, bitlen, (u32)dout, (u32)din);
mxcs->ctrl_reg = (mxcs->ctrl_reg &
~MXC_CSPICTRL_BITCOUNT(MXC_CSPICTRL_MAXBITS)) |
MXC_CSPICTRL_BITCOUNT(bitlen - 1);
reg_write(&regs->ctrl, mxcs->ctrl_reg | MXC_CSPICTRL_EN);
#ifdef MXC_ECSPI
reg_write(&regs->cfg, mxcs->cfg_reg);
#endif
/* Clear interrupt register */
reg_write(&regs->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF);
/*
* The SPI controller works only with words,
* check if less than a word is sent.
* Access to the FIFO is only 32 bit
*/
if (bitlen % 32) {
data = 0;
cnt = (bitlen % 32) / 8;
if (dout) {
for (i = 0; i < cnt; i++) {
data = (data << 8) | (*dout++ & 0xFF);
}
}
debug("Sending SPI 0x%x\n", data);
reg_write(&regs->txdata, data);
nbytes -= cnt;
}
data = 0;
while (nbytes > 0) {
data = 0;
if (dout) {
/* Buffer is not 32-bit aligned */
if ((unsigned long)dout & 0x03) {
data = 0;
for (i = 0; i < 4; i++)
data = (data << 8) | (*dout++ & 0xFF);
} else {
data = *(u32 *)dout;
data = cpu_to_be32(data);
dout += 4;
}
}
debug("Sending SPI 0x%x\n", data);
reg_write(&regs->txdata, data);
nbytes -= 4;
}
/* FIFO is written, now starts the transfer setting the XCH bit */
reg_write(&regs->ctrl, mxcs->ctrl_reg |
MXC_CSPICTRL_EN | MXC_CSPICTRL_XCH);
ts = get_timer(0);
status = reg_read(&regs->stat);
/* Wait until the TC (Transfer completed) bit is set */
while ((status & MXC_CSPICTRL_TC) == 0) {
if (get_timer(ts) > CONFIG_SYS_SPI_MXC_WAIT) {
printf("spi_xchg_single: Timeout!\n");
return -1;
}
status = reg_read(&regs->stat);
}
/* Transfer completed, clear any pending request */
reg_write(&regs->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF);
nbytes = DIV_ROUND_UP(bitlen, 8);
cnt = nbytes % 32;
if (bitlen % 32) {
data = reg_read(&regs->rxdata);
cnt = (bitlen % 32) / 8;
data = cpu_to_be32(data) >> ((sizeof(data) - cnt) * 8);
debug("SPI Rx unaligned: 0x%x\n", data);
if (din) {
memcpy(din, &data, cnt);
din += cnt;
}
nbytes -= cnt;
}
while (nbytes > 0) {
u32 tmp;
tmp = reg_read(&regs->rxdata);
data = cpu_to_be32(tmp);
debug("SPI Rx: 0x%x 0x%x\n", tmp, data);
cnt = min_t(u32, nbytes, sizeof(data));
if (din) {
memcpy(din, &data, cnt);
din += cnt;
}
nbytes -= cnt;
}
return 0;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
int n_bytes = DIV_ROUND_UP(bitlen, 8);
int n_bits;
int ret;
u32 blk_size;
u8 *p_outbuf = (u8 *)dout;
u8 *p_inbuf = (u8 *)din;
if (!slave)
return -1;
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
while (n_bytes > 0) {
if (n_bytes < MAX_SPI_BYTES)
blk_size = n_bytes;
else
blk_size = MAX_SPI_BYTES;
n_bits = blk_size * 8;
ret = spi_xchg_single(slave, n_bits, p_outbuf, p_inbuf, 0);
if (ret)
return ret;
if (dout)
p_outbuf += blk_size;
if (din)
p_inbuf += blk_size;
n_bytes -= blk_size;
}
if (flags & SPI_XFER_END) {
spi_cs_deactivate(slave);
}
return 0;
}
void spi_init(void)
{
}
/*
* Some SPI devices require active chip-select over multiple
* transactions, we achieve this using a GPIO. Still, the SPI
* controller has to be configured to use one of its own chipselects.
* To use this feature you have to implement board_spi_cs_gpio() to assign
* a gpio value for each cs (-1 if cs doesn't need to use gpio).
* You must use some unused on this SPI controller cs between 0 and 3.
*/
static int setup_cs_gpio(struct mxc_spi_slave *mxcs,
unsigned int bus, unsigned int cs)
{
int ret;
mxcs->gpio = board_spi_cs_gpio(bus, cs);
if (mxcs->gpio == -1)
return 0;
ret = gpio_direction_output(mxcs->gpio, !(mxcs->ss_pol));
if (ret) {
printf("mxc_spi: cannot setup gpio %d\n", mxcs->gpio);
return -EINVAL;
}
return 0;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct mxc_spi_slave *mxcs;
int ret;
if (bus >= ARRAY_SIZE(spi_bases))
return NULL;
if (max_hz == 0) {
printf("Error: desired clock is 0\n");
return NULL;
}
mxcs = spi_alloc_slave(struct mxc_spi_slave, bus, cs);
if (!mxcs) {
puts("mxc_spi: SPI Slave not allocated !\n");
return NULL;
}
mxcs->ss_pol = (mode & SPI_CS_HIGH) ? 1 : 0;
ret = setup_cs_gpio(mxcs, bus, cs);
if (ret < 0) {
free(mxcs);
return NULL;
}
mxcs->base = spi_bases[bus];
mxcs->max_hz = max_hz;
mxcs->mode = mode;
return &mxcs->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
free(mxcs);
}
int spi_claim_bus(struct spi_slave *slave)
{
int ret;
struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);
struct cspi_regs *regs = (struct cspi_regs *)mxcs->base;
reg_write(&regs->rxdata, 1);
udelay(1);
ret = spi_cfg_mxc(mxcs, slave->cs);
if (ret) {
printf("mxc_spi: cannot setup SPI controller\n");
return ret;
}
reg_write(&regs->period, MXC_CSPIPERIOD_32KHZ);
reg_write(&regs->intr, 0);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
/* TODO: Shut the controller down */
}

View File

@@ -0,0 +1,363 @@
/*
* Freescale i.MX28 SPI driver
*
* Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
* on behalf of DENX Software Engineering GmbH
*
* SPDX-License-Identifier: GPL-2.0+
*
* NOTE: This driver only supports the SPI-controller chipselects,
* GPIO driven chipselects are not supported.
*/
#include <common.h>
#include <malloc.h>
#include <memalign.h>
#include <spi.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/imx-regs.h>
#include <asm/arch/sys_proto.h>
#include <asm/imx-common/dma.h>
#define MXS_SPI_MAX_TIMEOUT 1000000
#define MXS_SPI_PORT_OFFSET 0x2000
#define MXS_SSP_CHIPSELECT_MASK 0x00300000
#define MXS_SSP_CHIPSELECT_SHIFT 20
#define MXSSSP_SMALL_TRANSFER 512
struct mxs_spi_slave {
struct spi_slave slave;
uint32_t max_khz;
uint32_t mode;
struct mxs_ssp_regs *regs;
};
static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
{
return container_of(slave, struct mxs_spi_slave, slave);
}
void spi_init(void)
{
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
/* MXS SPI: 4 ports and 3 chip selects maximum */
if (!mxs_ssp_bus_id_valid(bus) || cs > 2)
return 0;
else
return 1;
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct mxs_spi_slave *mxs_slave;
if (!spi_cs_is_valid(bus, cs)) {
printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);
return NULL;
}
mxs_slave = spi_alloc_slave(struct mxs_spi_slave, bus, cs);
if (!mxs_slave)
return NULL;
if (mxs_dma_init_channel(MXS_DMA_CHANNEL_AHB_APBH_SSP0 + bus))
goto err_init;
mxs_slave->max_khz = max_hz / 1000;
mxs_slave->mode = mode;
mxs_slave->regs = mxs_ssp_regs_by_bus(bus);
return &mxs_slave->slave;
err_init:
free(mxs_slave);
return NULL;
}
void spi_free_slave(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
free(mxs_slave);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
uint32_t reg = 0;
mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
writel((slave->cs << MXS_SSP_CHIPSELECT_SHIFT) |
SSP_CTRL0_BUS_WIDTH_ONE_BIT,
&ssp_regs->hw_ssp_ctrl0);
reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
writel(reg, &ssp_regs->hw_ssp_ctrl1);
writel(0, &ssp_regs->hw_ssp_cmd0);
mxs_set_ssp_busclock(slave->bus, mxs_slave->max_khz);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)
{
writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);
writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr);
}
static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)
{
writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr);
writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);
}
static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
char *data, int length, int write, unsigned long flags)
{
struct mxs_ssp_regs *ssp_regs = slave->regs;
if (flags & SPI_XFER_BEGIN)
mxs_spi_start_xfer(ssp_regs);
while (length--) {
/* We transfer 1 byte */
#if defined(CONFIG_MX23)
writel(SSP_CTRL0_XFER_COUNT_MASK, &ssp_regs->hw_ssp_ctrl0_clr);
writel(1, &ssp_regs->hw_ssp_ctrl0_set);
#elif defined(CONFIG_MX28)
writel(1, &ssp_regs->hw_ssp_xfer_size);
#endif
if ((flags & SPI_XFER_END) && !length)
mxs_spi_end_xfer(ssp_regs);
if (write)
writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);
else
writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set);
writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set);
if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for start\n");
return -ETIMEDOUT;
}
if (write)
writel(*data++, &ssp_regs->hw_ssp_data);
writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set);
if (!write) {
if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for data\n");
return -ETIMEDOUT;
}
*data = readl(&ssp_regs->hw_ssp_data);
data++;
}
if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
printf("MXS SPI: Timeout waiting for finish\n");
return -ETIMEDOUT;
}
}
return 0;
}
static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
char *data, int length, int write, unsigned long flags)
{
const int xfer_max_sz = 0xff00;
const int desc_count = DIV_ROUND_UP(length, xfer_max_sz) + 1;
struct mxs_ssp_regs *ssp_regs = slave->regs;
struct mxs_dma_desc *dp;
uint32_t ctrl0;
uint32_t cache_data_count;
const uint32_t dstart = (uint32_t)data;
int dmach;
int tl;
int ret = 0;
#if defined(CONFIG_MX23)
const int mxs_spi_pio_words = 1;
#elif defined(CONFIG_MX28)
const int mxs_spi_pio_words = 4;
#endif
ALLOC_CACHE_ALIGN_BUFFER(struct mxs_dma_desc, desc, desc_count);
memset(desc, 0, sizeof(struct mxs_dma_desc) * desc_count);
ctrl0 = readl(&ssp_regs->hw_ssp_ctrl0);
ctrl0 |= SSP_CTRL0_DATA_XFER;
if (flags & SPI_XFER_BEGIN)
ctrl0 |= SSP_CTRL0_LOCK_CS;
if (!write)
ctrl0 |= SSP_CTRL0_READ;
if (length % ARCH_DMA_MINALIGN)
cache_data_count = roundup(length, ARCH_DMA_MINALIGN);
else
cache_data_count = length;
/* Flush data to DRAM so DMA can pick them up */
if (write)
flush_dcache_range(dstart, dstart + cache_data_count);
/* Invalidate the area, so no writeback into the RAM races with DMA */
invalidate_dcache_range(dstart, dstart + cache_data_count);
dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus;
dp = desc;
while (length) {
dp->address = (dma_addr_t)dp;
dp->cmd.address = (dma_addr_t)data;
/*
* This is correct, even though it does indeed look insane.
* I hereby have to, wholeheartedly, thank Freescale Inc.,
* for always inventing insane hardware and keeping me busy
* and employed ;-)
*/
if (write)
dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
else
dp->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
/*
* The DMA controller can transfer large chunks (64kB) at
* time by setting the transfer length to 0. Setting tl to
* 0x10000 will overflow below and make .data contain 0.
* Otherwise, 0xff00 is the transfer maximum.
*/
if (length >= 0x10000)
tl = 0x10000;
else
tl = min(length, xfer_max_sz);
dp->cmd.data |=
((tl & 0xffff) << MXS_DMA_DESC_BYTES_OFFSET) |
(mxs_spi_pio_words << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
MXS_DMA_DESC_HALT_ON_TERMINATE |
MXS_DMA_DESC_TERMINATE_FLUSH;
data += tl;
length -= tl;
if (!length) {
dp->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM;
if (flags & SPI_XFER_END) {
ctrl0 &= ~SSP_CTRL0_LOCK_CS;
ctrl0 |= SSP_CTRL0_IGNORE_CRC;
}
}
/*
* Write CTRL0, CMD0, CMD1 and XFER_SIZE registers in
* case of MX28, write only CTRL0 in case of MX23 due
* to the difference in register layout. It is utterly
* essential that the XFER_SIZE register is written on
* a per-descriptor basis with the same size as is the
* descriptor!
*/
dp->cmd.pio_words[0] = ctrl0;
#ifdef CONFIG_MX28
dp->cmd.pio_words[1] = 0;
dp->cmd.pio_words[2] = 0;
dp->cmd.pio_words[3] = tl;
#endif
mxs_dma_desc_append(dmach, dp);
dp++;
}
if (mxs_dma_go(dmach))
ret = -EINVAL;
/* The data arrived into DRAM, invalidate cache over them */
if (!write)
invalidate_dcache_range(dstart, dstart + cache_data_count);
return ret;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
int len = bitlen / 8;
char dummy;
int write = 0;
char *data = NULL;
int dma = 1;
if (bitlen == 0) {
if (flags & SPI_XFER_END) {
din = (void *)&dummy;
len = 1;
} else
return 0;
}
/* Half-duplex only */
if (din && dout)
return -EINVAL;
/* No data */
if (!din && !dout)
return 0;
if (dout) {
data = (char *)dout;
write = 1;
} else if (din) {
data = (char *)din;
write = 0;
}
/*
* Check for alignment, if the buffer is aligned, do DMA transfer,
* PIO otherwise. This is a temporary workaround until proper bounce
* buffer is in place.
*/
if (dma) {
if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1))
dma = 0;
if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1))
dma = 0;
}
if (!dma || (len < MXSSSP_SMALL_TRANSFER)) {
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags);
} else {
writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags);
}
}

View File

@@ -0,0 +1,697 @@
/*
* Copyright (C) 2016 Jagan Teki <jteki@openedev.com>
* Christophe Ricard <christophe.ricard@gmail.com>
*
* Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com>
*
* Driver for McSPI controller on OMAP3. Based on davinci_spi.c
* Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
*
* Copyright (C) 2007 Atmel Corporation
*
* Parts taken from linux/drivers/spi/omap2_mcspi.c
* Copyright (C) 2005, 2006 Nokia Corporation
*
* Modified by Ruslan Araslanov <ruslan.araslanov@vitecmm.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <spi.h>
#include <malloc.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
#if defined(CONFIG_AM33XX) || defined(CONFIG_AM43XX)
#define OMAP3_MCSPI1_BASE 0x48030100
#define OMAP3_MCSPI2_BASE 0x481A0100
#else
#define OMAP3_MCSPI1_BASE 0x48098000
#define OMAP3_MCSPI2_BASE 0x4809A000
#define OMAP3_MCSPI3_BASE 0x480B8000
#define OMAP3_MCSPI4_BASE 0x480BA000
#endif
#define OMAP4_MCSPI_REG_OFFSET 0x100
struct omap2_mcspi_platform_config {
unsigned int regs_offset;
};
/* per-register bitmasks */
#define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3)
#define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP BIT(2)
#define OMAP3_MCSPI_SYSCONFIG_AUTOIDLE BIT(0)
#define OMAP3_MCSPI_SYSCONFIG_SOFTRESET BIT(1)
#define OMAP3_MCSPI_SYSSTATUS_RESETDONE BIT(0)
#define OMAP3_MCSPI_MODULCTRL_SINGLE BIT(0)
#define OMAP3_MCSPI_MODULCTRL_MS BIT(2)
#define OMAP3_MCSPI_MODULCTRL_STEST BIT(3)
#define OMAP3_MCSPI_CHCONF_PHA BIT(0)
#define OMAP3_MCSPI_CHCONF_POL BIT(1)
#define OMAP3_MCSPI_CHCONF_CLKD_MASK GENMASK(5, 2)
#define OMAP3_MCSPI_CHCONF_EPOL BIT(6)
#define OMAP3_MCSPI_CHCONF_WL_MASK GENMASK(11, 7)
#define OMAP3_MCSPI_CHCONF_TRM_RX_ONLY BIT(12)
#define OMAP3_MCSPI_CHCONF_TRM_TX_ONLY BIT(13)
#define OMAP3_MCSPI_CHCONF_TRM_MASK GENMASK(13, 12)
#define OMAP3_MCSPI_CHCONF_DMAW BIT(14)
#define OMAP3_MCSPI_CHCONF_DMAR BIT(15)
#define OMAP3_MCSPI_CHCONF_DPE0 BIT(16)
#define OMAP3_MCSPI_CHCONF_DPE1 BIT(17)
#define OMAP3_MCSPI_CHCONF_IS BIT(18)
#define OMAP3_MCSPI_CHCONF_TURBO BIT(19)
#define OMAP3_MCSPI_CHCONF_FORCE BIT(20)
#define OMAP3_MCSPI_CHSTAT_RXS BIT(0)
#define OMAP3_MCSPI_CHSTAT_TXS BIT(1)
#define OMAP3_MCSPI_CHSTAT_EOT BIT(2)
#define OMAP3_MCSPI_CHCTRL_EN BIT(0)
#define OMAP3_MCSPI_CHCTRL_DIS (0 << 0)
#define OMAP3_MCSPI_WAKEUPENABLE_WKEN BIT(0)
#define MCSPI_PINDIR_D0_IN_D1_OUT 0
#define MCSPI_PINDIR_D0_OUT_D1_IN 1
#define OMAP3_MCSPI_MAX_FREQ 48000000
#define SPI_WAIT_TIMEOUT 10
/* OMAP3 McSPI registers */
struct mcspi_channel {
unsigned int chconf; /* 0x2C, 0x40, 0x54, 0x68 */
unsigned int chstat; /* 0x30, 0x44, 0x58, 0x6C */
unsigned int chctrl; /* 0x34, 0x48, 0x5C, 0x70 */
unsigned int tx; /* 0x38, 0x4C, 0x60, 0x74 */
unsigned int rx; /* 0x3C, 0x50, 0x64, 0x78 */
};
struct mcspi {
unsigned char res1[0x10];
unsigned int sysconfig; /* 0x10 */
unsigned int sysstatus; /* 0x14 */
unsigned int irqstatus; /* 0x18 */
unsigned int irqenable; /* 0x1C */
unsigned int wakeupenable; /* 0x20 */
unsigned int syst; /* 0x24 */
unsigned int modulctrl; /* 0x28 */
struct mcspi_channel channel[4];
/* channel0: 0x2C - 0x3C, bus 0 & 1 & 2 & 3 */
/* channel1: 0x40 - 0x50, bus 0 & 1 */
/* channel2: 0x54 - 0x64, bus 0 & 1 */
/* channel3: 0x68 - 0x78, bus 0 */
};
struct omap3_spi_priv {
#ifndef CONFIG_DM_SPI
struct spi_slave slave;
#endif
struct mcspi *regs;
unsigned int cs;
unsigned int freq;
unsigned int mode;
unsigned int wordlen;
unsigned int pin_dir:1;
};
static void omap3_spi_write_chconf(struct omap3_spi_priv *priv, int val)
{
writel(val, &priv->regs->channel[priv->cs].chconf);
/* Flash post writes to make immediate effect */
readl(&priv->regs->channel[priv->cs].chconf);
}
static void omap3_spi_set_enable(struct omap3_spi_priv *priv, int enable)
{
writel(enable, &priv->regs->channel[priv->cs].chctrl);
/* Flash post writes to make immediate effect */
readl(&priv->regs->channel[priv->cs].chctrl);
}
static int omap3_spi_write(struct omap3_spi_priv *priv, unsigned int len,
const void *txp, unsigned long flags)
{
ulong start;
int i, chconf;
chconf = readl(&priv->regs->channel[priv->cs].chconf);
/* Enable the channel */
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN);
chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
chconf |= (priv->wordlen - 1) << 7;
chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY;
chconf |= OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
for (i = 0; i < len; i++) {
/* wait till TX register is empty (TXS == 1) */
start = get_timer(0);
while (!(readl(&priv->regs->channel[priv->cs].chstat) &
OMAP3_MCSPI_CHSTAT_TXS)) {
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
printf("SPI TXS timed out, status=0x%08x\n",
readl(&priv->regs->channel[priv->cs].chstat));
return -1;
}
}
/* Write the data */
unsigned int *tx = &priv->regs->channel[priv->cs].tx;
if (priv->wordlen > 16)
writel(((u32 *)txp)[i], tx);
else if (priv->wordlen > 8)
writel(((u16 *)txp)[i], tx);
else
writel(((u8 *)txp)[i], tx);
}
/* wait to finish of transfer */
while ((readl(&priv->regs->channel[priv->cs].chstat) &
(OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS)) !=
(OMAP3_MCSPI_CHSTAT_EOT | OMAP3_MCSPI_CHSTAT_TXS))
;
/* Disable the channel otherwise the next immediate RX will get affected */
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS);
if (flags & SPI_XFER_END) {
chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
}
return 0;
}
static int omap3_spi_read(struct omap3_spi_priv *priv, unsigned int len,
void *rxp, unsigned long flags)
{
int i, chconf;
ulong start;
chconf = readl(&priv->regs->channel[priv->cs].chconf);
/* Enable the channel */
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN);
chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
chconf |= (priv->wordlen - 1) << 7;
chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY;
chconf |= OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
writel(0, &priv->regs->channel[priv->cs].tx);
for (i = 0; i < len; i++) {
start = get_timer(0);
/* Wait till RX register contains data (RXS == 1) */
while (!(readl(&priv->regs->channel[priv->cs].chstat) &
OMAP3_MCSPI_CHSTAT_RXS)) {
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
printf("SPI RXS timed out, status=0x%08x\n",
readl(&priv->regs->channel[priv->cs].chstat));
return -1;
}
}
/* Disable the channel to prevent furher receiving */
if (i == (len - 1))
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS);
/* Read the data */
unsigned int *rx = &priv->regs->channel[priv->cs].rx;
if (priv->wordlen > 16)
((u32 *)rxp)[i] = readl(rx);
else if (priv->wordlen > 8)
((u16 *)rxp)[i] = (u16)readl(rx);
else
((u8 *)rxp)[i] = (u8)readl(rx);
}
if (flags & SPI_XFER_END) {
chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
}
return 0;
}
/*McSPI Transmit Receive Mode*/
static int omap3_spi_txrx(struct omap3_spi_priv *priv, unsigned int len,
const void *txp, void *rxp, unsigned long flags)
{
ulong start;
int chconf, i = 0;
chconf = readl(&priv->regs->channel[priv->cs].chconf);
/*Enable SPI channel*/
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN);
/*set TRANSMIT-RECEIVE Mode*/
chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
chconf |= (priv->wordlen - 1) << 7;
chconf |= OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
/*Shift in and out 1 byte at time*/
for (i=0; i < len; i++){
/* Write: wait for TX empty (TXS == 1)*/
start = get_timer(0);
while (!(readl(&priv->regs->channel[priv->cs].chstat) &
OMAP3_MCSPI_CHSTAT_TXS)) {
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
printf("SPI TXS timed out, status=0x%08x\n",
readl(&priv->regs->channel[priv->cs].chstat));
return -1;
}
}
/* Write the data */
unsigned int *tx = &priv->regs->channel[priv->cs].tx;
if (priv->wordlen > 16)
writel(((u32 *)txp)[i], tx);
else if (priv->wordlen > 8)
writel(((u16 *)txp)[i], tx);
else
writel(((u8 *)txp)[i], tx);
/*Read: wait for RX containing data (RXS == 1)*/
start = get_timer(0);
while (!(readl(&priv->regs->channel[priv->cs].chstat) &
OMAP3_MCSPI_CHSTAT_RXS)) {
if (get_timer(start) > SPI_WAIT_TIMEOUT) {
printf("SPI RXS timed out, status=0x%08x\n",
readl(&priv->regs->channel[priv->cs].chstat));
return -1;
}
}
/* Read the data */
unsigned int *rx = &priv->regs->channel[priv->cs].rx;
if (priv->wordlen > 16)
((u32 *)rxp)[i] = readl(rx);
else if (priv->wordlen > 8)
((u16 *)rxp)[i] = (u16)readl(rx);
else
((u8 *)rxp)[i] = (u8)readl(rx);
}
/* Disable the channel */
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS);
/*if transfer must be terminated disable the channel*/
if (flags & SPI_XFER_END) {
chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
}
return 0;
}
static int _spi_xfer(struct omap3_spi_priv *priv, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
unsigned int len;
int ret = -1;
if (priv->wordlen < 4 || priv->wordlen > 32) {
printf("omap3_spi: invalid wordlen %d\n", priv->wordlen);
return -1;
}
if (bitlen % priv->wordlen)
return -1;
len = bitlen / priv->wordlen;
if (bitlen == 0) { /* only change CS */
int chconf = readl(&priv->regs->channel[priv->cs].chconf);
if (flags & SPI_XFER_BEGIN) {
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_EN);
chconf |= OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
}
if (flags & SPI_XFER_END) {
chconf &= ~OMAP3_MCSPI_CHCONF_FORCE;
omap3_spi_write_chconf(priv, chconf);
omap3_spi_set_enable(priv, OMAP3_MCSPI_CHCTRL_DIS);
}
ret = 0;
} else {
if (dout != NULL && din != NULL)
ret = omap3_spi_txrx(priv, len, dout, din, flags);
else if (dout != NULL)
ret = omap3_spi_write(priv, len, dout, flags);
else if (din != NULL)
ret = omap3_spi_read(priv, len, din, flags);
}
return ret;
}
static void _omap3_spi_set_speed(struct omap3_spi_priv *priv)
{
uint32_t confr, div = 0;
confr = readl(&priv->regs->channel[priv->cs].chconf);
/* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */
if (priv->freq) {
while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div))
> priv->freq)
div++;
} else {
div = 0xC;
}
/* set clock divisor */
confr &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK;
confr |= div << 2;
omap3_spi_write_chconf(priv, confr);
}
static void _omap3_spi_set_mode(struct omap3_spi_priv *priv)
{
uint32_t confr;
confr = readl(&priv->regs->channel[priv->cs].chconf);
/* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS
* REVISIT: this controller could support SPI_3WIRE mode.
*/
if (priv->pin_dir == MCSPI_PINDIR_D0_IN_D1_OUT) {
confr &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
confr |= OMAP3_MCSPI_CHCONF_DPE0;
} else {
confr &= ~OMAP3_MCSPI_CHCONF_DPE0;
confr |= OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1;
}
/* set SPI mode 0..3 */
confr &= ~(OMAP3_MCSPI_CHCONF_POL | OMAP3_MCSPI_CHCONF_PHA);
if (priv->mode & SPI_CPHA)
confr |= OMAP3_MCSPI_CHCONF_PHA;
if (priv->mode & SPI_CPOL)
confr |= OMAP3_MCSPI_CHCONF_POL;
/* set chipselect polarity; manage with FORCE */
if (!(priv->mode & SPI_CS_HIGH))
confr |= OMAP3_MCSPI_CHCONF_EPOL; /* active-low; normal */
else
confr &= ~OMAP3_MCSPI_CHCONF_EPOL;
/* Transmit & receive mode */
confr &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
omap3_spi_write_chconf(priv, confr);
}
static void _omap3_spi_set_wordlen(struct omap3_spi_priv *priv)
{
unsigned int confr;
/* McSPI individual channel configuration */
confr = readl(&priv->regs->channel[priv->wordlen].chconf);
/* wordlength */
confr &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
confr |= (priv->wordlen - 1) << 7;
omap3_spi_write_chconf(priv, confr);
}
static void spi_reset(struct mcspi *regs)
{
unsigned int tmp;
writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &regs->sysconfig);
do {
tmp = readl(&regs->sysstatus);
} while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE));
writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE |
OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP |
OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, &regs->sysconfig);
writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &regs->wakeupenable);
}
static void _omap3_spi_claim_bus(struct omap3_spi_priv *priv)
{
unsigned int conf;
spi_reset(priv->regs);
/*
* setup when switching from (reset default) slave mode
* to single-channel master mode
*/
conf = readl(&priv->regs->modulctrl);
conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS);
conf |= OMAP3_MCSPI_MODULCTRL_SINGLE;
writel(conf, &priv->regs->modulctrl);
_omap3_spi_set_mode(priv);
_omap3_spi_set_speed(priv);
}
#ifndef CONFIG_DM_SPI
static inline struct omap3_spi_priv *to_omap3_spi(struct spi_slave *slave)
{
return container_of(slave, struct omap3_spi_priv, slave);
}
void spi_init(void)
{
/* do nothing */
}
void spi_free_slave(struct spi_slave *slave)
{
struct omap3_spi_priv *priv = to_omap3_spi(slave);
free(priv);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct omap3_spi_priv *priv = to_omap3_spi(slave);
_omap3_spi_claim_bus(priv);
_omap3_spi_set_wordlen(priv);
_omap3_spi_set_mode(priv);
_omap3_spi_set_speed(priv);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct omap3_spi_priv *priv = to_omap3_spi(slave);
/* Reset the SPI hardware */
spi_reset(priv->regs);
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct omap3_spi_priv *priv;
struct mcspi *regs;
/*
* OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules)
* with different number of chip selects (CS, channels):
* McSPI1 has 4 CS (bus 0, cs 0 - 3)
* McSPI2 has 2 CS (bus 1, cs 0 - 1)
* McSPI3 has 2 CS (bus 2, cs 0 - 1)
* McSPI4 has 1 CS (bus 3, cs 0)
*/
switch (bus) {
case 0:
regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
break;
#ifdef OMAP3_MCSPI2_BASE
case 1:
regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
break;
#endif
#ifdef OMAP3_MCSPI3_BASE
case 2:
regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
break;
#endif
#ifdef OMAP3_MCSPI4_BASE
case 3:
regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
break;
#endif
default:
printf("SPI error: unsupported bus %i. Supported busses 0 - 3\n", bus);
return NULL;
}
if (((bus == 0) && (cs > 3)) ||
((bus == 1) && (cs > 1)) ||
((bus == 2) && (cs > 1)) ||
((bus == 3) && (cs > 0))) {
printf("SPI error: unsupported chip select %i on bus %i\n", cs, bus);
return NULL;
}
if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
printf("SPI error: unsupported frequency %i Hz. Max frequency is 48 Mhz\n", max_hz);
return NULL;
}
if (mode > SPI_MODE_3) {
printf("SPI error: unsupported SPI mode %i\n", mode);
return NULL;
}
priv = spi_alloc_slave(struct omap3_spi_priv, bus, cs);
if (!priv) {
printf("SPI error: malloc of SPI structure failed\n");
return NULL;
}
priv->regs = regs;
priv->cs = cs;
priv->freq = max_hz;
priv->mode = mode;
priv->wordlen = priv->slave.wordlen;
#ifdef CONFIG_OMAP3_SPI_D0_D1_SWAPPED
priv->pin_dir = MCSPI_PINDIR_D0_OUT_D1_IN;
#endif
return &priv->slave;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct omap3_spi_priv *priv = to_omap3_spi(slave);
return _spi_xfer(priv, bitlen, dout, din, flags);
}
#else
static int omap3_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct omap3_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
priv->cs = slave_plat->cs;
priv->mode = slave_plat->mode;
priv->freq = slave_plat->max_hz;
_omap3_spi_claim_bus(priv);
return 0;
}
static int omap3_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct omap3_spi_priv *priv = dev_get_priv(bus);
/* Reset the SPI hardware */
spi_reset(priv->regs);
return 0;
}
static int omap3_spi_set_wordlen(struct udevice *dev, unsigned int wordlen)
{
struct udevice *bus = dev->parent;
struct omap3_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
priv->cs = slave_plat->cs;
priv->wordlen = wordlen;
_omap3_spi_set_wordlen(priv);
return 0;
}
static int omap3_spi_probe(struct udevice *dev)
{
struct omap3_spi_priv *priv = dev_get_priv(dev);
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
struct omap2_mcspi_platform_config* data =
(struct omap2_mcspi_platform_config*)dev_get_driver_data(dev);
priv->regs = (struct mcspi *)(dev_get_addr(dev) + data->regs_offset);
priv->pin_dir = fdtdec_get_uint(blob, node, "ti,pindir-d0-out-d1-in",
MCSPI_PINDIR_D0_IN_D1_OUT);
priv->wordlen = SPI_DEFAULT_WORDLEN;
return 0;
}
static int omap3_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct omap3_spi_priv *priv = dev_get_priv(bus);
return _spi_xfer(priv, bitlen, dout, din, flags);
}
static int omap3_spi_set_speed(struct udevice *bus, unsigned int speed)
{
return 0;
}
static int omap3_spi_set_mode(struct udevice *bus, uint mode)
{
return 0;
}
static const struct dm_spi_ops omap3_spi_ops = {
.claim_bus = omap3_spi_claim_bus,
.release_bus = omap3_spi_release_bus,
.set_wordlen = omap3_spi_set_wordlen,
.xfer = omap3_spi_xfer,
.set_speed = omap3_spi_set_speed,
.set_mode = omap3_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static struct omap2_mcspi_platform_config omap2_pdata = {
.regs_offset = 0,
};
static struct omap2_mcspi_platform_config omap4_pdata = {
.regs_offset = OMAP4_MCSPI_REG_OFFSET,
};
static const struct udevice_id omap3_spi_ids[] = {
{ .compatible = "ti,omap2-mcspi", .data = (ulong)&omap2_pdata },
{ .compatible = "ti,omap4-mcspi", .data = (ulong)&omap4_pdata },
{ }
};
U_BOOT_DRIVER(omap3_spi) = {
.name = "omap3_spi",
.id = UCLASS_SPI,
.of_match = omap3_spi_ids,
.probe = omap3_spi_probe,
.ops = &omap3_spi_ops,
.priv_auto_alloc_size = sizeof(struct omap3_spi_priv),
.probe = omap3_spi_probe,
};
#endif

View File

@@ -0,0 +1,448 @@
/*
* Microchip PIC32 SPI controller driver.
*
* Copyright (c) 2015, Microchip Technology Inc.
* Purna Chandra Mandal <purna.mandal@microchip.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <linux/compat.h>
#include <malloc.h>
#include <spi.h>
#include <asm/types.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <dt-bindings/clock/microchip,clock.h>
#include <mach/pic32.h>
DECLARE_GLOBAL_DATA_PTR;
/* PIC32 SPI controller registers */
struct pic32_reg_spi {
struct pic32_reg_atomic ctrl;
struct pic32_reg_atomic status;
struct pic32_reg_atomic buf;
struct pic32_reg_atomic baud;
struct pic32_reg_atomic ctrl2;
};
/* Bit fields in SPI Control Register */
#define PIC32_SPI_CTRL_MSTEN BIT(5) /* Enable SPI Master */
#define PIC32_SPI_CTRL_CKP BIT(6) /* active low */
#define PIC32_SPI_CTRL_CKE BIT(8) /* Tx on falling edge */
#define PIC32_SPI_CTRL_SMP BIT(9) /* Rx at middle or end of tx */
#define PIC32_SPI_CTRL_BPW_MASK 0x03 /* Bits per word */
#define PIC32_SPI_CTRL_BPW_8 0x0
#define PIC32_SPI_CTRL_BPW_16 0x1
#define PIC32_SPI_CTRL_BPW_32 0x2
#define PIC32_SPI_CTRL_BPW_SHIFT 10
#define PIC32_SPI_CTRL_ON BIT(15) /* Macro enable */
#define PIC32_SPI_CTRL_ENHBUF BIT(16) /* Enable enhanced buffering */
#define PIC32_SPI_CTRL_MCLKSEL BIT(23) /* Select SPI Clock src */
#define PIC32_SPI_CTRL_MSSEN BIT(28) /* SPI macro will drive SS */
#define PIC32_SPI_CTRL_FRMEN BIT(31) /* Enable framing mode */
/* Bit fields in SPI Status Register */
#define PIC32_SPI_STAT_RX_OV BIT(6) /* err, s/w needs to clear */
#define PIC32_SPI_STAT_TF_LVL_MASK 0x1f
#define PIC32_SPI_STAT_TF_LVL_SHIFT 16
#define PIC32_SPI_STAT_RF_LVL_MASK 0x1f
#define PIC32_SPI_STAT_RF_LVL_SHIFT 24
/* Bit fields in SPI Baud Register */
#define PIC32_SPI_BAUD_MASK 0x1ff
struct pic32_spi_priv {
struct pic32_reg_spi *regs;
u32 fifo_depth; /* FIFO depth in bytes */
u32 fifo_n_word; /* FIFO depth in words */
struct gpio_desc cs_gpio;
/* Current SPI slave specific */
ulong clk_rate;
u32 speed_hz; /* spi-clk rate */
int mode;
/* Current message/transfer state */
const void *tx;
const void *tx_end;
const void *rx;
const void *rx_end;
u32 len;
/* SPI FiFo accessor */
void (*rx_fifo)(struct pic32_spi_priv *);
void (*tx_fifo)(struct pic32_spi_priv *);
};
static inline void pic32_spi_enable(struct pic32_spi_priv *priv)
{
writel(PIC32_SPI_CTRL_ON, &priv->regs->ctrl.set);
}
static inline void pic32_spi_disable(struct pic32_spi_priv *priv)
{
writel(PIC32_SPI_CTRL_ON, &priv->regs->ctrl.clr);
}
static inline u32 pic32_spi_rx_fifo_level(struct pic32_spi_priv *priv)
{
u32 sr = readl(&priv->regs->status.raw);
return (sr >> PIC32_SPI_STAT_RF_LVL_SHIFT) & PIC32_SPI_STAT_RF_LVL_MASK;
}
static inline u32 pic32_spi_tx_fifo_level(struct pic32_spi_priv *priv)
{
u32 sr = readl(&priv->regs->status.raw);
return (sr >> PIC32_SPI_STAT_TF_LVL_SHIFT) & PIC32_SPI_STAT_TF_LVL_MASK;
}
/* Return the max entries we can fill into tx fifo */
static u32 pic32_tx_max(struct pic32_spi_priv *priv, int n_bytes)
{
u32 tx_left, tx_room, rxtx_gap;
tx_left = (priv->tx_end - priv->tx) / n_bytes;
tx_room = priv->fifo_n_word - pic32_spi_tx_fifo_level(priv);
rxtx_gap = (priv->rx_end - priv->rx) - (priv->tx_end - priv->tx);
rxtx_gap /= n_bytes;
return min3(tx_left, tx_room, (u32)(priv->fifo_n_word - rxtx_gap));
}
/* Return the max entries we should read out of rx fifo */
static u32 pic32_rx_max(struct pic32_spi_priv *priv, int n_bytes)
{
u32 rx_left = (priv->rx_end - priv->rx) / n_bytes;
return min_t(u32, rx_left, pic32_spi_rx_fifo_level(priv));
}
#define BUILD_SPI_FIFO_RW(__name, __type, __bwl) \
static void pic32_spi_rx_##__name(struct pic32_spi_priv *priv) \
{ \
__type val; \
u32 mx = pic32_rx_max(priv, sizeof(__type)); \
\
for (; mx; mx--) { \
val = read##__bwl(&priv->regs->buf.raw); \
if (priv->rx_end - priv->len) \
*(__type *)(priv->rx) = val; \
priv->rx += sizeof(__type); \
} \
} \
\
static void pic32_spi_tx_##__name(struct pic32_spi_priv *priv) \
{ \
__type val; \
u32 mx = pic32_tx_max(priv, sizeof(__type)); \
\
for (; mx ; mx--) { \
val = (__type) ~0U; \
if (priv->tx_end - priv->len) \
val = *(__type *)(priv->tx); \
write##__bwl(val, &priv->regs->buf.raw); \
priv->tx += sizeof(__type); \
} \
}
BUILD_SPI_FIFO_RW(byte, u8, b);
BUILD_SPI_FIFO_RW(word, u16, w);
BUILD_SPI_FIFO_RW(dword, u32, l);
static int pic32_spi_set_word_size(struct pic32_spi_priv *priv,
unsigned int wordlen)
{
u32 bits_per_word;
u32 val;
switch (wordlen) {
case 8:
priv->rx_fifo = pic32_spi_rx_byte;
priv->tx_fifo = pic32_spi_tx_byte;
bits_per_word = PIC32_SPI_CTRL_BPW_8;
break;
case 16:
priv->rx_fifo = pic32_spi_rx_word;
priv->tx_fifo = pic32_spi_tx_word;
bits_per_word = PIC32_SPI_CTRL_BPW_16;
break;
case 32:
priv->rx_fifo = pic32_spi_rx_dword;
priv->tx_fifo = pic32_spi_tx_dword;
bits_per_word = PIC32_SPI_CTRL_BPW_32;
break;
default:
printf("pic32-spi: unsupported wordlen\n");
return -EINVAL;
}
/* set bits-per-word */
val = readl(&priv->regs->ctrl.raw);
val &= ~(PIC32_SPI_CTRL_BPW_MASK << PIC32_SPI_CTRL_BPW_SHIFT);
val |= bits_per_word << PIC32_SPI_CTRL_BPW_SHIFT;
writel(val, &priv->regs->ctrl.raw);
/* calculate maximum number of words fifo can hold */
priv->fifo_n_word = DIV_ROUND_UP(priv->fifo_depth, wordlen / 8);
return 0;
}
static int pic32_spi_claim_bus(struct udevice *slave)
{
struct pic32_spi_priv *priv = dev_get_priv(slave->parent);
/* enable chip */
pic32_spi_enable(priv);
return 0;
}
static int pic32_spi_release_bus(struct udevice *slave)
{
struct pic32_spi_priv *priv = dev_get_priv(slave->parent);
/* disable chip */
pic32_spi_disable(priv);
return 0;
}
static void spi_cs_activate(struct pic32_spi_priv *priv)
{
if (!dm_gpio_is_valid(&priv->cs_gpio))
return;
dm_gpio_set_value(&priv->cs_gpio, 1);
}
static void spi_cs_deactivate(struct pic32_spi_priv *priv)
{
if (!dm_gpio_is_valid(&priv->cs_gpio))
return;
dm_gpio_set_value(&priv->cs_gpio, 0);
}
static int pic32_spi_xfer(struct udevice *slave, unsigned int bitlen,
const void *tx_buf, void *rx_buf,
unsigned long flags)
{
struct dm_spi_slave_platdata *slave_plat;
struct udevice *bus = slave->parent;
struct pic32_spi_priv *priv;
int len = bitlen / 8;
int ret = 0;
ulong tbase;
priv = dev_get_priv(bus);
slave_plat = dev_get_parent_platdata(slave);
debug("spi_xfer: bus:%i cs:%i flags:%lx\n",
bus->seq, slave_plat->cs, flags);
debug("msg tx %p, rx %p submitted of %d byte(s)\n",
tx_buf, rx_buf, len);
/* assert cs */
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(priv);
/* set current transfer information */
priv->tx = tx_buf;
priv->rx = rx_buf;
priv->tx_end = priv->tx + len;
priv->rx_end = priv->rx + len;
priv->len = len;
/* transact by polling */
tbase = get_timer(0);
for (;;) {
priv->tx_fifo(priv);
priv->rx_fifo(priv);
/* received sufficient data */
if (priv->rx >= priv->rx_end) {
ret = 0;
break;
}
if (get_timer(tbase) > 5 * CONFIG_SYS_HZ) {
printf("pic32_spi: error, xfer timedout.\n");
flags |= SPI_XFER_END;
ret = -ETIMEDOUT;
break;
}
}
/* deassert cs */
if (flags & SPI_XFER_END)
spi_cs_deactivate(priv);
return ret;
}
static int pic32_spi_set_speed(struct udevice *bus, uint speed)
{
struct pic32_spi_priv *priv = dev_get_priv(bus);
u32 div;
debug("%s: %s, speed %u\n", __func__, bus->name, speed);
/* div = [clk_in / (2 * spi_clk)] - 1 */
div = (priv->clk_rate / 2 / speed) - 1;
div &= PIC32_SPI_BAUD_MASK;
writel(div, &priv->regs->baud.raw);
priv->speed_hz = speed;
return 0;
}
static int pic32_spi_set_mode(struct udevice *bus, uint mode)
{
struct pic32_spi_priv *priv = dev_get_priv(bus);
u32 val;
debug("%s: %s, mode %d\n", __func__, bus->name, mode);
/* set spi-clk mode */
val = readl(&priv->regs->ctrl.raw);
/* HIGH when idle */
if (mode & SPI_CPOL)
val |= PIC32_SPI_CTRL_CKP;
else
val &= ~PIC32_SPI_CTRL_CKP;
/* TX at idle-to-active clk transition */
if (mode & SPI_CPHA)
val &= ~PIC32_SPI_CTRL_CKE;
else
val |= PIC32_SPI_CTRL_CKE;
/* RX at end of tx */
val |= PIC32_SPI_CTRL_SMP;
writel(val, &priv->regs->ctrl.raw);
priv->mode = mode;
return 0;
}
static int pic32_spi_set_wordlen(struct udevice *slave, unsigned int wordlen)
{
struct pic32_spi_priv *priv = dev_get_priv(slave->parent);
return pic32_spi_set_word_size(priv, wordlen);
}
static void pic32_spi_hw_init(struct pic32_spi_priv *priv)
{
u32 val;
/* disable module */
pic32_spi_disable(priv);
val = readl(&priv->regs->ctrl);
/* enable enhanced fifo of 128bit deep */
val |= PIC32_SPI_CTRL_ENHBUF;
priv->fifo_depth = 16;
/* disable framing mode */
val &= ~PIC32_SPI_CTRL_FRMEN;
/* enable master mode */
val |= PIC32_SPI_CTRL_MSTEN;
/* select clk source */
val &= ~PIC32_SPI_CTRL_MCLKSEL;
/* set manual /CS mode */
val &= ~PIC32_SPI_CTRL_MSSEN;
writel(val, &priv->regs->ctrl);
/* clear rx overflow indicator */
writel(PIC32_SPI_STAT_RX_OV, &priv->regs->status.clr);
}
static int pic32_spi_probe(struct udevice *bus)
{
struct pic32_spi_priv *priv = dev_get_priv(bus);
struct dm_spi_bus *dm_spi = dev_get_uclass_priv(bus);
struct udevice *clkdev;
fdt_addr_t addr;
fdt_size_t size;
int ret;
debug("%s: %d, bus: %i\n", __func__, __LINE__, bus->seq);
addr = fdtdec_get_addr_size(gd->fdt_blob, bus->of_offset, "reg", &size);
if (addr == FDT_ADDR_T_NONE)
return -EINVAL;
priv->regs = ioremap(addr, size);
if (!priv->regs)
return -EINVAL;
dm_spi->max_hz = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
"spi-max-frequency", 250000000);
/* get clock rate */
ret = clk_get_by_index(bus, 0, &clkdev);
if (ret < 0) {
printf("pic32-spi: error, clk not found\n");
return ret;
}
priv->clk_rate = clk_get_periph_rate(clkdev, ret);
/* initialize HW */
pic32_spi_hw_init(priv);
/* set word len */
pic32_spi_set_word_size(priv, SPI_DEFAULT_WORDLEN);
/* PIC32 SPI controller can automatically drive /CS during transfer
* depending on fifo fill-level. /CS will stay asserted as long as
* TX fifo is non-empty, else will be deasserted confirming completion
* of the ongoing transfer. To avoid this sort of error we will drive
* /CS manually by toggling cs-gpio pins.
*/
ret = gpio_request_by_name_nodev(gd->fdt_blob, bus->of_offset,
"cs-gpios", 0,
&priv->cs_gpio, GPIOD_IS_OUT);
if (ret) {
printf("pic32-spi: error, cs-gpios not found\n");
return ret;
}
return 0;
}
static const struct dm_spi_ops pic32_spi_ops = {
.claim_bus = pic32_spi_claim_bus,
.release_bus = pic32_spi_release_bus,
.xfer = pic32_spi_xfer,
.set_speed = pic32_spi_set_speed,
.set_mode = pic32_spi_set_mode,
.set_wordlen = pic32_spi_set_wordlen,
};
static const struct udevice_id pic32_spi_ids[] = {
{ .compatible = "microchip,pic32mzda-spi" },
{ }
};
U_BOOT_DRIVER(pic32_spi) = {
.name = "pic32_spi",
.id = UCLASS_SPI,
.of_match = pic32_spi_ids,
.ops = &pic32_spi_ops,
.priv_auto_alloc_size = sizeof(struct pic32_spi_priv),
.probe = pic32_spi_probe,
};

380
u-boot/drivers/spi/rk_spi.c Normal file
View File

@@ -0,0 +1,380 @@
/*
* spi driver for rockchip
*
* (C) Copyright 2015 Google, Inc
*
* (C) Copyright 2008-2013 Rockchip Electronics
* Peter, Software Engineering, <superpeter.cai@gmail.com>.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <clk.h>
#include <dm.h>
#include <errno.h>
#include <spi.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch/periph.h>
#include <dm/pinctrl.h>
#include "rk_spi.h"
DECLARE_GLOBAL_DATA_PTR;
/* Change to 1 to output registers at the start of each transaction */
#define DEBUG_RK_SPI 0
struct rockchip_spi_platdata {
s32 frequency; /* Default clock frequency, -1 for none */
fdt_addr_t base;
uint deactivate_delay_us; /* Delay to wait after deactivate */
uint activate_delay_us; /* Delay to wait after activate */
};
struct rockchip_spi_priv {
struct rockchip_spi *regs;
struct clk clk;
unsigned int max_freq;
unsigned int mode;
ulong last_transaction_us; /* Time of last transaction end */
u8 bits_per_word; /* max 16 bits per word */
u8 n_bytes;
unsigned int speed_hz;
unsigned int last_speed_hz;
unsigned int tmode;
uint input_rate;
};
#define SPI_FIFO_DEPTH 32
static void rkspi_dump_regs(struct rockchip_spi *regs)
{
debug("ctrl0: \t\t0x%08x\n", readl(&regs->ctrlr0));
debug("ctrl1: \t\t0x%08x\n", readl(&regs->ctrlr1));
debug("ssienr: \t\t0x%08x\n", readl(&regs->enr));
debug("ser: \t\t0x%08x\n", readl(&regs->ser));
debug("baudr: \t\t0x%08x\n", readl(&regs->baudr));
debug("txftlr: \t\t0x%08x\n", readl(&regs->txftlr));
debug("rxftlr: \t\t0x%08x\n", readl(&regs->rxftlr));
debug("txflr: \t\t0x%08x\n", readl(&regs->txflr));
debug("rxflr: \t\t0x%08x\n", readl(&regs->rxflr));
debug("sr: \t\t0x%08x\n", readl(&regs->sr));
debug("imr: \t\t0x%08x\n", readl(&regs->imr));
debug("isr: \t\t0x%08x\n", readl(&regs->isr));
debug("dmacr: \t\t0x%08x\n", readl(&regs->dmacr));
debug("dmatdlr: \t0x%08x\n", readl(&regs->dmatdlr));
debug("dmardlr: \t0x%08x\n", readl(&regs->dmardlr));
}
static void rkspi_enable_chip(struct rockchip_spi *regs, bool enable)
{
writel(enable ? 1 : 0, &regs->enr);
}
static void rkspi_set_clk(struct rockchip_spi_priv *priv, uint speed)
{
uint clk_div;
clk_div = clk_get_divisor(priv->input_rate, speed);
debug("spi speed %u, div %u\n", speed, clk_div);
writel(clk_div, &priv->regs->baudr);
priv->last_speed_hz = speed;
}
static int rkspi_wait_till_not_busy(struct rockchip_spi *regs)
{
unsigned long start;
start = get_timer(0);
while (readl(&regs->sr) & SR_BUSY) {
if (get_timer(start) > ROCKCHIP_SPI_TIMEOUT_MS) {
debug("RK SPI: Status keeps busy for 1000us after a read/write!\n");
return -ETIMEDOUT;
}
}
return 0;
}
static void spi_cs_activate(struct udevice *dev, uint cs)
{
struct udevice *bus = dev->parent;
struct rockchip_spi_platdata *plat = bus->platdata;
struct rockchip_spi_priv *priv = dev_get_priv(bus);
struct rockchip_spi *regs = priv->regs;
debug("activate cs%u\n", cs);
writel(1 << cs, &regs->ser);
if (plat->activate_delay_us)
udelay(plat->activate_delay_us);
}
static void spi_cs_deactivate(struct udevice *dev, uint cs)
{
struct udevice *bus = dev->parent;
struct rockchip_spi_platdata *plat = bus->platdata;
struct rockchip_spi_priv *priv = dev_get_priv(bus);
struct rockchip_spi *regs = priv->regs;
debug("deactivate cs%u\n", cs);
writel(0, &regs->ser);
/* Remember time of this transaction so we can honour the bus delay */
if (plat->deactivate_delay_us)
priv->last_transaction_us = timer_get_us();
}
static int rockchip_spi_ofdata_to_platdata(struct udevice *bus)
{
struct rockchip_spi_platdata *plat = bus->platdata;
struct rockchip_spi_priv *priv = dev_get_priv(bus);
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
int ret;
plat->base = dev_get_addr(bus);
ret = clk_get_by_index(bus, 0, &priv->clk);
if (ret < 0) {
debug("%s: Could not get clock for %s: %d\n", __func__,
bus->name, ret);
return ret;
}
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
50000000);
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
plat->activate_delay_us = fdtdec_get_int(blob, node,
"spi-activate-delay", 0);
debug("%s: base=%x, max-frequency=%d, deactivate_delay=%d\n",
__func__, (uint)plat->base, plat->frequency,
plat->deactivate_delay_us);
return 0;
}
static int rockchip_spi_probe(struct udevice *bus)
{
struct rockchip_spi_platdata *plat = dev_get_platdata(bus);
struct rockchip_spi_priv *priv = dev_get_priv(bus);
int ret;
debug("%s: probe\n", __func__);
priv->regs = (struct rockchip_spi *)plat->base;
priv->last_transaction_us = timer_get_us();
priv->max_freq = plat->frequency;
/*
* Use 99 MHz as our clock since it divides nicely into 594 MHz which
* is the assumed speed for CLK_GENERAL.
*/
ret = clk_set_rate(&priv->clk, 99000000);
if (ret < 0) {
debug("%s: Failed to set clock: %d\n", __func__, ret);
return ret;
}
priv->input_rate = ret;
debug("%s: rate = %u\n", __func__, priv->input_rate);
priv->bits_per_word = 8;
priv->tmode = TMOD_TR; /* Tx & Rx */
return 0;
}
static int rockchip_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct rockchip_spi_priv *priv = dev_get_priv(bus);
struct rockchip_spi *regs = priv->regs;
u8 spi_dfs, spi_tf;
uint ctrlr0;
/* Disable the SPI hardware */
rkspi_enable_chip(regs, 0);
switch (priv->bits_per_word) {
case 8:
priv->n_bytes = 1;
spi_dfs = DFS_8BIT;
spi_tf = HALF_WORD_OFF;
break;
case 16:
priv->n_bytes = 2;
spi_dfs = DFS_16BIT;
spi_tf = HALF_WORD_ON;
break;
default:
debug("%s: unsupported bits: %dbits\n", __func__,
priv->bits_per_word);
return -EPROTONOSUPPORT;
}
if (priv->speed_hz != priv->last_speed_hz)
rkspi_set_clk(priv, priv->speed_hz);
/* Operation Mode */
ctrlr0 = OMOD_MASTER << OMOD_SHIFT;
/* Data Frame Size */
ctrlr0 |= spi_dfs << DFS_SHIFT;
/* set SPI mode 0..3 */
if (priv->mode & SPI_CPOL)
ctrlr0 |= SCOL_HIGH << SCOL_SHIFT;
if (priv->mode & SPI_CPHA)
ctrlr0 |= SCPH_TOGSTA << SCPH_SHIFT;
/* Chip Select Mode */
ctrlr0 |= CSM_KEEP << CSM_SHIFT;
/* SSN to Sclk_out delay */
ctrlr0 |= SSN_DELAY_ONE << SSN_DELAY_SHIFT;
/* Serial Endian Mode */
ctrlr0 |= SEM_LITTLE << SEM_SHIFT;
/* First Bit Mode */
ctrlr0 |= FBM_MSB << FBM_SHIFT;
/* Byte and Halfword Transform */
ctrlr0 |= spi_tf << HALF_WORD_TX_SHIFT;
/* Rxd Sample Delay */
ctrlr0 |= 0 << RXDSD_SHIFT;
/* Frame Format */
ctrlr0 |= FRF_SPI << FRF_SHIFT;
/* Tx and Rx mode */
ctrlr0 |= (priv->tmode & TMOD_MASK) << TMOD_SHIFT;
writel(ctrlr0, &regs->ctrlr0);
return 0;
}
static int rockchip_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct rockchip_spi_priv *priv = dev_get_priv(bus);
rkspi_enable_chip(priv->regs, false);
return 0;
}
static int rockchip_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct rockchip_spi_priv *priv = dev_get_priv(bus);
struct rockchip_spi *regs = priv->regs;
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
int len = bitlen >> 3;
const u8 *out = dout;
u8 *in = din;
int toread, towrite;
int ret;
debug("%s: dout=%p, din=%p, len=%x, flags=%lx\n", __func__, dout, din,
len, flags);
if (DEBUG_RK_SPI)
rkspi_dump_regs(regs);
/* Assert CS before transfer */
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev, slave_plat->cs);
while (len > 0) {
int todo = min(len, 0xffff);
rkspi_enable_chip(regs, false);
writel(todo - 1, &regs->ctrlr1);
rkspi_enable_chip(regs, true);
toread = todo;
towrite = todo;
while (toread || towrite) {
u32 status = readl(&regs->sr);
if (towrite && !(status & SR_TF_FULL)) {
writel(out ? *out++ : 0, regs->txdr);
towrite--;
}
if (toread && !(status & SR_RF_EMPT)) {
u32 byte = readl(regs->rxdr);
if (in)
*in++ = byte;
toread--;
}
}
ret = rkspi_wait_till_not_busy(regs);
if (ret)
break;
len -= todo;
}
/* Deassert CS after transfer */
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev, slave_plat->cs);
rkspi_enable_chip(regs, false);
return ret;
}
static int rockchip_spi_set_speed(struct udevice *bus, uint speed)
{
struct rockchip_spi_priv *priv = dev_get_priv(bus);
if (speed > ROCKCHIP_SPI_MAX_RATE)
return -EINVAL;
if (speed > priv->max_freq)
speed = priv->max_freq;
priv->speed_hz = speed;
return 0;
}
static int rockchip_spi_set_mode(struct udevice *bus, uint mode)
{
struct rockchip_spi_priv *priv = dev_get_priv(bus);
priv->mode = mode;
return 0;
}
static const struct dm_spi_ops rockchip_spi_ops = {
.claim_bus = rockchip_spi_claim_bus,
.release_bus = rockchip_spi_release_bus,
.xfer = rockchip_spi_xfer,
.set_speed = rockchip_spi_set_speed,
.set_mode = rockchip_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id rockchip_spi_ids[] = {
{ .compatible = "rockchip,rk3288-spi" },
{ }
};
U_BOOT_DRIVER(rockchip_spi) = {
.name = "rockchip_spi",
.id = UCLASS_SPI,
.of_match = rockchip_spi_ids,
.ops = &rockchip_spi_ops,
.ofdata_to_platdata = rockchip_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct rockchip_spi_platdata),
.priv_auto_alloc_size = sizeof(struct rockchip_spi_priv),
.probe = rockchip_spi_probe,
};

124
u-boot/drivers/spi/rk_spi.h Normal file
View File

@@ -0,0 +1,124 @@
/*
* SPI driver for rockchip
*
* (C) Copyright 2015 Google, Inc
*
* (C) Copyright 2008-2013 Rockchip Electronics
* Peter, Software Engineering, <superpeter.cai@gmail.com>.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#ifndef __RK_SPI_H
#define __RK_SPI_H
struct rockchip_spi {
u32 ctrlr0;
u32 ctrlr1;
u32 enr;
u32 ser;
u32 baudr;
u32 txftlr;
u32 rxftlr;
u32 txflr;
u32 rxflr;
u32 sr;
u32 ipr;
u32 imr;
u32 isr;
u32 risr;
u32 icr;
u32 dmacr;
u32 dmatdlr;
u32 dmardlr; /* 0x44 */
u32 reserved[0xef];
u32 txdr[0x100]; /* 0x400 */
u32 rxdr[0x100]; /* 0x800 */
};
/* CTRLR0 */
enum {
DFS_SHIFT = 0, /* Data Frame Size */
DFS_MASK = 3,
DFS_4BIT = 0,
DFS_8BIT,
DFS_16BIT,
DFS_RESV,
CFS_SHIFT = 2, /* Control Frame Size */
CFS_MASK = 0xf,
SCPH_SHIFT = 6, /* Serial Clock Phase */
SCPH_MASK = 1,
SCPH_TOGMID = 0, /* SCLK toggles in middle of first data bit */
SCPH_TOGSTA, /* SCLK toggles at start of first data bit */
SCOL_SHIFT = 7, /* Serial Clock Polarity */
SCOL_MASK = 1,
SCOL_LOW = 0, /* Inactive state of serial clock is low */
SCOL_HIGH, /* Inactive state of serial clock is high */
CSM_SHIFT = 8, /* Chip Select Mode */
CSM_MASK = 0x3,
CSM_KEEP = 0, /* ss_n stays low after each frame */
CSM_HALF, /* ss_n high for half sclk_out cycles */
CSM_ONE, /* ss_n high for one sclk_out cycle */
CSM_RESV,
SSN_DELAY_SHIFT = 10, /* SSN to Sclk_out delay */
SSN_DELAY_MASK = 1,
SSN_DELAY_HALF = 0, /* 1/2 sclk_out cycle */
SSN_DELAY_ONE = 1, /* 1 sclk_out cycle */
SEM_SHIFT = 11, /* Serial Endian Mode */
SEM_MASK = 1,
SEM_LITTLE = 0, /* little endian */
SEM_BIG, /* big endian */
FBM_SHIFT = 12, /* First Bit Mode */
FBM_MASK = 1,
FBM_MSB = 0, /* first bit is MSB */
FBM_LSB, /* first bit in LSB */
HALF_WORD_TX_SHIFT = 13, /* Byte and Halfword Transform */
HALF_WORD_MASK = 1,
HALF_WORD_ON = 0, /* apb 16bit write/read, spi 8bit write/read */
HALF_WORD_OFF, /* apb 8bit write/read, spi 8bit write/read */
RXDSD_SHIFT = 14, /* Rxd Sample Delay, in cycles */
RXDSD_MASK = 3,
FRF_SHIFT = 16, /* Frame Format */
FRF_MASK = 3,
FRF_SPI = 0, /* Motorola SPI */
FRF_SSP, /* Texas Instruments SSP*/
FRF_MICROWIRE, /* National Semiconductors Microwire */
FRF_RESV,
TMOD_SHIFT = 18, /* Transfer Mode */
TMOD_MASK = 3,
TMOD_TR = 0, /* xmit & recv */
TMOD_TO, /* xmit only */
TMOD_RO, /* recv only */
TMOD_RESV,
OMOD_SHIFT = 20, /* Operation Mode */
OMOD_MASK = 1,
OMOD_MASTER = 0, /* Master Mode */
OMOD_SLAVE, /* Slave Mode */
};
/* SR */
enum {
SR_MASK = 0x7f,
SR_BUSY = 1 << 0,
SR_TF_FULL = 1 << 1,
SR_TF_EMPT = 1 << 2,
SR_RF_EMPT = 1 << 3,
SR_RF_FULL = 1 << 4,
};
#define ROCKCHIP_SPI_TIMEOUT_MS 1000
#define ROCKCHIP_SPI_MAX_RATE 48000000
#endif /* __RK_SPI_H */

View File

@@ -0,0 +1,164 @@
/*
* Simulate a SPI port
*
* Copyright (c) 2011-2013 The Chromium OS Authors.
* See file CREDITS for list of people who contributed to this
* project.
*
* Licensed under the GPL-2 or later.
*/
#include <common.h>
#include <dm.h>
#include <malloc.h>
#include <spi.h>
#include <spi_flash.h>
#include <os.h>
#include <asm/errno.h>
#include <asm/spi.h>
#include <asm/state.h>
#include <dm/device-internal.h>
DECLARE_GLOBAL_DATA_PTR;
#ifndef CONFIG_SPI_IDLE_VAL
# define CONFIG_SPI_IDLE_VAL 0xFF
#endif
const char *sandbox_spi_parse_spec(const char *arg, unsigned long *bus,
unsigned long *cs)
{
char *endp;
*bus = simple_strtoul(arg, &endp, 0);
if (*endp != ':' || *bus >= CONFIG_SANDBOX_SPI_MAX_BUS)
return NULL;
*cs = simple_strtoul(endp + 1, &endp, 0);
if (*endp != ':' || *cs >= CONFIG_SANDBOX_SPI_MAX_CS)
return NULL;
return endp + 1;
}
__weak int sandbox_spi_get_emul(struct sandbox_state *state,
struct udevice *bus, struct udevice *slave,
struct udevice **emulp)
{
return -ENOENT;
}
static int sandbox_spi_xfer(struct udevice *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = slave->parent;
struct sandbox_state *state = state_get_current();
struct dm_spi_emul_ops *ops;
struct udevice *emul;
uint bytes = bitlen / 8, i;
int ret;
u8 *tx = (void *)dout, *rx = din;
uint busnum, cs;
if (bitlen == 0)
return 0;
/* we can only do 8 bit transfers */
if (bitlen % 8) {
printf("sandbox_spi: xfer: invalid bitlen size %u; needs to be 8bit\n",
bitlen);
return -EINVAL;
}
busnum = bus->seq;
cs = spi_chip_select(slave);
if (busnum >= CONFIG_SANDBOX_SPI_MAX_BUS ||
cs >= CONFIG_SANDBOX_SPI_MAX_CS) {
printf("%s: busnum=%u, cs=%u: out of range\n", __func__,
busnum, cs);
return -ENOENT;
}
ret = sandbox_spi_get_emul(state, bus, slave, &emul);
if (ret) {
printf("%s: busnum=%u, cs=%u: no emulation available (err=%d)\n",
__func__, busnum, cs, ret);
return -ENOENT;
}
ret = device_probe(emul);
if (ret)
return ret;
/* make sure rx/tx buffers are full so clients can assume */
if (!tx) {
debug("sandbox_spi: xfer: auto-allocating tx scratch buffer\n");
tx = malloc(bytes);
if (!tx) {
debug("sandbox_spi: Out of memory\n");
return -ENOMEM;
}
}
if (!rx) {
debug("sandbox_spi: xfer: auto-allocating rx scratch buffer\n");
rx = malloc(bytes);
if (!rx) {
debug("sandbox_spi: Out of memory\n");
return -ENOMEM;
}
}
ops = spi_emul_get_ops(emul);
ret = ops->xfer(emul, bitlen, dout, din, flags);
debug("sandbox_spi: xfer: got back %i (that's %s)\n rx:",
ret, ret ? "bad" : "good");
for (i = 0; i < bytes; ++i)
debug(" %u:%02x", i, rx[i]);
debug("\n");
if (tx != dout)
free(tx);
if (rx != din)
free(rx);
return ret;
}
static int sandbox_spi_set_speed(struct udevice *bus, uint speed)
{
return 0;
}
static int sandbox_spi_set_mode(struct udevice *bus, uint mode)
{
return 0;
}
static int sandbox_cs_info(struct udevice *bus, uint cs,
struct spi_cs_info *info)
{
/* Always allow activity on CS 0 */
if (cs >= 1)
return -ENODEV;
return 0;
}
static const struct dm_spi_ops sandbox_spi_ops = {
.xfer = sandbox_spi_xfer,
.set_speed = sandbox_spi_set_speed,
.set_mode = sandbox_spi_set_mode,
.cs_info = sandbox_cs_info,
};
static const struct udevice_id sandbox_spi_ids[] = {
{ .compatible = "sandbox,spi" },
{ }
};
U_BOOT_DRIVER(spi_sandbox) = {
.name = "spi_sandbox",
.id = UCLASS_SPI,
.of_match = sandbox_spi_ids,
.ops = &sandbox_spi_ops,
};

View File

@@ -0,0 +1,279 @@
/*
* SH QSPI (Quad SPI) driver
*
* Copyright (C) 2013 Renesas Electronics Corporation
* Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <console.h>
#include <malloc.h>
#include <spi.h>
#include <asm/arch/rmobile.h>
#include <asm/io.h>
/* SH QSPI register bit masks <REG>_<BIT> */
#define SPCR_MSTR 0x08
#define SPCR_SPE 0x40
#define SPSR_SPRFF 0x80
#define SPSR_SPTEF 0x20
#define SPPCR_IO3FV 0x04
#define SPPCR_IO2FV 0x02
#define SPPCR_IO1FV 0x01
#define SPBDCR_RXBC0 BIT(0)
#define SPCMD_SCKDEN BIT(15)
#define SPCMD_SLNDEN BIT(14)
#define SPCMD_SPNDEN BIT(13)
#define SPCMD_SSLKP BIT(7)
#define SPCMD_BRDV0 BIT(2)
#define SPCMD_INIT1 SPCMD_SCKDEN | SPCMD_SLNDEN | \
SPCMD_SPNDEN | SPCMD_SSLKP | \
SPCMD_BRDV0
#define SPCMD_INIT2 SPCMD_SPNDEN | SPCMD_SSLKP | \
SPCMD_BRDV0
#define SPBFCR_TXRST BIT(7)
#define SPBFCR_RXRST BIT(6)
/* SH QSPI register set */
struct sh_qspi_regs {
unsigned char spcr;
unsigned char sslp;
unsigned char sppcr;
unsigned char spsr;
unsigned long spdr;
unsigned char spscr;
unsigned char spssr;
unsigned char spbr;
unsigned char spdcr;
unsigned char spckd;
unsigned char sslnd;
unsigned char spnd;
unsigned char dummy0;
unsigned short spcmd0;
unsigned short spcmd1;
unsigned short spcmd2;
unsigned short spcmd3;
unsigned char spbfcr;
unsigned char dummy1;
unsigned short spbdcr;
unsigned long spbmul0;
unsigned long spbmul1;
unsigned long spbmul2;
unsigned long spbmul3;
};
struct sh_qspi_slave {
struct spi_slave slave;
struct sh_qspi_regs *regs;
};
static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave)
{
return container_of(slave, struct sh_qspi_slave, slave);
}
static void sh_qspi_init(struct sh_qspi_slave *ss)
{
/* QSPI initialize */
/* Set master mode only */
writeb(SPCR_MSTR, &ss->regs->spcr);
/* Set SSL signal level */
writeb(0x00, &ss->regs->sslp);
/* Set MOSI signal value when transfer is in idle state */
writeb(SPPCR_IO3FV|SPPCR_IO2FV, &ss->regs->sppcr);
/* Set bit rate. See 58.3.8 Quad Serial Peripheral Interface */
writeb(0x01, &ss->regs->spbr);
/* Disable Dummy Data Transmission */
writeb(0x00, &ss->regs->spdcr);
/* Set clock delay value */
writeb(0x00, &ss->regs->spckd);
/* Set SSL negation delay value */
writeb(0x00, &ss->regs->sslnd);
/* Set next-access delay value */
writeb(0x00, &ss->regs->spnd);
/* Set equence command */
writew(SPCMD_INIT2, &ss->regs->spcmd0);
/* Reset transfer and receive Buffer */
setbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST);
/* Clear transfer and receive Buffer control bit */
clrbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST);
/* Set equence control method. Use equence0 only */
writeb(0x00, &ss->regs->spscr);
/* Enable SPI function */
setbits_8(&ss->regs->spcr, SPCR_SPE);
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return 1;
}
void spi_cs_activate(struct spi_slave *slave)
{
struct sh_qspi_slave *ss = to_sh_qspi(slave);
/* Set master mode only */
writeb(SPCR_MSTR, &ss->regs->spcr);
/* Set command */
writew(SPCMD_INIT1, &ss->regs->spcmd0);
/* Reset transfer and receive Buffer */
setbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST);
/* Clear transfer and receive Buffer control bit */
clrbits_8(&ss->regs->spbfcr, SPBFCR_TXRST|SPBFCR_RXRST);
/* Set equence control method. Use equence0 only */
writeb(0x00, &ss->regs->spscr);
/* Enable SPI function */
setbits_8(&ss->regs->spcr, SPCR_SPE);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct sh_qspi_slave *ss = to_sh_qspi(slave);
/* Disable SPI Function */
clrbits_8(&ss->regs->spcr, SPCR_SPE);
}
void spi_init(void)
{
/* nothing to do */
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct sh_qspi_slave *ss;
if (!spi_cs_is_valid(bus, cs))
return NULL;
ss = spi_alloc_slave(struct sh_qspi_slave, bus, cs);
if (!ss) {
printf("SPI_error: Fail to allocate sh_qspi_slave\n");
return NULL;
}
ss->regs = (struct sh_qspi_regs *)SH_QSPI_BASE;
/* Init SH QSPI */
sh_qspi_init(ss);
return &ss->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct sh_qspi_slave *spi = to_sh_qspi(slave);
free(spi);
}
int spi_claim_bus(struct spi_slave *slave)
{
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct sh_qspi_slave *ss = to_sh_qspi(slave);
unsigned long nbyte;
int ret = 0;
unsigned char dtdata = 0, drdata;
unsigned char *tdata = &dtdata, *rdata = &drdata;
unsigned long *spbmul0 = &ss->regs->spbmul0;
if (dout == NULL && din == NULL) {
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return 0;
}
if (bitlen % 8) {
printf("%s: bitlen is not 8bit alined %d", __func__, bitlen);
return 1;
}
nbyte = bitlen / 8;
if (flags & SPI_XFER_BEGIN) {
spi_cs_activate(slave);
/* Set 1048576 byte */
writel(0x100000, spbmul0);
}
if (flags & SPI_XFER_END)
writel(nbyte, spbmul0);
if (dout != NULL)
tdata = (unsigned char *)dout;
if (din != NULL)
rdata = din;
while (nbyte > 0) {
while (!(readb(&ss->regs->spsr) & SPSR_SPTEF)) {
if (ctrlc()) {
puts("abort\n");
return 1;
}
udelay(10);
}
writeb(*tdata, (unsigned char *)(&ss->regs->spdr));
while ((readw(&ss->regs->spbdcr) != SPBDCR_RXBC0)) {
if (ctrlc()) {
puts("abort\n");
return 1;
}
udelay(1);
}
while (!(readb(&ss->regs->spsr) & SPSR_SPRFF)) {
if (ctrlc()) {
puts("abort\n");
return 1;
}
udelay(10);
}
*rdata = readb((unsigned char *)(&ss->regs->spdr));
if (dout != NULL)
tdata++;
if (din != NULL)
rdata++;
nbyte--;
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return ret;
}

254
u-boot/drivers/spi/sh_spi.c Normal file
View File

@@ -0,0 +1,254 @@
/*
* SH SPI driver
*
* Copyright (C) 2011-2012 Renesas Solutions Corp.
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <console.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
#include "sh_spi.h"
static void sh_spi_write(unsigned long data, unsigned long *reg)
{
writel(data, reg);
}
static unsigned long sh_spi_read(unsigned long *reg)
{
return readl(reg);
}
static void sh_spi_set_bit(unsigned long val, unsigned long *reg)
{
unsigned long tmp;
tmp = sh_spi_read(reg);
tmp |= val;
sh_spi_write(tmp, reg);
}
static void sh_spi_clear_bit(unsigned long val, unsigned long *reg)
{
unsigned long tmp;
tmp = sh_spi_read(reg);
tmp &= ~val;
sh_spi_write(tmp, reg);
}
static void clear_fifo(struct sh_spi *ss)
{
sh_spi_set_bit(SH_SPI_RSTF, &ss->regs->cr2);
sh_spi_clear_bit(SH_SPI_RSTF, &ss->regs->cr2);
}
static int recvbuf_wait(struct sh_spi *ss)
{
while (sh_spi_read(&ss->regs->cr1) & SH_SPI_RBE) {
if (ctrlc())
return 1;
udelay(10);
}
return 0;
}
static int write_fifo_empty_wait(struct sh_spi *ss)
{
while (!(sh_spi_read(&ss->regs->cr1) & SH_SPI_TBE)) {
if (ctrlc())
return 1;
udelay(10);
}
return 0;
}
void spi_init(void)
{
}
static void sh_spi_set_cs(struct sh_spi *ss, unsigned int cs)
{
unsigned long val = 0;
if (cs & 0x01)
val |= SH_SPI_SSS0;
if (cs & 0x02)
val |= SH_SPI_SSS1;
sh_spi_clear_bit(SH_SPI_SSS0 | SH_SPI_SSS1, &ss->regs->cr4);
sh_spi_set_bit(val, &ss->regs->cr4);
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct sh_spi *ss;
if (!spi_cs_is_valid(bus, cs))
return NULL;
ss = spi_alloc_slave(struct sh_spi, bus, cs);
if (!ss)
return NULL;
ss->regs = (struct sh_spi_regs *)CONFIG_SH_SPI_BASE;
/* SPI sycle stop */
sh_spi_write(0xfe, &ss->regs->cr1);
/* CR1 init */
sh_spi_write(0x00, &ss->regs->cr1);
/* CR3 init */
sh_spi_write(0x00, &ss->regs->cr3);
sh_spi_set_cs(ss, cs);
clear_fifo(ss);
/* 1/8 clock */
sh_spi_write(sh_spi_read(&ss->regs->cr2) | 0x07, &ss->regs->cr2);
udelay(10);
return &ss->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct sh_spi *spi = to_sh_spi(slave);
free(spi);
}
int spi_claim_bus(struct spi_slave *slave)
{
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
struct sh_spi *ss = to_sh_spi(slave);
sh_spi_write(sh_spi_read(&ss->regs->cr1) &
~(SH_SPI_SSA | SH_SPI_SSDB | SH_SPI_SSD), &ss->regs->cr1);
}
static int sh_spi_send(struct sh_spi *ss, const unsigned char *tx_data,
unsigned int len, unsigned long flags)
{
int i, cur_len, ret = 0;
int remain = (int)len;
if (len >= SH_SPI_FIFO_SIZE)
sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1);
while (remain > 0) {
cur_len = (remain < SH_SPI_FIFO_SIZE) ?
remain : SH_SPI_FIFO_SIZE;
for (i = 0; i < cur_len &&
!(sh_spi_read(&ss->regs->cr4) & SH_SPI_WPABRT) &&
!(sh_spi_read(&ss->regs->cr1) & SH_SPI_TBF);
i++)
sh_spi_write(tx_data[i], &ss->regs->tbr_rbr);
cur_len = i;
if (sh_spi_read(&ss->regs->cr4) & SH_SPI_WPABRT) {
/* Abort the transaction */
flags |= SPI_XFER_END;
sh_spi_set_bit(SH_SPI_WPABRT, &ss->regs->cr4);
ret = 1;
break;
}
remain -= cur_len;
tx_data += cur_len;
if (remain > 0)
write_fifo_empty_wait(ss);
}
if (flags & SPI_XFER_END) {
sh_spi_clear_bit(SH_SPI_SSD | SH_SPI_SSDB, &ss->regs->cr1);
sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1);
udelay(100);
write_fifo_empty_wait(ss);
}
return ret;
}
static int sh_spi_receive(struct sh_spi *ss, unsigned char *rx_data,
unsigned int len, unsigned long flags)
{
int i;
if (len > SH_SPI_MAX_BYTE)
sh_spi_write(SH_SPI_MAX_BYTE, &ss->regs->cr3);
else
sh_spi_write(len, &ss->regs->cr3);
sh_spi_clear_bit(SH_SPI_SSD | SH_SPI_SSDB, &ss->regs->cr1);
sh_spi_set_bit(SH_SPI_SSA, &ss->regs->cr1);
for (i = 0; i < len; i++) {
if (recvbuf_wait(ss))
return 0;
rx_data[i] = (unsigned char)sh_spi_read(&ss->regs->tbr_rbr);
}
sh_spi_write(0, &ss->regs->cr3);
return 0;
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct sh_spi *ss = to_sh_spi(slave);
const unsigned char *tx_data = dout;
unsigned char *rx_data = din;
unsigned int len = bitlen / 8;
int ret = 0;
if (flags & SPI_XFER_BEGIN)
sh_spi_write(sh_spi_read(&ss->regs->cr1) & ~SH_SPI_SSA,
&ss->regs->cr1);
if (tx_data)
ret = sh_spi_send(ss, tx_data, len, flags);
if (ret == 0 && rx_data)
ret = sh_spi_receive(ss, rx_data, len, flags);
if (flags & SPI_XFER_END) {
sh_spi_set_bit(SH_SPI_SSD, &ss->regs->cr1);
udelay(100);
sh_spi_clear_bit(SH_SPI_SSA | SH_SPI_SSDB | SH_SPI_SSD,
&ss->regs->cr1);
clear_fifo(ss);
}
return ret;
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
if (!bus && cs < SH_SPI_NUM_CS)
return 1;
else
return 0;
}
void spi_cs_activate(struct spi_slave *slave)
{
}
void spi_cs_deactivate(struct spi_slave *slave)
{
}

View File

@@ -0,0 +1,68 @@
/*
* SH SPI driver
*
* Copyright (C) 2011 Renesas Solutions Corp.
*
* SPDX-License-Identifier: GPL-2.0
*/
#ifndef __SH_SPI_H__
#define __SH_SPI_H__
#include <spi.h>
struct sh_spi_regs {
unsigned long tbr_rbr;
unsigned long resv1;
unsigned long cr1;
unsigned long resv2;
unsigned long cr2;
unsigned long resv3;
unsigned long cr3;
unsigned long resv4;
unsigned long cr4;
};
/* CR1 */
#define SH_SPI_TBE 0x80
#define SH_SPI_TBF 0x40
#define SH_SPI_RBE 0x20
#define SH_SPI_RBF 0x10
#define SH_SPI_PFONRD 0x08
#define SH_SPI_SSDB 0x04
#define SH_SPI_SSD 0x02
#define SH_SPI_SSA 0x01
/* CR2 */
#define SH_SPI_RSTF 0x80
#define SH_SPI_LOOPBK 0x40
#define SH_SPI_CPOL 0x20
#define SH_SPI_CPHA 0x10
#define SH_SPI_L1M0 0x08
/* CR3 */
#define SH_SPI_MAX_BYTE 0xFF
/* CR4 */
#define SH_SPI_TBEI 0x80
#define SH_SPI_TBFI 0x40
#define SH_SPI_RBEI 0x20
#define SH_SPI_RBFI 0x10
#define SH_SPI_SSS1 0x08
#define SH_SPI_WPABRT 0x04
#define SH_SPI_SSS0 0x01
#define SH_SPI_FIFO_SIZE 32
#define SH_SPI_NUM_CS 4
struct sh_spi {
struct spi_slave slave;
struct sh_spi_regs *regs;
};
static inline struct sh_spi *to_sh_spi(struct spi_slave *slave)
{
return container_of(slave, struct sh_spi, slave);
}
#endif

View File

@@ -0,0 +1,259 @@
/*
* Copyright (c) 2014 Google, Inc
*
* (C) Copyright 2002
* Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
*
* Influenced by code from:
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <asm/gpio.h>
DECLARE_GLOBAL_DATA_PTR;
struct soft_spi_platdata {
struct gpio_desc cs;
struct gpio_desc sclk;
struct gpio_desc mosi;
struct gpio_desc miso;
int spi_delay_us;
int flags;
};
#define SPI_MASTER_NO_RX BIT(0)
#define SPI_MASTER_NO_TX BIT(1)
struct soft_spi_priv {
unsigned int mode;
};
static int soft_spi_scl(struct udevice *dev, int bit)
{
struct udevice *bus = dev_get_parent(dev);
struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->sclk, bit);
return 0;
}
static int soft_spi_sda(struct udevice *dev, int bit)
{
struct udevice *bus = dev_get_parent(dev);
struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->mosi, bit);
return 0;
}
static int soft_spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->cs, 0);
dm_gpio_set_value(&plat->sclk, 0);
dm_gpio_set_value(&plat->cs, 1);
return 0;
}
static int soft_spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->cs, 0);
return 0;
}
static int soft_spi_claim_bus(struct udevice *dev)
{
/*
* Make sure the SPI clock is in idle state as defined for
* this slave.
*/
return soft_spi_scl(dev, 0);
}
static int soft_spi_release_bus(struct udevice *dev)
{
/* Nothing to do */
return 0;
}
/*-----------------------------------------------------------------------
* SPI transfer
*
* This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
* "bitlen" bits in the SPI MISO port. That's just the way SPI works.
*
* The source of the outgoing bits is the "dout" parameter and the
* destination of the input bits is the "din" parameter. Note that "dout"
* and "din" can point to the same memory location, in which case the
* input data overwrites the output data (since both are buffered by
* temporary variables, this is OK).
*/
static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct soft_spi_priv *priv = dev_get_priv(bus);
struct soft_spi_platdata *plat = dev_get_platdata(bus);
uchar tmpdin = 0;
uchar tmpdout = 0;
const u8 *txd = dout;
u8 *rxd = din;
int cpha = priv->mode & SPI_CPHA;
unsigned int j;
debug("spi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
dev->parent->name, dev->name, *(uint *)txd, *(uint *)rxd,
bitlen);
if (flags & SPI_XFER_BEGIN)
soft_spi_cs_activate(dev);
for (j = 0; j < bitlen; j++) {
/*
* Check if it is time to work on a new byte.
*/
if ((j % 8) == 0) {
if (txd)
tmpdout = *txd++;
else
tmpdout = 0;
if (j != 0) {
if (rxd)
*rxd++ = tmpdin;
}
tmpdin = 0;
}
if (!cpha)
soft_spi_scl(dev, 0);
if ((plat->flags & SPI_MASTER_NO_TX) == 0)
soft_spi_sda(dev, !!(tmpdout & 0x80));
udelay(plat->spi_delay_us);
if (cpha)
soft_spi_scl(dev, 0);
else
soft_spi_scl(dev, 1);
tmpdin <<= 1;
if ((plat->flags & SPI_MASTER_NO_RX) == 0)
tmpdin |= dm_gpio_get_value(&plat->miso);
tmpdout <<= 1;
udelay(plat->spi_delay_us);
if (cpha)
soft_spi_scl(dev, 1);
}
/*
* If the number of bits isn't a multiple of 8, shift the last
* bits over to left-justify them. Then store the last byte
* read in.
*/
if (rxd) {
if ((bitlen % 8) != 0)
tmpdin <<= 8 - (bitlen % 8);
*rxd++ = tmpdin;
}
if (flags & SPI_XFER_END)
soft_spi_cs_deactivate(dev);
return 0;
}
static int soft_spi_set_speed(struct udevice *dev, unsigned int speed)
{
/* Accept any speed */
return 0;
}
static int soft_spi_set_mode(struct udevice *dev, unsigned int mode)
{
struct soft_spi_priv *priv = dev_get_priv(dev);
priv->mode = mode;
return 0;
}
static const struct dm_spi_ops soft_spi_ops = {
.claim_bus = soft_spi_claim_bus,
.release_bus = soft_spi_release_bus,
.xfer = soft_spi_xfer,
.set_speed = soft_spi_set_speed,
.set_mode = soft_spi_set_mode,
};
static int soft_spi_ofdata_to_platdata(struct udevice *dev)
{
struct soft_spi_platdata *plat = dev->platdata;
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
plat->spi_delay_us = fdtdec_get_int(blob, node, "spi-delay-us", 0);
return 0;
}
static int soft_spi_probe(struct udevice *dev)
{
struct spi_slave *slave = dev_get_parent_priv(dev);
struct soft_spi_platdata *plat = dev->platdata;
int cs_flags, clk_flags;
int ret;
cs_flags = (slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
clk_flags = (slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
GPIOD_IS_OUT | cs_flags) ||
gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
GPIOD_IS_OUT | clk_flags))
return -EINVAL;
ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi,
GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
if (ret)
plat->flags |= SPI_MASTER_NO_TX;
ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
GPIOD_IS_IN);
if (ret)
plat->flags |= SPI_MASTER_NO_RX;
if ((plat->flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) ==
(SPI_MASTER_NO_RX | SPI_MASTER_NO_TX))
return -EINVAL;
return 0;
}
static const struct udevice_id soft_spi_ids[] = {
{ .compatible = "spi-gpio" },
{ }
};
U_BOOT_DRIVER(soft_spi) = {
.name = "soft_spi",
.id = UCLASS_SPI,
.of_match = soft_spi_ids,
.ops = &soft_spi_ops,
.ofdata_to_platdata = soft_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct soft_spi_platdata),
.priv_auto_alloc_size = sizeof(struct soft_spi_priv),
.probe = soft_spi_probe,
};

View File

@@ -0,0 +1,176 @@
/*
* (C) Copyright 2002
* Gerald Van Baren, Custom IDEAS, vanbaren@cideas.com.
*
* Influenced by code from:
* Wolfgang Denk, DENX Software Engineering, wd@denx.de.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <spi.h>
#include <malloc.h>
/*-----------------------------------------------------------------------
* Definitions
*/
#ifdef DEBUG_SPI
#define PRINTD(fmt,args...) printf (fmt ,##args)
#else
#define PRINTD(fmt,args...)
#endif
struct soft_spi_slave {
struct spi_slave slave;
unsigned int mode;
};
static inline struct soft_spi_slave *to_soft_spi(struct spi_slave *slave)
{
return container_of(slave, struct soft_spi_slave, slave);
}
/*=====================================================================*/
/* Public Functions */
/*=====================================================================*/
/*-----------------------------------------------------------------------
* Initialization
*/
void spi_init (void)
{
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct soft_spi_slave *ss;
if (!spi_cs_is_valid(bus, cs))
return NULL;
ss = spi_alloc_slave(struct soft_spi_slave, bus, cs);
if (!ss)
return NULL;
ss->mode = mode;
/* TODO: Use max_hz to limit the SCK rate */
return &ss->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct soft_spi_slave *ss = to_soft_spi(slave);
free(ss);
}
int spi_claim_bus(struct spi_slave *slave)
{
#ifdef CONFIG_SYS_IMMR
volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
#endif
struct soft_spi_slave *ss = to_soft_spi(slave);
/*
* Make sure the SPI clock is in idle state as defined for
* this slave.
*/
if (ss->mode & SPI_CPOL)
SPI_SCL(1);
else
SPI_SCL(0);
return 0;
}
void spi_release_bus(struct spi_slave *slave)
{
/* Nothing to do */
}
/*-----------------------------------------------------------------------
* SPI transfer
*
* This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks
* "bitlen" bits in the SPI MISO port. That's just the way SPI works.
*
* The source of the outgoing bits is the "dout" parameter and the
* destination of the input bits is the "din" parameter. Note that "dout"
* and "din" can point to the same memory location, in which case the
* input data overwrites the output data (since both are buffered by
* temporary variables, this is OK).
*/
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
#ifdef CONFIG_SYS_IMMR
volatile immap_t *immr = (immap_t *)CONFIG_SYS_IMMR;
#endif
struct soft_spi_slave *ss = to_soft_spi(slave);
uchar tmpdin = 0;
uchar tmpdout = 0;
const u8 *txd = dout;
u8 *rxd = din;
int cpol = ss->mode & SPI_CPOL;
int cpha = ss->mode & SPI_CPHA;
unsigned int j;
PRINTD("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n",
slave->bus, slave->cs, *(uint *)txd, *(uint *)rxd, bitlen);
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(slave);
for(j = 0; j < bitlen; j++) {
/*
* Check if it is time to work on a new byte.
*/
if ((j % 8) == 0) {
if (txd)
tmpdout = *txd++;
else
tmpdout = 0;
if(j != 0) {
if (rxd)
*rxd++ = tmpdin;
}
tmpdin = 0;
}
if (!cpha)
SPI_SCL(!cpol);
SPI_SDA(tmpdout & 0x80);
SPI_DELAY;
if (cpha)
SPI_SCL(!cpol);
else
SPI_SCL(cpol);
tmpdin <<= 1;
tmpdin |= SPI_READ;
tmpdout <<= 1;
SPI_DELAY;
if (cpha)
SPI_SCL(cpol);
}
/*
* If the number of bits isn't a multiple of 8, shift the last
* bits over to left-justify them. Then store the last byte
* read in.
*/
if (rxd) {
if ((bitlen % 8) != 0)
tmpdin <<= 8 - (bitlen % 8);
*rxd++ = tmpdin;
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(slave);
return(0);
}

View File

@@ -0,0 +1,15 @@
/*
* Copyright (c) 2014 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <spi.h>
#include <spi_flash.h>
UCLASS_DRIVER(spi_emul) = {
.id = UCLASS_SPI_EMUL,
.name = "spi_emul",
};

View File

@@ -0,0 +1,463 @@
/*
* Copyright (c) 2014 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
#include <dm/device-internal.h>
#include <dm/uclass-internal.h>
#include <dm/root.h>
#include <dm/lists.h>
#include <dm/util.h>
DECLARE_GLOBAL_DATA_PTR;
static int spi_set_speed_mode(struct udevice *bus, int speed, int mode)
{
struct dm_spi_ops *ops;
int ret;
ops = spi_get_ops(bus);
if (ops->set_speed)
ret = ops->set_speed(bus, speed);
else
ret = -EINVAL;
if (ret) {
printf("Cannot set speed (err=%d)\n", ret);
return ret;
}
if (ops->set_mode)
ret = ops->set_mode(bus, mode);
else
ret = -EINVAL;
if (ret) {
printf("Cannot set mode (err=%d)\n", ret);
return ret;
}
return 0;
}
int dm_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
struct dm_spi_bus *spi = dev_get_uclass_priv(bus);
struct spi_slave *slave = dev_get_parent_priv(dev);
int speed;
int ret;
speed = slave->max_hz;
if (spi->max_hz) {
if (speed)
speed = min(speed, (int)spi->max_hz);
else
speed = spi->max_hz;
}
if (!speed)
speed = 100000;
if (speed != slave->speed) {
ret = spi_set_speed_mode(bus, speed, slave->mode);
if (ret)
return ret;
slave->speed = speed;
}
return ops->claim_bus ? ops->claim_bus(dev) : 0;
}
void dm_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
if (ops->release_bus)
ops->release_bus(dev);
}
int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
if (bus->uclass->uc_drv->id != UCLASS_SPI)
return -EOPNOTSUPP;
return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags);
}
int spi_claim_bus(struct spi_slave *slave)
{
return dm_spi_claim_bus(slave->dev);
}
void spi_release_bus(struct spi_slave *slave)
{
dm_spi_release_bus(slave->dev);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
return dm_spi_xfer(slave->dev, bitlen, dout, din, flags);
}
static int spi_post_bind(struct udevice *dev)
{
/* Scan the bus for devices */
return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
}
static int spi_child_post_bind(struct udevice *dev)
{
struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev);
if (dev->of_offset == -1)
return 0;
return spi_slave_ofdata_to_platdata(gd->fdt_blob, dev->of_offset, plat);
}
static int spi_post_probe(struct udevice *bus)
{
struct dm_spi_bus *spi = dev_get_uclass_priv(bus);
spi->max_hz = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
"spi-max-frequency", 0);
#if defined(CONFIG_NEEDS_MANUAL_RELOC)
struct dm_spi_ops *ops = spi_get_ops(bus);
if (ops->claim_bus)
ops->claim_bus += gd->reloc_off;
if (ops->release_bus)
ops->release_bus += gd->reloc_off;
if (ops->set_wordlen)
ops->set_wordlen += gd->reloc_off;
if (ops->xfer)
ops->xfer += gd->reloc_off;
if (ops->set_speed)
ops->set_speed += gd->reloc_off;
if (ops->set_mode)
ops->set_mode += gd->reloc_off;
if (ops->cs_info)
ops->cs_info += gd->reloc_off;
#endif
return 0;
}
static int spi_child_pre_probe(struct udevice *dev)
{
struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev);
struct spi_slave *slave = dev_get_parent_priv(dev);
/*
* This is needed because we pass struct spi_slave around the place
* instead slave->dev (a struct udevice). So we have to have some
* way to access the slave udevice given struct spi_slave. Once we
* change the SPI API to use udevice instead of spi_slave, we can
* drop this.
*/
slave->dev = dev;
slave->max_hz = plat->max_hz;
slave->mode = plat->mode;
slave->mode_rx = plat->mode_rx;
slave->wordlen = SPI_DEFAULT_WORDLEN;
return 0;
}
int spi_chip_select(struct udevice *dev)
{
struct dm_spi_slave_platdata *plat = dev_get_parent_platdata(dev);
return plat ? plat->cs : -ENOENT;
}
int spi_find_chip_select(struct udevice *bus, int cs, struct udevice **devp)
{
struct udevice *dev;
for (device_find_first_child(bus, &dev); dev;
device_find_next_child(&dev)) {
struct dm_spi_slave_platdata *plat;
plat = dev_get_parent_platdata(dev);
debug("%s: plat=%p, cs=%d\n", __func__, plat, plat->cs);
if (plat->cs == cs) {
*devp = dev;
return 0;
}
}
return -ENODEV;
}
int spi_cs_is_valid(unsigned int busnum, unsigned int cs)
{
struct spi_cs_info info;
struct udevice *bus;
int ret;
ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, false, &bus);
if (ret) {
debug("%s: No bus %d\n", __func__, busnum);
return ret;
}
return spi_cs_info(bus, cs, &info);
}
int spi_cs_info(struct udevice *bus, uint cs, struct spi_cs_info *info)
{
struct spi_cs_info local_info;
struct dm_spi_ops *ops;
int ret;
if (!info)
info = &local_info;
/* If there is a device attached, return it */
info->dev = NULL;
ret = spi_find_chip_select(bus, cs, &info->dev);
if (!ret)
return 0;
/*
* Otherwise ask the driver. For the moment we don't have CS info.
* When we do we could provide the driver with a helper function
* to figure out what chip selects are valid, or just handle the
* request.
*/
ops = spi_get_ops(bus);
if (ops->cs_info)
return ops->cs_info(bus, cs, info);
/*
* We could assume there is at least one valid chip select, but best
* to be sure and return an error in this case. The driver didn't
* care enough to tell us.
*/
return -ENODEV;
}
int spi_find_bus_and_cs(int busnum, int cs, struct udevice **busp,
struct udevice **devp)
{
struct udevice *bus, *dev;
int ret;
ret = uclass_find_device_by_seq(UCLASS_SPI, busnum, false, &bus);
if (ret) {
debug("%s: No bus %d\n", __func__, busnum);
return ret;
}
ret = spi_find_chip_select(bus, cs, &dev);
if (ret) {
debug("%s: No cs %d\n", __func__, cs);
return ret;
}
*busp = bus;
*devp = dev;
return ret;
}
int spi_get_bus_and_cs(int busnum, int cs, int speed, int mode,
const char *drv_name, const char *dev_name,
struct udevice **busp, struct spi_slave **devp)
{
struct udevice *bus, *dev;
bool created = false;
int ret;
ret = uclass_get_device_by_seq(UCLASS_SPI, busnum, &bus);
if (ret) {
printf("Invalid bus %d (err=%d)\n", busnum, ret);
return ret;
}
ret = spi_find_chip_select(bus, cs, &dev);
/*
* If there is no such device, create one automatically. This means
* that we don't need a device tree node or platform data for the
* SPI flash chip - we will bind to the correct driver.
*/
if (ret == -ENODEV && drv_name) {
struct dm_spi_slave_platdata *plat;
debug("%s: Binding new device '%s', busnum=%d, cs=%d, driver=%s\n",
__func__, dev_name, busnum, cs, drv_name);
ret = device_bind_driver(bus, drv_name, dev_name, &dev);
if (ret)
return ret;
plat = dev_get_parent_platdata(dev);
plat->cs = cs;
plat->max_hz = speed;
plat->mode = mode;
created = true;
} else if (ret) {
printf("Invalid chip select %d:%d (err=%d)\n", busnum, cs,
ret);
return ret;
}
if (!device_active(dev)) {
struct spi_slave *slave;
ret = device_probe(dev);
if (ret)
goto err;
slave = dev_get_parent_priv(dev);
slave->dev = dev;
}
ret = spi_set_speed_mode(bus, speed, mode);
if (ret)
goto err;
*busp = bus;
*devp = dev_get_parent_priv(dev);
debug("%s: bus=%p, slave=%p\n", __func__, bus, *devp);
return 0;
err:
debug("%s: Error path, credted=%d, device '%s'\n", __func__,
created, dev->name);
if (created) {
device_remove(dev);
device_unbind(dev);
}
return ret;
}
/* Compatibility function - to be removed */
struct spi_slave *spi_setup_slave_fdt(const void *blob, int node,
int bus_node)
{
struct udevice *bus, *dev;
int ret;
ret = uclass_get_device_by_of_offset(UCLASS_SPI, bus_node, &bus);
if (ret)
return NULL;
ret = device_get_child_by_of_offset(bus, node, &dev);
if (ret)
return NULL;
return dev_get_parent_priv(dev);
}
/* Compatibility function - to be removed */
struct spi_slave *spi_setup_slave(unsigned int busnum, unsigned int cs,
unsigned int speed, unsigned int mode)
{
struct spi_slave *slave;
struct udevice *dev;
int ret;
ret = spi_get_bus_and_cs(busnum, cs, speed, mode, NULL, 0, &dev,
&slave);
if (ret)
return NULL;
return slave;
}
void spi_free_slave(struct spi_slave *slave)
{
device_remove(slave->dev);
slave->dev = NULL;
}
int spi_slave_ofdata_to_platdata(const void *blob, int node,
struct dm_spi_slave_platdata *plat)
{
int mode = 0, mode_rx = 0;
int value;
plat->cs = fdtdec_get_int(blob, node, "reg", -1);
plat->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 0);
if (fdtdec_get_bool(blob, node, "spi-cpol"))
mode |= SPI_CPOL;
if (fdtdec_get_bool(blob, node, "spi-cpha"))
mode |= SPI_CPHA;
if (fdtdec_get_bool(blob, node, "spi-cs-high"))
mode |= SPI_CS_HIGH;
if (fdtdec_get_bool(blob, node, "spi-3wire"))
mode |= SPI_3WIRE;
if (fdtdec_get_bool(blob, node, "spi-half-duplex"))
mode |= SPI_PREAMBLE;
/* Device DUAL/QUAD mode */
value = fdtdec_get_uint(blob, node, "spi-tx-bus-width", 1);
switch (value) {
case 1:
break;
case 2:
mode |= SPI_TX_DUAL;
break;
case 4:
mode |= SPI_TX_QUAD;
break;
default:
error("spi-tx-bus-width %d not supported\n", value);
break;
}
plat->mode = mode;
value = fdtdec_get_uint(blob, node, "spi-rx-bus-width", 1);
switch (value) {
case 1:
break;
case 2:
mode_rx |= SPI_RX_DUAL;
break;
case 4:
mode_rx |= SPI_RX_QUAD;
break;
default:
error("spi-rx-bus-width %d not supported\n", value);
break;
}
plat->mode_rx = mode_rx;
return 0;
}
UCLASS_DRIVER(spi) = {
.id = UCLASS_SPI,
.name = "spi",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_bind = spi_post_bind,
.post_probe = spi_post_probe,
.child_pre_probe = spi_child_pre_probe,
.per_device_auto_alloc_size = sizeof(struct dm_spi_bus),
.per_child_auto_alloc_size = sizeof(struct spi_slave),
.per_child_platdata_auto_alloc_size =
sizeof(struct dm_spi_slave_platdata),
.child_post_bind = spi_child_post_bind,
};
UCLASS_DRIVER(spi_generic) = {
.id = UCLASS_SPI_GENERIC,
.name = "spi_generic",
};
U_BOOT_DRIVER(spi_generic_drv) = {
.name = "spi_generic_drv",
.id = UCLASS_SPI_GENERIC,
};

60
u-boot/drivers/spi/spi.c Normal file
View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2011 The Chromium OS Authors.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <fdtdec.h>
#include <malloc.h>
#include <spi.h>
int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen)
{
if (wordlen == 0 || wordlen > 32) {
printf("spi: invalid wordlen %d\n", wordlen);
return -1;
}
slave->wordlen = wordlen;
return 0;
}
void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
unsigned int cs)
{
struct spi_slave *slave;
void *ptr;
ptr = malloc(size);
if (ptr) {
memset(ptr, '\0', size);
slave = (struct spi_slave *)(ptr + offset);
slave->bus = bus;
slave->cs = cs;
slave->wordlen = SPI_DEFAULT_WORDLEN;
}
return ptr;
}
#ifdef CONFIG_OF_SPI
struct spi_slave *spi_base_setup_slave_fdt(const void *blob, int busnum,
int node)
{
int cs, max_hz, mode = 0;
cs = fdtdec_get_int(blob, node, "reg", -1);
max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", 100000);
if (fdtdec_get_bool(blob, node, "spi-cpol"))
mode |= SPI_CPOL;
if (fdtdec_get_bool(blob, node, "spi-cpha"))
mode |= SPI_CPHA;
if (fdtdec_get_bool(blob, node, "spi-cs-high"))
mode |= SPI_CS_HIGH;
if (fdtdec_get_bool(blob, node, "spi-half-duplex"))
mode |= SPI_PREAMBLE;
return spi_setup_slave(busnum, cs, max_hz, mode);
}
#endif

View File

@@ -0,0 +1,401 @@
/*
* NVIDIA Tegra SPI controller (T114 and later)
*
* Copyright (c) 2010-2013 NVIDIA Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch-tegra/clk_rst.h>
#include <spi.h>
#include <fdtdec.h>
#include "tegra_spi.h"
DECLARE_GLOBAL_DATA_PTR;
/* COMMAND1 */
#define SPI_CMD1_GO BIT(31)
#define SPI_CMD1_M_S BIT(30)
#define SPI_CMD1_MODE_MASK GENMASK(1, 0)
#define SPI_CMD1_MODE_SHIFT 28
#define SPI_CMD1_CS_SEL_MASK GENMASK(1, 0)
#define SPI_CMD1_CS_SEL_SHIFT 26
#define SPI_CMD1_CS_POL_INACTIVE3 BIT(25)
#define SPI_CMD1_CS_POL_INACTIVE2 BIT(24)
#define SPI_CMD1_CS_POL_INACTIVE1 BIT(23)
#define SPI_CMD1_CS_POL_INACTIVE0 BIT(22)
#define SPI_CMD1_CS_SW_HW BIT(21)
#define SPI_CMD1_CS_SW_VAL BIT(20)
#define SPI_CMD1_IDLE_SDA_MASK GENMASK(1, 0)
#define SPI_CMD1_IDLE_SDA_SHIFT 18
#define SPI_CMD1_BIDIR BIT(17)
#define SPI_CMD1_LSBI_FE BIT(16)
#define SPI_CMD1_LSBY_FE BIT(15)
#define SPI_CMD1_BOTH_EN_BIT BIT(14)
#define SPI_CMD1_BOTH_EN_BYTE BIT(13)
#define SPI_CMD1_RX_EN BIT(12)
#define SPI_CMD1_TX_EN BIT(11)
#define SPI_CMD1_PACKED BIT(5)
#define SPI_CMD1_BIT_LEN_MASK GENMASK(4, 0)
#define SPI_CMD1_BIT_LEN_SHIFT 0
/* COMMAND2 */
#define SPI_CMD2_TX_CLK_TAP_DELAY BIT(6)
#define SPI_CMD2_TX_CLK_TAP_DELAY_MASK GENMASK(11, 6)
#define SPI_CMD2_RX_CLK_TAP_DELAY BIT(0)
#define SPI_CMD2_RX_CLK_TAP_DELAY_MASK GENMASK(5, 0)
/* TRANSFER STATUS */
#define SPI_XFER_STS_RDY BIT(30)
/* FIFO STATUS */
#define SPI_FIFO_STS_CS_INACTIVE BIT(31)
#define SPI_FIFO_STS_FRAME_END BIT(30)
#define SPI_FIFO_STS_RX_FIFO_FLUSH BIT(15)
#define SPI_FIFO_STS_TX_FIFO_FLUSH BIT(14)
#define SPI_FIFO_STS_ERR BIT(8)
#define SPI_FIFO_STS_TX_FIFO_OVF BIT(7)
#define SPI_FIFO_STS_TX_FIFO_UNR BIT(6)
#define SPI_FIFO_STS_RX_FIFO_OVF BIT(5)
#define SPI_FIFO_STS_RX_FIFO_UNR BIT(4)
#define SPI_FIFO_STS_TX_FIFO_FULL BIT(3)
#define SPI_FIFO_STS_TX_FIFO_EMPTY BIT(2)
#define SPI_FIFO_STS_RX_FIFO_FULL BIT(1)
#define SPI_FIFO_STS_RX_FIFO_EMPTY BIT(0)
#define SPI_TIMEOUT 1000
#define TEGRA_SPI_MAX_FREQ 52000000
struct spi_regs {
u32 command1; /* 000:SPI_COMMAND1 register */
u32 command2; /* 004:SPI_COMMAND2 register */
u32 timing1; /* 008:SPI_CS_TIM1 register */
u32 timing2; /* 00c:SPI_CS_TIM2 register */
u32 xfer_status;/* 010:SPI_TRANS_STATUS register */
u32 fifo_status;/* 014:SPI_FIFO_STATUS register */
u32 tx_data; /* 018:SPI_TX_DATA register */
u32 rx_data; /* 01c:SPI_RX_DATA register */
u32 dma_ctl; /* 020:SPI_DMA_CTL register */
u32 dma_blk; /* 024:SPI_DMA_BLK register */
u32 rsvd[56]; /* 028-107 reserved */
u32 tx_fifo; /* 108:SPI_FIFO1 register */
u32 rsvd2[31]; /* 10c-187 reserved */
u32 rx_fifo; /* 188:SPI_FIFO2 register */
u32 spare_ctl; /* 18c:SPI_SPARE_CTRL register */
};
struct tegra114_spi_priv {
struct spi_regs *regs;
unsigned int freq;
unsigned int mode;
int periph_id;
int valid;
int last_transaction_us;
};
static int tegra114_spi_ofdata_to_platdata(struct udevice *bus)
{
struct tegra_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->base = dev_get_addr(bus);
plat->periph_id = clock_decode_periph_id(blob, node);
if (plat->periph_id == PERIPH_ID_NONE) {
debug("%s: could not decode periph id %d\n", __func__,
plat->periph_id);
return -FDT_ERR_NOTFOUND;
}
/* Use 500KHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n",
__func__, plat->base, plat->periph_id, plat->frequency,
plat->deactivate_delay_us);
return 0;
}
static int tegra114_spi_probe(struct udevice *bus)
{
struct tegra_spi_platdata *plat = dev_get_platdata(bus);
struct tegra114_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs;
ulong rate;
priv->regs = (struct spi_regs *)plat->base;
regs = priv->regs;
priv->last_transaction_us = timer_get_us();
priv->freq = plat->frequency;
priv->periph_id = plat->periph_id;
/*
* Change SPI clock to correct frequency, PLLP_OUT0 source, falling
* back to the oscillator if that is too fast.
*/
rate = clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH,
priv->freq);
if (rate > priv->freq + 100000) {
rate = clock_start_periph_pll(priv->periph_id, CLOCK_ID_OSC,
priv->freq);
if (rate != priv->freq) {
printf("Warning: SPI '%s' requested clock %u, actual clock %lu\n",
bus->name, priv->freq, rate);
}
}
/* Clear stale status here */
setbits_le32(&regs->fifo_status,
SPI_FIFO_STS_ERR |
SPI_FIFO_STS_TX_FIFO_OVF |
SPI_FIFO_STS_TX_FIFO_UNR |
SPI_FIFO_STS_RX_FIFO_OVF |
SPI_FIFO_STS_RX_FIFO_UNR |
SPI_FIFO_STS_TX_FIFO_FULL |
SPI_FIFO_STS_TX_FIFO_EMPTY |
SPI_FIFO_STS_RX_FIFO_FULL |
SPI_FIFO_STS_RX_FIFO_EMPTY);
debug("%s: FIFO STATUS = %08x\n", __func__, readl(&regs->fifo_status));
setbits_le32(&priv->regs->command1, SPI_CMD1_M_S | SPI_CMD1_CS_SW_HW |
(priv->mode << SPI_CMD1_MODE_SHIFT) | SPI_CMD1_CS_SW_VAL);
debug("%s: COMMAND1 = %08x\n", __func__, readl(&regs->command1));
return 0;
}
/**
* Activate the CS by driving it LOW
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra114_spi_priv *priv = dev_get_priv(bus);
/* If it's too soon to do another transaction, wait */
if (pdata->deactivate_delay_us &&
priv->last_transaction_us) {
ulong delay_us; /* The delay completed so far */
delay_us = timer_get_us() - priv->last_transaction_us;
if (delay_us < pdata->deactivate_delay_us)
udelay(pdata->deactivate_delay_us - delay_us);
}
clrbits_le32(&priv->regs->command1, SPI_CMD1_CS_SW_VAL);
}
/**
* Deactivate the CS by driving it HIGH
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra114_spi_priv *priv = dev_get_priv(bus);
setbits_le32(&priv->regs->command1, SPI_CMD1_CS_SW_VAL);
/* Remember time of this transaction so we can honour the bus delay */
if (pdata->deactivate_delay_us)
priv->last_transaction_us = timer_get_us();
debug("Deactivate CS, bus '%s'\n", bus->name);
}
static int tegra114_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *data_out, void *data_in,
unsigned long flags)
{
struct udevice *bus = dev->parent;
struct tegra114_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg, tmpdout, tmpdin = 0;
const u8 *dout = data_out;
u8 *din = data_in;
int num_bytes;
int ret;
debug("%s: slave %u:%u dout %p din %p bitlen %u\n",
__func__, bus->seq, spi_chip_select(dev), dout, din, bitlen);
if (bitlen % 8)
return -1;
num_bytes = bitlen / 8;
ret = 0;
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
/* clear all error status bits */
reg = readl(&regs->fifo_status);
writel(reg, &regs->fifo_status);
clrsetbits_le32(&regs->command1, SPI_CMD1_CS_SW_VAL,
SPI_CMD1_RX_EN | SPI_CMD1_TX_EN | SPI_CMD1_LSBY_FE |
(spi_chip_select(dev) << SPI_CMD1_CS_SEL_SHIFT));
/* set xfer size to 1 block (32 bits) */
writel(0, &regs->dma_blk);
/* handle data in 32-bit chunks */
while (num_bytes > 0) {
int bytes;
int tm, i;
tmpdout = 0;
bytes = (num_bytes > 4) ? 4 : num_bytes;
if (dout != NULL) {
for (i = 0; i < bytes; ++i)
tmpdout = (tmpdout << 8) | dout[i];
dout += bytes;
}
num_bytes -= bytes;
/* clear ready bit */
setbits_le32(&regs->xfer_status, SPI_XFER_STS_RDY);
clrsetbits_le32(&regs->command1,
SPI_CMD1_BIT_LEN_MASK << SPI_CMD1_BIT_LEN_SHIFT,
(bytes * 8 - 1) << SPI_CMD1_BIT_LEN_SHIFT);
writel(tmpdout, &regs->tx_fifo);
setbits_le32(&regs->command1, SPI_CMD1_GO);
/*
* Wait for SPI transmit FIFO to empty, or to time out.
* The RX FIFO status will be read and cleared last
*/
for (tm = 0; tm < SPI_TIMEOUT; ++tm) {
u32 fifo_status, xfer_status;
xfer_status = readl(&regs->xfer_status);
if (!(xfer_status & SPI_XFER_STS_RDY))
continue;
fifo_status = readl(&regs->fifo_status);
if (fifo_status & SPI_FIFO_STS_ERR) {
debug("%s: got a fifo error: ", __func__);
if (fifo_status & SPI_FIFO_STS_TX_FIFO_OVF)
debug("tx FIFO overflow ");
if (fifo_status & SPI_FIFO_STS_TX_FIFO_UNR)
debug("tx FIFO underrun ");
if (fifo_status & SPI_FIFO_STS_RX_FIFO_OVF)
debug("rx FIFO overflow ");
if (fifo_status & SPI_FIFO_STS_RX_FIFO_UNR)
debug("rx FIFO underrun ");
if (fifo_status & SPI_FIFO_STS_TX_FIFO_FULL)
debug("tx FIFO full ");
if (fifo_status & SPI_FIFO_STS_TX_FIFO_EMPTY)
debug("tx FIFO empty ");
if (fifo_status & SPI_FIFO_STS_RX_FIFO_FULL)
debug("rx FIFO full ");
if (fifo_status & SPI_FIFO_STS_RX_FIFO_EMPTY)
debug("rx FIFO empty ");
debug("\n");
break;
}
if (!(fifo_status & SPI_FIFO_STS_RX_FIFO_EMPTY)) {
tmpdin = readl(&regs->rx_fifo);
/* swap bytes read in */
if (din != NULL) {
for (i = bytes - 1; i >= 0; --i) {
din[i] = tmpdin & 0xff;
tmpdin >>= 8;
}
din += bytes;
}
/* We can exit when we've had both RX and TX */
break;
}
}
if (tm >= SPI_TIMEOUT)
ret = tm;
/* clear ACK RDY, etc. bits */
writel(readl(&regs->fifo_status), &regs->fifo_status);
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
debug("%s: transfer ended. Value=%08x, fifo_status = %08x\n",
__func__, tmpdin, readl(&regs->fifo_status));
if (ret) {
printf("%s: timeout during SPI transfer, tm %d\n",
__func__, ret);
return -1;
}
return ret;
}
static int tegra114_spi_set_speed(struct udevice *bus, uint speed)
{
struct tegra_spi_platdata *plat = bus->platdata;
struct tegra114_spi_priv *priv = dev_get_priv(bus);
if (speed > plat->frequency)
speed = plat->frequency;
priv->freq = speed;
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
return 0;
}
static int tegra114_spi_set_mode(struct udevice *bus, uint mode)
{
struct tegra114_spi_priv *priv = dev_get_priv(bus);
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops tegra114_spi_ops = {
.xfer = tegra114_spi_xfer,
.set_speed = tegra114_spi_set_speed,
.set_mode = tegra114_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id tegra114_spi_ids[] = {
{ .compatible = "nvidia,tegra114-spi" },
{ }
};
U_BOOT_DRIVER(tegra114_spi) = {
.name = "tegra114_spi",
.id = UCLASS_SPI,
.of_match = tegra114_spi_ids,
.ops = &tegra114_spi_ops,
.ofdata_to_platdata = tegra114_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata),
.priv_auto_alloc_size = sizeof(struct tegra114_spi_priv),
.probe = tegra114_spi_probe,
};

View File

@@ -0,0 +1,353 @@
/*
* Copyright (c) 2010-2013 NVIDIA Corporation
* With help from the mpc8xxx SPI driver
* With more help from omap3_spi SPI driver
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <asm/io.h>
#include <asm/gpio.h>
#include <asm/arch/clock.h>
#include <asm/arch/pinmux.h>
#include <asm/arch-tegra/clk_rst.h>
#include <spi.h>
#include <fdtdec.h>
#include "tegra_spi.h"
DECLARE_GLOBAL_DATA_PTR;
#define SPI_CMD_GO BIT(30)
#define SPI_CMD_ACTIVE_SCLK_SHIFT 26
#define SPI_CMD_ACTIVE_SCLK_MASK (3 << SPI_CMD_ACTIVE_SCLK_SHIFT)
#define SPI_CMD_CK_SDA BIT(21)
#define SPI_CMD_ACTIVE_SDA_SHIFT 18
#define SPI_CMD_ACTIVE_SDA_MASK (3 << SPI_CMD_ACTIVE_SDA_SHIFT)
#define SPI_CMD_CS_POL BIT(16)
#define SPI_CMD_TXEN BIT(15)
#define SPI_CMD_RXEN BIT(14)
#define SPI_CMD_CS_VAL BIT(13)
#define SPI_CMD_CS_SOFT BIT(12)
#define SPI_CMD_CS_DELAY BIT(9)
#define SPI_CMD_CS3_EN BIT(8)
#define SPI_CMD_CS2_EN BIT(7)
#define SPI_CMD_CS1_EN BIT(6)
#define SPI_CMD_CS0_EN BIT(5)
#define SPI_CMD_BIT_LENGTH BIT(4)
#define SPI_CMD_BIT_LENGTH_MASK GENMASK(4, 0)
#define SPI_STAT_BSY BIT(31)
#define SPI_STAT_RDY BIT(30)
#define SPI_STAT_RXF_FLUSH BIT(29)
#define SPI_STAT_TXF_FLUSH BIT(28)
#define SPI_STAT_RXF_UNR BIT(27)
#define SPI_STAT_TXF_OVF BIT(26)
#define SPI_STAT_RXF_EMPTY BIT(25)
#define SPI_STAT_RXF_FULL BIT(24)
#define SPI_STAT_TXF_EMPTY BIT(23)
#define SPI_STAT_TXF_FULL BIT(22)
#define SPI_STAT_SEL_TXRX_N BIT(16)
#define SPI_STAT_CUR_BLKCNT BIT(15)
#define SPI_TIMEOUT 1000
#define TEGRA_SPI_MAX_FREQ 52000000
struct spi_regs {
u32 command; /* SPI_COMMAND_0 register */
u32 status; /* SPI_STATUS_0 register */
u32 rx_cmp; /* SPI_RX_CMP_0 register */
u32 dma_ctl; /* SPI_DMA_CTL_0 register */
u32 tx_fifo; /* SPI_TX_FIFO_0 register */
u32 rsvd[3]; /* offsets 0x14 to 0x1F reserved */
u32 rx_fifo; /* SPI_RX_FIFO_0 register */
};
struct tegra20_sflash_priv {
struct spi_regs *regs;
unsigned int freq;
unsigned int mode;
int periph_id;
int valid;
int last_transaction_us;
};
int tegra20_sflash_cs_info(struct udevice *bus, unsigned int cs,
struct spi_cs_info *info)
{
/* Tegra20 SPI-Flash - only 1 device ('bus/cs') */
if (cs != 0)
return -ENODEV;
else
return 0;
}
static int tegra20_sflash_ofdata_to_platdata(struct udevice *bus)
{
struct tegra_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->base = dev_get_addr(bus);
plat->periph_id = clock_decode_periph_id(blob, node);
if (plat->periph_id == PERIPH_ID_NONE) {
debug("%s: could not decode periph id %d\n", __func__,
plat->periph_id);
return -FDT_ERR_NOTFOUND;
}
/* Use 500KHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n",
__func__, plat->base, plat->periph_id, plat->frequency,
plat->deactivate_delay_us);
return 0;
}
static int tegra20_sflash_probe(struct udevice *bus)
{
struct tegra_spi_platdata *plat = dev_get_platdata(bus);
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
priv->regs = (struct spi_regs *)plat->base;
priv->last_transaction_us = timer_get_us();
priv->freq = plat->frequency;
priv->periph_id = plat->periph_id;
return 0;
}
static int tegra20_sflash_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg;
/* Change SPI clock to correct frequency, PLLP_OUT0 source */
clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH,
priv->freq);
/* Clear stale status here */
reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \
SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF;
writel(reg, &regs->status);
debug("%s: STATUS = %08x\n", __func__, readl(&regs->status));
/*
* Use sw-controlled CS, so we can clock in data after ReadID, etc.
*/
reg = (priv->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT;
if (priv->mode & 2)
reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT;
clrsetbits_le32(&regs->command, SPI_CMD_ACTIVE_SCLK_MASK |
SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg);
debug("%s: COMMAND = %08x\n", __func__, readl(&regs->command));
/*
* SPI pins on Tegra20 are muxed - change pinmux later due to UART
* issue.
*/
pinmux_set_func(PMUX_PINGRP_GMD, PMUX_FUNC_SFLASH);
pinmux_tristate_disable(PMUX_PINGRP_LSPI);
pinmux_set_func(PMUX_PINGRP_GMC, PMUX_FUNC_SFLASH);
return 0;
}
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
/* If it's too soon to do another transaction, wait */
if (pdata->deactivate_delay_us &&
priv->last_transaction_us) {
ulong delay_us; /* The delay completed so far */
delay_us = timer_get_us() - priv->last_transaction_us;
if (delay_us < pdata->deactivate_delay_us)
udelay(pdata->deactivate_delay_us - delay_us);
}
/* CS is negated on Tegra, so drive a 1 to get a 0 */
setbits_le32(&priv->regs->command, SPI_CMD_CS_VAL);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
/* CS is negated on Tegra, so drive a 0 to get a 1 */
clrbits_le32(&priv->regs->command, SPI_CMD_CS_VAL);
/* Remember time of this transaction so we can honour the bus delay */
if (pdata->deactivate_delay_us)
priv->last_transaction_us = timer_get_us();
}
static int tegra20_sflash_xfer(struct udevice *dev, unsigned int bitlen,
const void *data_out, void *data_in,
unsigned long flags)
{
struct udevice *bus = dev->parent;
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg, tmpdout, tmpdin = 0;
const u8 *dout = data_out;
u8 *din = data_in;
int num_bytes;
int ret;
debug("%s: slave %u:%u dout %p din %p bitlen %u\n",
__func__, bus->seq, spi_chip_select(dev), dout, din, bitlen);
if (bitlen % 8)
return -1;
num_bytes = bitlen / 8;
ret = 0;
reg = readl(&regs->status);
writel(reg, &regs->status); /* Clear all SPI events via R/W */
debug("spi_xfer entry: STATUS = %08x\n", reg);
reg = readl(&regs->command);
reg |= SPI_CMD_TXEN | SPI_CMD_RXEN;
writel(reg, &regs->command);
debug("spi_xfer: COMMAND = %08x\n", readl(&regs->command));
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
/* handle data in 32-bit chunks */
while (num_bytes > 0) {
int bytes;
int is_read = 0;
int tm, i;
tmpdout = 0;
bytes = (num_bytes > 4) ? 4 : num_bytes;
if (dout != NULL) {
for (i = 0; i < bytes; ++i)
tmpdout = (tmpdout << 8) | dout[i];
}
num_bytes -= bytes;
if (dout)
dout += bytes;
clrsetbits_le32(&regs->command, SPI_CMD_BIT_LENGTH_MASK,
bytes * 8 - 1);
writel(tmpdout, &regs->tx_fifo);
setbits_le32(&regs->command, SPI_CMD_GO);
/*
* Wait for SPI transmit FIFO to empty, or to time out.
* The RX FIFO status will be read and cleared last
*/
for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) {
u32 status;
status = readl(&regs->status);
/* We can exit when we've had both RX and TX activity */
if (is_read && (status & SPI_STAT_TXF_EMPTY))
break;
if ((status & (SPI_STAT_BSY | SPI_STAT_RDY)) !=
SPI_STAT_RDY)
tm++;
else if (!(status & SPI_STAT_RXF_EMPTY)) {
tmpdin = readl(&regs->rx_fifo);
is_read = 1;
/* swap bytes read in */
if (din != NULL) {
for (i = bytes - 1; i >= 0; --i) {
din[i] = tmpdin & 0xff;
tmpdin >>= 8;
}
din += bytes;
}
}
}
if (tm >= SPI_TIMEOUT)
ret = tm;
/* clear ACK RDY, etc. bits */
writel(readl(&regs->status), &regs->status);
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n",
tmpdin, readl(&regs->status));
if (ret) {
printf("spi_xfer: timeout during SPI transfer, tm %d\n", ret);
return -1;
}
return 0;
}
static int tegra20_sflash_set_speed(struct udevice *bus, uint speed)
{
struct tegra_spi_platdata *plat = bus->platdata;
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
if (speed > plat->frequency)
speed = plat->frequency;
priv->freq = speed;
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
return 0;
}
static int tegra20_sflash_set_mode(struct udevice *bus, uint mode)
{
struct tegra20_sflash_priv *priv = dev_get_priv(bus);
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops tegra20_sflash_ops = {
.claim_bus = tegra20_sflash_claim_bus,
.xfer = tegra20_sflash_xfer,
.set_speed = tegra20_sflash_set_speed,
.set_mode = tegra20_sflash_set_mode,
.cs_info = tegra20_sflash_cs_info,
};
static const struct udevice_id tegra20_sflash_ids[] = {
{ .compatible = "nvidia,tegra20-sflash" },
{ }
};
U_BOOT_DRIVER(tegra20_sflash) = {
.name = "tegra20_sflash",
.id = UCLASS_SPI,
.of_match = tegra20_sflash_ids,
.ops = &tegra20_sflash_ops,
.ofdata_to_platdata = tegra20_sflash_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata),
.priv_auto_alloc_size = sizeof(struct tegra20_sflash_priv),
.probe = tegra20_sflash_probe,
};

View File

@@ -0,0 +1,372 @@
/*
* NVIDIA Tegra SPI-SLINK controller
*
* Copyright (c) 2010-2013 NVIDIA Corporation
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch-tegra/clk_rst.h>
#include <spi.h>
#include <fdtdec.h>
#include "tegra_spi.h"
DECLARE_GLOBAL_DATA_PTR;
/* COMMAND */
#define SLINK_CMD_ENB BIT(31)
#define SLINK_CMD_GO BIT(30)
#define SLINK_CMD_M_S BIT(28)
#define SLINK_CMD_IDLE_SCLK_DRIVE_LOW (0 << 24)
#define SLINK_CMD_IDLE_SCLK_DRIVE_HIGH BIT(24)
#define SLINK_CMD_IDLE_SCLK_PULL_LOW (2 << 24)
#define SLINK_CMD_IDLE_SCLK_PULL_HIGH (3 << 24)
#define SLINK_CMD_IDLE_SCLK_MASK (3 << 24)
#define SLINK_CMD_CK_SDA BIT(21)
#define SLINK_CMD_CS_POL BIT(13)
#define SLINK_CMD_CS_VAL BIT(12)
#define SLINK_CMD_CS_SOFT BIT(11)
#define SLINK_CMD_BIT_LENGTH BIT(4)
#define SLINK_CMD_BIT_LENGTH_MASK GENMASK(4, 0)
/* COMMAND2 */
#define SLINK_CMD2_TXEN BIT(30)
#define SLINK_CMD2_RXEN BIT(31)
#define SLINK_CMD2_SS_EN BIT(18)
#define SLINK_CMD2_SS_EN_SHIFT 18
#define SLINK_CMD2_SS_EN_MASK GENMASK(19, 18)
#define SLINK_CMD2_CS_ACTIVE_BETWEEN BIT(17)
/* STATUS */
#define SLINK_STAT_BSY BIT(31)
#define SLINK_STAT_RDY BIT(30)
#define SLINK_STAT_ERR BIT(29)
#define SLINK_STAT_RXF_FLUSH BIT(27)
#define SLINK_STAT_TXF_FLUSH BIT(26)
#define SLINK_STAT_RXF_OVF BIT(25)
#define SLINK_STAT_TXF_UNR BIT(24)
#define SLINK_STAT_RXF_EMPTY BIT(23)
#define SLINK_STAT_RXF_FULL BIT(22)
#define SLINK_STAT_TXF_EMPTY BIT(21)
#define SLINK_STAT_TXF_FULL BIT(20)
#define SLINK_STAT_TXF_OVF BIT(19)
#define SLINK_STAT_RXF_UNR BIT(18)
#define SLINK_STAT_CUR_BLKCNT BIT(15)
/* STATUS2 */
#define SLINK_STAT2_RXF_FULL_CNT BIT(16)
#define SLINK_STAT2_TXF_FULL_CNT BIT(0)
#define SPI_TIMEOUT 1000
#define TEGRA_SPI_MAX_FREQ 52000000
struct spi_regs {
u32 command; /* SLINK_COMMAND_0 register */
u32 command2; /* SLINK_COMMAND2_0 reg */
u32 status; /* SLINK_STATUS_0 register */
u32 reserved; /* Reserved offset 0C */
u32 mas_data; /* SLINK_MAS_DATA_0 reg */
u32 slav_data; /* SLINK_SLAVE_DATA_0 reg */
u32 dma_ctl; /* SLINK_DMA_CTL_0 register */
u32 status2; /* SLINK_STATUS2_0 reg */
u32 rsvd[56]; /* 0x20 to 0xFF reserved */
u32 tx_fifo; /* SLINK_TX_FIFO_0 reg off 100h */
u32 rsvd2[31]; /* 0x104 to 0x17F reserved */
u32 rx_fifo; /* SLINK_RX_FIFO_0 reg off 180h */
};
struct tegra30_spi_priv {
struct spi_regs *regs;
unsigned int freq;
unsigned int mode;
int periph_id;
int valid;
int last_transaction_us;
};
struct tegra_spi_slave {
struct spi_slave slave;
struct tegra30_spi_priv *ctrl;
};
static int tegra30_spi_ofdata_to_platdata(struct udevice *bus)
{
struct tegra_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->base = dev_get_addr(bus);
plat->periph_id = clock_decode_periph_id(blob, node);
if (plat->periph_id == PERIPH_ID_NONE) {
debug("%s: could not decode periph id %d\n", __func__,
plat->periph_id);
return -FDT_ERR_NOTFOUND;
}
/* Use 500KHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n",
__func__, plat->base, plat->periph_id, plat->frequency,
plat->deactivate_delay_us);
return 0;
}
static int tegra30_spi_probe(struct udevice *bus)
{
struct tegra_spi_platdata *plat = dev_get_platdata(bus);
struct tegra30_spi_priv *priv = dev_get_priv(bus);
priv->regs = (struct spi_regs *)plat->base;
priv->last_transaction_us = timer_get_us();
priv->freq = plat->frequency;
priv->periph_id = plat->periph_id;
return 0;
}
static int tegra30_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra30_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg;
/* Change SPI clock to correct frequency, PLLP_OUT0 source */
clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH,
priv->freq);
/* Clear stale status here */
reg = SLINK_STAT_RDY | SLINK_STAT_RXF_FLUSH | SLINK_STAT_TXF_FLUSH | \
SLINK_STAT_RXF_UNR | SLINK_STAT_TXF_OVF;
writel(reg, &regs->status);
debug("%s: STATUS = %08x\n", __func__, readl(&regs->status));
/* Set master mode and sw controlled CS */
reg = readl(&regs->command);
reg |= SLINK_CMD_M_S | SLINK_CMD_CS_SOFT;
writel(reg, &regs->command);
debug("%s: COMMAND = %08x\n", __func__, readl(&regs->command));
return 0;
}
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra30_spi_priv *priv = dev_get_priv(bus);
/* If it's too soon to do another transaction, wait */
if (pdata->deactivate_delay_us &&
priv->last_transaction_us) {
ulong delay_us; /* The delay completed so far */
delay_us = timer_get_us() - priv->last_transaction_us;
if (delay_us < pdata->deactivate_delay_us)
udelay(pdata->deactivate_delay_us - delay_us);
}
/* CS is negated on Tegra, so drive a 1 to get a 0 */
setbits_le32(&priv->regs->command, SLINK_CMD_CS_VAL);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra30_spi_priv *priv = dev_get_priv(bus);
/* CS is negated on Tegra, so drive a 0 to get a 1 */
clrbits_le32(&priv->regs->command, SLINK_CMD_CS_VAL);
/* Remember time of this transaction so we can honour the bus delay */
if (pdata->deactivate_delay_us)
priv->last_transaction_us = timer_get_us();
}
static int tegra30_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *data_out, void *data_in,
unsigned long flags)
{
struct udevice *bus = dev->parent;
struct tegra30_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg, tmpdout, tmpdin = 0;
const u8 *dout = data_out;
u8 *din = data_in;
int num_bytes;
int ret;
debug("%s: slave %u:%u dout %p din %p bitlen %u\n",
__func__, bus->seq, spi_chip_select(dev), dout, din, bitlen);
if (bitlen % 8)
return -1;
num_bytes = bitlen / 8;
ret = 0;
reg = readl(&regs->status);
writel(reg, &regs->status); /* Clear all SPI events via R/W */
debug("%s entry: STATUS = %08x\n", __func__, reg);
reg = readl(&regs->status2);
writel(reg, &regs->status2); /* Clear all STATUS2 events via R/W */
debug("%s entry: STATUS2 = %08x\n", __func__, reg);
debug("%s entry: COMMAND = %08x\n", __func__, readl(&regs->command));
clrsetbits_le32(&regs->command2, SLINK_CMD2_SS_EN_MASK,
SLINK_CMD2_TXEN | SLINK_CMD2_RXEN |
(spi_chip_select(dev) << SLINK_CMD2_SS_EN_SHIFT));
debug("%s entry: COMMAND2 = %08x\n", __func__, readl(&regs->command2));
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
/* handle data in 32-bit chunks */
while (num_bytes > 0) {
int bytes;
int is_read = 0;
int tm, i;
tmpdout = 0;
bytes = (num_bytes > 4) ? 4 : num_bytes;
if (dout != NULL) {
for (i = 0; i < bytes; ++i)
tmpdout = (tmpdout << 8) | dout[i];
dout += bytes;
}
num_bytes -= bytes;
clrsetbits_le32(&regs->command, SLINK_CMD_BIT_LENGTH_MASK,
bytes * 8 - 1);
writel(tmpdout, &regs->tx_fifo);
setbits_le32(&regs->command, SLINK_CMD_GO);
/*
* Wait for SPI transmit FIFO to empty, or to time out.
* The RX FIFO status will be read and cleared last
*/
for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) {
u32 status;
status = readl(&regs->status);
/* We can exit when we've had both RX and TX activity */
if (is_read && (status & SLINK_STAT_TXF_EMPTY))
break;
if ((status & (SLINK_STAT_BSY | SLINK_STAT_RDY)) !=
SLINK_STAT_RDY)
tm++;
else if (!(status & SLINK_STAT_RXF_EMPTY)) {
tmpdin = readl(&regs->rx_fifo);
is_read = 1;
/* swap bytes read in */
if (din != NULL) {
for (i = bytes - 1; i >= 0; --i) {
din[i] = tmpdin & 0xff;
tmpdin >>= 8;
}
din += bytes;
}
}
}
if (tm >= SPI_TIMEOUT)
ret = tm;
/* clear ACK RDY, etc. bits */
writel(readl(&regs->status), &regs->status);
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
debug("%s: transfer ended. Value=%08x, status = %08x\n",
__func__, tmpdin, readl(&regs->status));
if (ret) {
printf("%s: timeout during SPI transfer, tm %d\n",
__func__, ret);
return -1;
}
return 0;
}
static int tegra30_spi_set_speed(struct udevice *bus, uint speed)
{
struct tegra_spi_platdata *plat = bus->platdata;
struct tegra30_spi_priv *priv = dev_get_priv(bus);
if (speed > plat->frequency)
speed = plat->frequency;
priv->freq = speed;
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
return 0;
}
static int tegra30_spi_set_mode(struct udevice *bus, uint mode)
{
struct tegra30_spi_priv *priv = dev_get_priv(bus);
struct spi_regs *regs = priv->regs;
u32 reg;
reg = readl(&regs->command);
/* Set CPOL and CPHA */
reg &= ~(SLINK_CMD_IDLE_SCLK_MASK | SLINK_CMD_CK_SDA);
if (mode & SPI_CPHA)
reg |= SLINK_CMD_CK_SDA;
if (mode & SPI_CPOL)
reg |= SLINK_CMD_IDLE_SCLK_DRIVE_HIGH;
else
reg |= SLINK_CMD_IDLE_SCLK_DRIVE_LOW;
writel(reg, &regs->command);
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops tegra30_spi_ops = {
.claim_bus = tegra30_spi_claim_bus,
.xfer = tegra30_spi_xfer,
.set_speed = tegra30_spi_set_speed,
.set_mode = tegra30_spi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id tegra30_spi_ids[] = {
{ .compatible = "nvidia,tegra20-slink" },
{ }
};
U_BOOT_DRIVER(tegra30_spi) = {
.name = "tegra20_slink",
.id = UCLASS_SPI,
.of_match = tegra30_spi_ids,
.ops = &tegra30_spi_ops,
.ofdata_to_platdata = tegra30_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata),
.priv_auto_alloc_size = sizeof(struct tegra30_spi_priv),
.probe = tegra30_spi_probe,
};

View File

@@ -0,0 +1,417 @@
/*
* NVIDIA Tegra210 QSPI controller driver
*
* (C) Copyright 2015 NVIDIA Corporation <www.nvidia.com>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <asm/arch-tegra/clk_rst.h>
#include <spi.h>
#include <fdtdec.h>
#include "tegra_spi.h"
DECLARE_GLOBAL_DATA_PTR;
/* COMMAND1 */
#define QSPI_CMD1_GO BIT(31)
#define QSPI_CMD1_M_S BIT(30)
#define QSPI_CMD1_MODE_MASK GENMASK(1,0)
#define QSPI_CMD1_MODE_SHIFT 28
#define QSPI_CMD1_CS_SEL_MASK GENMASK(1,0)
#define QSPI_CMD1_CS_SEL_SHIFT 26
#define QSPI_CMD1_CS_POL_INACTIVE0 BIT(22)
#define QSPI_CMD1_CS_SW_HW BIT(21)
#define QSPI_CMD1_CS_SW_VAL BIT(20)
#define QSPI_CMD1_IDLE_SDA_MASK GENMASK(1,0)
#define QSPI_CMD1_IDLE_SDA_SHIFT 18
#define QSPI_CMD1_BIDIR BIT(17)
#define QSPI_CMD1_LSBI_FE BIT(16)
#define QSPI_CMD1_LSBY_FE BIT(15)
#define QSPI_CMD1_BOTH_EN_BIT BIT(14)
#define QSPI_CMD1_BOTH_EN_BYTE BIT(13)
#define QSPI_CMD1_RX_EN BIT(12)
#define QSPI_CMD1_TX_EN BIT(11)
#define QSPI_CMD1_PACKED BIT(5)
#define QSPI_CMD1_BITLEN_MASK GENMASK(4,0)
#define QSPI_CMD1_BITLEN_SHIFT 0
/* COMMAND2 */
#define QSPI_CMD2_TX_CLK_TAP_DELAY BIT(6)
#define QSPI_CMD2_TX_CLK_TAP_DELAY_MASK GENMASK(11,6)
#define QSPI_CMD2_RX_CLK_TAP_DELAY BIT(0)
#define QSPI_CMD2_RX_CLK_TAP_DELAY_MASK GENMASK(5,0)
/* TRANSFER STATUS */
#define QSPI_XFER_STS_RDY BIT(30)
/* FIFO STATUS */
#define QSPI_FIFO_STS_CS_INACTIVE BIT(31)
#define QSPI_FIFO_STS_FRAME_END BIT(30)
#define QSPI_FIFO_STS_RX_FIFO_FLUSH BIT(15)
#define QSPI_FIFO_STS_TX_FIFO_FLUSH BIT(14)
#define QSPI_FIFO_STS_ERR BIT(8)
#define QSPI_FIFO_STS_TX_FIFO_OVF BIT(7)
#define QSPI_FIFO_STS_TX_FIFO_UNR BIT(6)
#define QSPI_FIFO_STS_RX_FIFO_OVF BIT(5)
#define QSPI_FIFO_STS_RX_FIFO_UNR BIT(4)
#define QSPI_FIFO_STS_TX_FIFO_FULL BIT(3)
#define QSPI_FIFO_STS_TX_FIFO_EMPTY BIT(2)
#define QSPI_FIFO_STS_RX_FIFO_FULL BIT(1)
#define QSPI_FIFO_STS_RX_FIFO_EMPTY BIT(0)
#define QSPI_TIMEOUT 1000
struct qspi_regs {
u32 command1; /* 000:QSPI_COMMAND1 register */
u32 command2; /* 004:QSPI_COMMAND2 register */
u32 timing1; /* 008:QSPI_CS_TIM1 register */
u32 timing2; /* 00c:QSPI_CS_TIM2 register */
u32 xfer_status;/* 010:QSPI_TRANS_STATUS register */
u32 fifo_status;/* 014:QSPI_FIFO_STATUS register */
u32 tx_data; /* 018:QSPI_TX_DATA register */
u32 rx_data; /* 01c:QSPI_RX_DATA register */
u32 dma_ctl; /* 020:QSPI_DMA_CTL register */
u32 dma_blk; /* 024:QSPI_DMA_BLK register */
u32 rsvd[56]; /* 028-107 reserved */
u32 tx_fifo; /* 108:QSPI_FIFO1 register */
u32 rsvd2[31]; /* 10c-187 reserved */
u32 rx_fifo; /* 188:QSPI_FIFO2 register */
u32 spare_ctl; /* 18c:QSPI_SPARE_CTRL register */
};
struct tegra210_qspi_priv {
struct qspi_regs *regs;
unsigned int freq;
unsigned int mode;
int periph_id;
int valid;
int last_transaction_us;
};
static int tegra210_qspi_ofdata_to_platdata(struct udevice *bus)
{
struct tegra_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->base = dev_get_addr(bus);
plat->periph_id = clock_decode_periph_id(blob, node);
if (plat->periph_id == PERIPH_ID_NONE) {
debug("%s: could not decode periph id %d\n", __func__,
plat->periph_id);
return -FDT_ERR_NOTFOUND;
}
/* Use 500KHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
500000);
plat->deactivate_delay_us = fdtdec_get_int(blob, node,
"spi-deactivate-delay", 0);
debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n",
__func__, plat->base, plat->periph_id, plat->frequency,
plat->deactivate_delay_us);
return 0;
}
static int tegra210_qspi_probe(struct udevice *bus)
{
struct tegra_spi_platdata *plat = dev_get_platdata(bus);
struct tegra210_qspi_priv *priv = dev_get_priv(bus);
priv->regs = (struct qspi_regs *)plat->base;
priv->last_transaction_us = timer_get_us();
priv->freq = plat->frequency;
priv->periph_id = plat->periph_id;
return 0;
}
static int tegra210_qspi_claim_bus(struct udevice *bus)
{
struct tegra210_qspi_priv *priv = dev_get_priv(bus);
struct qspi_regs *regs = priv->regs;
/* Change SPI clock to correct frequency, PLLP_OUT0 source */
clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH, priv->freq);
debug("%s: FIFO STATUS = %08x\n", __func__, readl(&regs->fifo_status));
/* Set master mode and sw controlled CS */
setbits_le32(&regs->command1, QSPI_CMD1_M_S | QSPI_CMD1_CS_SW_HW |
(priv->mode << QSPI_CMD1_MODE_SHIFT));
debug("%s: COMMAND1 = %08x\n", __func__, readl(&regs->command1));
return 0;
}
/**
* Activate the CS by driving it LOW
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra210_qspi_priv *priv = dev_get_priv(bus);
/* If it's too soon to do another transaction, wait */
if (pdata->deactivate_delay_us &&
priv->last_transaction_us) {
ulong delay_us; /* The delay completed so far */
delay_us = timer_get_us() - priv->last_transaction_us;
if (delay_us < pdata->deactivate_delay_us)
udelay(pdata->deactivate_delay_us - delay_us);
}
clrbits_le32(&priv->regs->command1, QSPI_CMD1_CS_SW_VAL);
}
/**
* Deactivate the CS by driving it HIGH
*
* @param slave Pointer to spi_slave to which controller has to
* communicate with
*/
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct tegra_spi_platdata *pdata = dev_get_platdata(bus);
struct tegra210_qspi_priv *priv = dev_get_priv(bus);
setbits_le32(&priv->regs->command1, QSPI_CMD1_CS_SW_VAL);
/* Remember time of this transaction so we can honour the bus delay */
if (pdata->deactivate_delay_us)
priv->last_transaction_us = timer_get_us();
debug("Deactivate CS, bus '%s'\n", bus->name);
}
static int tegra210_qspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *data_out, void *data_in,
unsigned long flags)
{
struct udevice *bus = dev->parent;
struct tegra210_qspi_priv *priv = dev_get_priv(bus);
struct qspi_regs *regs = priv->regs;
u32 reg, tmpdout, tmpdin = 0;
const u8 *dout = data_out;
u8 *din = data_in;
int num_bytes, tm, ret;
debug("%s: slave %u:%u dout %p din %p bitlen %u\n",
__func__, bus->seq, spi_chip_select(dev), dout, din, bitlen);
if (bitlen % 8)
return -1;
num_bytes = bitlen / 8;
ret = 0;
/* clear all error status bits */
reg = readl(&regs->fifo_status);
writel(reg, &regs->fifo_status);
/* flush RX/TX FIFOs */
setbits_le32(&regs->fifo_status,
(QSPI_FIFO_STS_RX_FIFO_FLUSH |
QSPI_FIFO_STS_TX_FIFO_FLUSH));
tm = QSPI_TIMEOUT;
while ((tm && readl(&regs->fifo_status) &
(QSPI_FIFO_STS_RX_FIFO_FLUSH |
QSPI_FIFO_STS_TX_FIFO_FLUSH))) {
tm--;
udelay(1);
}
if (!tm) {
printf("%s: timeout during QSPI FIFO flush!\n",
__func__);
return -1;
}
/*
* Notes:
* 1. don't set LSBY_FE, so no need to swap bytes from/to TX/RX FIFOs;
* 2. don't set RX_EN and TX_EN yet.
* (SW needs to make sure that while programming the blk_size,
* tx_en and rx_en bits must be zero)
* [TODO] I (Yen Lin) have problems when both RX/TX EN bits are set
* i.e., both dout and din are not NULL.
*/
clrsetbits_le32(&regs->command1,
(QSPI_CMD1_LSBI_FE | QSPI_CMD1_LSBY_FE |
QSPI_CMD1_RX_EN | QSPI_CMD1_TX_EN),
(spi_chip_select(dev) << QSPI_CMD1_CS_SEL_SHIFT));
/* set xfer size to 1 block (32 bits) */
writel(0, &regs->dma_blk);
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
/* handle data in 32-bit chunks */
while (num_bytes > 0) {
int bytes;
tmpdout = 0;
bytes = (num_bytes > 4) ? 4 : num_bytes;
if (dout != NULL) {
memcpy((void *)&tmpdout, (void *)dout, bytes);
dout += bytes;
num_bytes -= bytes;
writel(tmpdout, &regs->tx_fifo);
setbits_le32(&regs->command1, QSPI_CMD1_TX_EN);
}
if (din != NULL)
setbits_le32(&regs->command1, QSPI_CMD1_RX_EN);
/* clear ready bit */
setbits_le32(&regs->xfer_status, QSPI_XFER_STS_RDY);
clrsetbits_le32(&regs->command1,
QSPI_CMD1_BITLEN_MASK << QSPI_CMD1_BITLEN_SHIFT,
(bytes * 8 - 1) << QSPI_CMD1_BITLEN_SHIFT);
/* Need to stabilize other reg bits before GO bit set.
* As per the TRM:
* "For successful operation at various freq combinations,
* a minimum of 4-5 spi_clk cycle delay might be required
* before enabling the PIO or DMA bits. The worst case delay
* calculation can be done considering slowest qspi_clk as
* 1MHz. Based on that 1us delay should be enough before
* enabling PIO or DMA." Padded another 1us for safety.
*/
udelay(2);
setbits_le32(&regs->command1, QSPI_CMD1_GO);
udelay(1);
/*
* Wait for SPI transmit FIFO to empty, or to time out.
* The RX FIFO status will be read and cleared last
*/
for (tm = 0; tm < QSPI_TIMEOUT; ++tm) {
u32 fifo_status, xfer_status;
xfer_status = readl(&regs->xfer_status);
if (!(xfer_status & QSPI_XFER_STS_RDY))
continue;
fifo_status = readl(&regs->fifo_status);
if (fifo_status & QSPI_FIFO_STS_ERR) {
debug("%s: got a fifo error: ", __func__);
if (fifo_status & QSPI_FIFO_STS_TX_FIFO_OVF)
debug("tx FIFO overflow ");
if (fifo_status & QSPI_FIFO_STS_TX_FIFO_UNR)
debug("tx FIFO underrun ");
if (fifo_status & QSPI_FIFO_STS_RX_FIFO_OVF)
debug("rx FIFO overflow ");
if (fifo_status & QSPI_FIFO_STS_RX_FIFO_UNR)
debug("rx FIFO underrun ");
if (fifo_status & QSPI_FIFO_STS_TX_FIFO_FULL)
debug("tx FIFO full ");
if (fifo_status & QSPI_FIFO_STS_TX_FIFO_EMPTY)
debug("tx FIFO empty ");
if (fifo_status & QSPI_FIFO_STS_RX_FIFO_FULL)
debug("rx FIFO full ");
if (fifo_status & QSPI_FIFO_STS_RX_FIFO_EMPTY)
debug("rx FIFO empty ");
debug("\n");
break;
}
if (!(fifo_status & QSPI_FIFO_STS_RX_FIFO_EMPTY)) {
tmpdin = readl(&regs->rx_fifo);
if (din != NULL) {
memcpy(din, &tmpdin, bytes);
din += bytes;
num_bytes -= bytes;
}
}
break;
}
if (tm >= QSPI_TIMEOUT)
ret = tm;
/* clear ACK RDY, etc. bits */
writel(readl(&regs->fifo_status), &regs->fifo_status);
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
debug("%s: transfer ended. Value=%08x, fifo_status = %08x\n",
__func__, tmpdin, readl(&regs->fifo_status));
if (ret) {
printf("%s: timeout during SPI transfer, tm %d\n",
__func__, ret);
return -1;
}
return ret;
}
static int tegra210_qspi_set_speed(struct udevice *bus, uint speed)
{
struct tegra_spi_platdata *plat = bus->platdata;
struct tegra210_qspi_priv *priv = dev_get_priv(bus);
if (speed > plat->frequency)
speed = plat->frequency;
priv->freq = speed;
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
return 0;
}
static int tegra210_qspi_set_mode(struct udevice *bus, uint mode)
{
struct tegra210_qspi_priv *priv = dev_get_priv(bus);
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops tegra210_qspi_ops = {
.claim_bus = tegra210_qspi_claim_bus,
.xfer = tegra210_qspi_xfer,
.set_speed = tegra210_qspi_set_speed,
.set_mode = tegra210_qspi_set_mode,
/*
* cs_info is not needed, since we require all chip selects to be
* in the device tree explicitly
*/
};
static const struct udevice_id tegra210_qspi_ids[] = {
{ .compatible = "nvidia,tegra210-qspi" },
{ }
};
U_BOOT_DRIVER(tegra210_qspi) = {
.name = "tegra210-qspi",
.id = UCLASS_SPI,
.of_match = tegra210_qspi_ids,
.ops = &tegra210_qspi_ops,
.ofdata_to_platdata = tegra210_qspi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata),
.priv_auto_alloc_size = sizeof(struct tegra210_qspi_priv),
.per_child_auto_alloc_size = sizeof(struct spi_slave),
.probe = tegra210_qspi_probe,
};

View File

@@ -0,0 +1,12 @@
/*
* (C) Copyright 2014 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
struct tegra_spi_platdata {
enum periph_id periph_id;
int frequency; /* Default clock frequency, -1 for none */
ulong base;
uint deactivate_delay_us; /* Delay to wait after deactivate */
};

View File

@@ -0,0 +1,594 @@
/*
* TI QSPI driver
*
* Copyright (C) 2013, Texas Instruments, Incorporated
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <asm/io.h>
#include <asm/arch/omap.h>
#include <malloc.h>
#include <spi.h>
#include <dm.h>
#include <asm/gpio.h>
#include <asm/omap_gpio.h>
#include <asm/omap_common.h>
#include <asm/ti-common/ti-edma3.h>
DECLARE_GLOBAL_DATA_PTR;
/* ti qpsi register bit masks */
#define QSPI_TIMEOUT 2000000
#define QSPI_FCLK 192000000
/* clock control */
#define QSPI_CLK_EN BIT(31)
#define QSPI_CLK_DIV_MAX 0xffff
/* command */
#define QSPI_EN_CS(n) (n << 28)
#define QSPI_WLEN(n) ((n-1) << 19)
#define QSPI_3_PIN BIT(18)
#define QSPI_RD_SNGL BIT(16)
#define QSPI_WR_SNGL (2 << 16)
#define QSPI_INVAL (4 << 16)
#define QSPI_RD_QUAD (7 << 16)
/* device control */
#define QSPI_DD(m, n) (m << (3 + n*8))
#define QSPI_CKPHA(n) (1 << (2 + n*8))
#define QSPI_CSPOL(n) (1 << (1 + n*8))
#define QSPI_CKPOL(n) (1 << (n*8))
/* status */
#define QSPI_WC BIT(1)
#define QSPI_BUSY BIT(0)
#define QSPI_WC_BUSY (QSPI_WC | QSPI_BUSY)
#define QSPI_XFER_DONE QSPI_WC
#define MM_SWITCH 0x01
#define MEM_CS(cs) ((cs + 1) << 8)
#define MEM_CS_UNSELECT 0xfffff8ff
#define MMAP_START_ADDR_DRA 0x5c000000
#define MMAP_START_ADDR_AM43x 0x30000000
#define CORE_CTRL_IO 0x4a002558
#define QSPI_CMD_READ (0x3 << 0)
#define QSPI_CMD_READ_DUAL (0x6b << 0)
#define QSPI_CMD_READ_QUAD (0x6c << 0)
#define QSPI_CMD_READ_FAST (0x0b << 0)
#define QSPI_SETUP0_NUM_A_BYTES (0x3 << 8)
#define QSPI_SETUP0_NUM_D_BYTES_NO_BITS (0x0 << 10)
#define QSPI_SETUP0_NUM_D_BYTES_8_BITS (0x1 << 10)
#define QSPI_SETUP0_READ_NORMAL (0x0 << 12)
#define QSPI_SETUP0_READ_DUAL (0x1 << 12)
#define QSPI_SETUP0_READ_QUAD (0x3 << 12)
#define QSPI_CMD_WRITE (0x12 << 16)
#define QSPI_NUM_DUMMY_BITS (0x0 << 24)
/* ti qspi register set */
struct ti_qspi_regs {
u32 pid;
u32 pad0[3];
u32 sysconfig;
u32 pad1[3];
u32 int_stat_raw;
u32 int_stat_en;
u32 int_en_set;
u32 int_en_ctlr;
u32 intc_eoi;
u32 pad2[3];
u32 clk_ctrl;
u32 dc;
u32 cmd;
u32 status;
u32 data;
u32 setup0;
u32 setup1;
u32 setup2;
u32 setup3;
u32 memswitch;
u32 data1;
u32 data2;
u32 data3;
};
/* ti qspi priv */
struct ti_qspi_priv {
#ifndef CONFIG_DM_SPI
struct spi_slave slave;
#else
void *memory_map;
uint max_hz;
u32 num_cs;
#endif
struct ti_qspi_regs *base;
void *ctrl_mod_mmap;
unsigned int mode;
u32 cmd;
u32 dc;
};
static void ti_spi_set_speed(struct ti_qspi_priv *priv, uint hz)
{
uint clk_div;
debug("ti_spi_set_speed: hz: %d, clock divider %d\n", hz, clk_div);
if (!hz)
clk_div = 0;
else
clk_div = (QSPI_FCLK / hz) - 1;
/* disable SCLK */
writel(readl(&priv->base->clk_ctrl) & ~QSPI_CLK_EN,
&priv->base->clk_ctrl);
/* assign clk_div values */
if (clk_div < 0)
clk_div = 0;
else if (clk_div > QSPI_CLK_DIV_MAX)
clk_div = QSPI_CLK_DIV_MAX;
/* enable SCLK */
writel(QSPI_CLK_EN | clk_div, &priv->base->clk_ctrl);
}
static void ti_qspi_cs_deactivate(struct ti_qspi_priv *priv)
{
writel(priv->cmd | QSPI_INVAL, &priv->base->cmd);
/* dummy readl to ensure bus sync */
readl(&priv->base->cmd);
}
static int __ti_qspi_set_mode(struct ti_qspi_priv *priv, unsigned int mode)
{
priv->dc = 0;
if (mode & SPI_CPHA)
priv->dc |= QSPI_CKPHA(0);
if (mode & SPI_CPOL)
priv->dc |= QSPI_CKPOL(0);
if (mode & SPI_CS_HIGH)
priv->dc |= QSPI_CSPOL(0);
return 0;
}
static int __ti_qspi_claim_bus(struct ti_qspi_priv *priv, int cs)
{
writel(priv->dc, &priv->base->dc);
writel(0, &priv->base->cmd);
writel(0, &priv->base->data);
priv->dc <<= cs * 8;
writel(priv->dc, &priv->base->dc);
return 0;
}
static void __ti_qspi_release_bus(struct ti_qspi_priv *priv)
{
writel(0, &priv->base->dc);
writel(0, &priv->base->cmd);
writel(0, &priv->base->data);
}
static void ti_qspi_ctrl_mode_mmap(void *ctrl_mod_mmap, int cs, bool enable)
{
u32 val;
val = readl(ctrl_mod_mmap);
if (enable)
val |= MEM_CS(cs);
else
val &= MEM_CS_UNSELECT;
writel(val, ctrl_mod_mmap);
}
static int __ti_qspi_xfer(struct ti_qspi_priv *priv, unsigned int bitlen,
const void *dout, void *din, unsigned long flags,
u32 cs)
{
uint words = bitlen >> 3; /* fixed 8-bit word length */
const uchar *txp = dout;
uchar *rxp = din;
uint status;
int timeout;
/* Setup mmap flags */
if (flags & SPI_XFER_MMAP) {
writel(MM_SWITCH, &priv->base->memswitch);
if (priv->ctrl_mod_mmap)
ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap, cs, true);
return 0;
} else if (flags & SPI_XFER_MMAP_END) {
writel(~MM_SWITCH, &priv->base->memswitch);
if (priv->ctrl_mod_mmap)
ti_qspi_ctrl_mode_mmap(priv->ctrl_mod_mmap, cs, false);
return 0;
}
if (bitlen == 0)
return -1;
if (bitlen % 8) {
debug("spi_xfer: Non byte aligned SPI transfer\n");
return -1;
}
/* Setup command reg */
priv->cmd = 0;
priv->cmd |= QSPI_WLEN(8);
priv->cmd |= QSPI_EN_CS(cs);
if (priv->mode & SPI_3WIRE)
priv->cmd |= QSPI_3_PIN;
priv->cmd |= 0xfff;
/* FIXME: This delay is required for successfull
* completion of read/write/erase. Once its root
* caused, it will be remove from the driver.
*/
#ifdef CONFIG_AM43XX
udelay(100);
#endif
while (words--) {
if (txp) {
debug("tx cmd %08x dc %08x data %02x\n",
priv->cmd | QSPI_WR_SNGL, priv->dc, *txp);
writel(*txp++, &priv->base->data);
writel(priv->cmd | QSPI_WR_SNGL,
&priv->base->cmd);
status = readl(&priv->base->status);
timeout = QSPI_TIMEOUT;
while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) {
if (--timeout < 0) {
printf("spi_xfer: TX timeout!\n");
return -1;
}
status = readl(&priv->base->status);
}
debug("tx done, status %08x\n", status);
}
if (rxp) {
priv->cmd |= QSPI_RD_SNGL;
debug("rx cmd %08x dc %08x\n",
priv->cmd, priv->dc);
#ifdef CONFIG_DRA7XX
udelay(500);
#endif
writel(priv->cmd, &priv->base->cmd);
status = readl(&priv->base->status);
timeout = QSPI_TIMEOUT;
while ((status & QSPI_WC_BUSY) != QSPI_XFER_DONE) {
if (--timeout < 0) {
printf("spi_xfer: RX timeout!\n");
return -1;
}
status = readl(&priv->base->status);
}
*rxp++ = readl(&priv->base->data);
debug("rx done, status %08x, read %02x\n",
status, *(rxp-1));
}
}
/* Terminate frame */
if (flags & SPI_XFER_END)
ti_qspi_cs_deactivate(priv);
return 0;
}
/* TODO: control from sf layer to here through dm-spi */
#if defined(CONFIG_TI_EDMA3) && !defined(CONFIG_DMA)
void spi_flash_copy_mmap(void *data, void *offset, size_t len)
{
unsigned int addr = (unsigned int) (data);
unsigned int edma_slot_num = 1;
/* Invalidate the area, so no writeback into the RAM races with DMA */
invalidate_dcache_range(addr, addr + roundup(len, ARCH_DMA_MINALIGN));
/* enable edma3 clocks */
enable_edma3_clocks();
/* Call edma3 api to do actual DMA transfer */
edma3_transfer(EDMA3_BASE, edma_slot_num, data, offset, len);
/* disable edma3 clocks */
disable_edma3_clocks();
*((unsigned int *)offset) += len;
}
#endif
#ifndef CONFIG_DM_SPI
static inline struct ti_qspi_priv *to_ti_qspi_priv(struct spi_slave *slave)
{
return container_of(slave, struct ti_qspi_priv, slave);
}
int spi_cs_is_valid(unsigned int bus, unsigned int cs)
{
return 1;
}
void spi_cs_activate(struct spi_slave *slave)
{
/* CS handled in xfer */
return;
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct ti_qspi_priv *priv = to_ti_qspi_priv(slave);
ti_qspi_cs_deactivate(priv);
}
void spi_init(void)
{
/* nothing to do */
}
static void ti_spi_setup_spi_register(struct ti_qspi_priv *priv)
{
u32 memval = 0;
#ifdef CONFIG_QSPI_QUAD_SUPPORT
struct spi_slave *slave = &priv->slave;
memval |= (QSPI_CMD_READ_QUAD | QSPI_SETUP0_NUM_A_BYTES |
QSPI_SETUP0_NUM_D_BYTES_8_BITS |
QSPI_SETUP0_READ_QUAD | QSPI_CMD_WRITE |
QSPI_NUM_DUMMY_BITS);
slave->mode_rx = SPI_RX_QUAD;
#else
memval |= QSPI_CMD_READ | QSPI_SETUP0_NUM_A_BYTES |
QSPI_SETUP0_NUM_D_BYTES_NO_BITS |
QSPI_SETUP0_READ_NORMAL | QSPI_CMD_WRITE |
QSPI_NUM_DUMMY_BITS;
#endif
writel(memval, &priv->base->setup0);
}
struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
unsigned int max_hz, unsigned int mode)
{
struct ti_qspi_priv *priv;
#ifdef CONFIG_AM43XX
gpio_request(CONFIG_QSPI_SEL_GPIO, "qspi_gpio");
gpio_direction_output(CONFIG_QSPI_SEL_GPIO, 1);
#endif
priv = spi_alloc_slave(struct ti_qspi_priv, bus, cs);
if (!priv) {
printf("SPI_error: Fail to allocate ti_qspi_priv\n");
return NULL;
}
priv->base = (struct ti_qspi_regs *)QSPI_BASE;
priv->mode = mode;
#if defined(CONFIG_DRA7XX) || defined(CONFIG_AM57XX)
priv->ctrl_mod_mmap = (void *)CORE_CTRL_IO;
priv->slave.memory_map = (void *)MMAP_START_ADDR_DRA;
#else
priv->slave.memory_map = (void *)MMAP_START_ADDR_AM43x;
#endif
ti_spi_set_speed(priv, max_hz);
#ifdef CONFIG_TI_SPI_MMAP
ti_spi_setup_spi_register(priv);
#endif
return &priv->slave;
}
void spi_free_slave(struct spi_slave *slave)
{
struct ti_qspi_priv *priv = to_ti_qspi_priv(slave);
free(priv);
}
int spi_claim_bus(struct spi_slave *slave)
{
struct ti_qspi_priv *priv = to_ti_qspi_priv(slave);
debug("%s: bus:%i cs:%i\n", __func__, priv->slave.bus, priv->slave.cs);
__ti_qspi_set_mode(priv, priv->mode);
return __ti_qspi_claim_bus(priv, priv->slave.cs);
}
void spi_release_bus(struct spi_slave *slave)
{
struct ti_qspi_priv *priv = to_ti_qspi_priv(slave);
debug("%s: bus:%i cs:%i\n", __func__, priv->slave.bus, priv->slave.cs);
__ti_qspi_release_bus(priv);
}
int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
void *din, unsigned long flags)
{
struct ti_qspi_priv *priv = to_ti_qspi_priv(slave);
debug("spi_xfer: bus:%i cs:%i bitlen:%i flags:%lx\n",
priv->slave.bus, priv->slave.cs, bitlen, flags);
return __ti_qspi_xfer(priv, bitlen, dout, din, flags, priv->slave.cs);
}
#else /* CONFIG_DM_SPI */
static void __ti_qspi_setup_memorymap(struct ti_qspi_priv *priv,
struct spi_slave *slave,
bool enable)
{
u32 memval;
u32 mode = slave->mode_rx & (SPI_RX_QUAD | SPI_RX_DUAL);
if (!enable) {
writel(0, &priv->base->setup0);
return;
}
memval = QSPI_SETUP0_NUM_A_BYTES | QSPI_CMD_WRITE | QSPI_NUM_DUMMY_BITS;
switch (mode) {
case SPI_RX_QUAD:
memval |= QSPI_CMD_READ_QUAD;
memval |= QSPI_SETUP0_NUM_D_BYTES_8_BITS;
memval |= QSPI_SETUP0_READ_QUAD;
slave->mode_rx = SPI_RX_QUAD;
break;
case SPI_RX_DUAL:
memval |= QSPI_CMD_READ_DUAL;
memval |= QSPI_SETUP0_NUM_D_BYTES_8_BITS;
memval |= QSPI_SETUP0_READ_DUAL;
break;
default:
memval |= QSPI_CMD_READ;
memval |= QSPI_SETUP0_NUM_D_BYTES_NO_BITS;
memval |= QSPI_SETUP0_READ_NORMAL;
break;
}
writel(memval, &priv->base->setup0);
}
static int ti_qspi_set_speed(struct udevice *bus, uint max_hz)
{
struct ti_qspi_priv *priv = dev_get_priv(bus);
ti_spi_set_speed(priv, max_hz);
return 0;
}
static int ti_qspi_set_mode(struct udevice *bus, uint mode)
{
struct ti_qspi_priv *priv = dev_get_priv(bus);
return __ti_qspi_set_mode(priv, mode);
}
static int ti_qspi_claim_bus(struct udevice *dev)
{
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
struct spi_slave *slave = dev_get_parent_priv(dev);
struct ti_qspi_priv *priv;
struct udevice *bus;
bus = dev->parent;
priv = dev_get_priv(bus);
if (slave_plat->cs > priv->num_cs) {
debug("invalid qspi chip select\n");
return -EINVAL;
}
__ti_qspi_setup_memorymap(priv, slave, true);
return __ti_qspi_claim_bus(priv, slave_plat->cs);
}
static int ti_qspi_release_bus(struct udevice *dev)
{
struct spi_slave *slave = dev_get_parent_priv(dev);
struct ti_qspi_priv *priv;
struct udevice *bus;
bus = dev->parent;
priv = dev_get_priv(bus);
__ti_qspi_setup_memorymap(priv, slave, false);
__ti_qspi_release_bus(priv);
return 0;
}
static int ti_qspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
struct ti_qspi_priv *priv;
struct udevice *bus;
bus = dev->parent;
priv = dev_get_priv(bus);
if (slave->cs > priv->num_cs) {
debug("invalid qspi chip select\n");
return -EINVAL;
}
return __ti_qspi_xfer(priv, bitlen, dout, din, flags, slave->cs);
}
static int ti_qspi_probe(struct udevice *bus)
{
/* Nothing to do in probe */
return 0;
}
static int ti_qspi_ofdata_to_platdata(struct udevice *bus)
{
struct ti_qspi_priv *priv = dev_get_priv(bus);
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
fdt_addr_t addr;
void *mmap;
priv->base = map_physmem(dev_get_addr(bus), sizeof(struct ti_qspi_regs),
MAP_NOCACHE);
priv->memory_map = map_physmem(dev_get_addr_index(bus, 1), 0,
MAP_NOCACHE);
addr = dev_get_addr_index(bus, 2);
mmap = map_physmem(dev_get_addr_index(bus, 2), 0, MAP_NOCACHE);
priv->ctrl_mod_mmap = (addr == FDT_ADDR_T_NONE) ? NULL : mmap;
priv->max_hz = fdtdec_get_int(blob, node, "spi-max-frequency", -1);
if (priv->max_hz < 0) {
debug("Error: Max frequency missing\n");
return -ENODEV;
}
priv->num_cs = fdtdec_get_int(blob, node, "num-cs", 4);
debug("%s: regs=<0x%x>, max-frequency=%d\n", __func__,
(int)priv->base, priv->max_hz);
return 0;
}
static int ti_qspi_child_pre_probe(struct udevice *dev)
{
struct spi_slave *slave = dev_get_parent_priv(dev);
struct udevice *bus = dev_get_parent(dev);
struct ti_qspi_priv *priv = dev_get_priv(bus);
slave->memory_map = priv->memory_map;
return 0;
}
static const struct dm_spi_ops ti_qspi_ops = {
.claim_bus = ti_qspi_claim_bus,
.release_bus = ti_qspi_release_bus,
.xfer = ti_qspi_xfer,
.set_speed = ti_qspi_set_speed,
.set_mode = ti_qspi_set_mode,
};
static const struct udevice_id ti_qspi_ids[] = {
{ .compatible = "ti,dra7xxx-qspi" },
{ .compatible = "ti,am4372-qspi" },
{ }
};
U_BOOT_DRIVER(ti_qspi) = {
.name = "ti_qspi",
.id = UCLASS_SPI,
.of_match = ti_qspi_ids,
.ops = &ti_qspi_ops,
.ofdata_to_platdata = ti_qspi_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct ti_qspi_priv),
.probe = ti_qspi_probe,
.child_pre_probe = ti_qspi_child_pre_probe,
};
#endif /* CONFIG_DM_SPI */

View File

@@ -0,0 +1,302 @@
/*
* Xilinx SPI driver
*
* Supports 8 bit SPI transfers only, with or w/o FIFO
*
* Based on bfin_spi.c, by way of altera_spi.c
* Copyright (c) 2015 Jagan Teki <jteki@openedev.com>
* Copyright (c) 2012 Stephan Linz <linz@li-pro.net>
* Copyright (c) 2010 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
* Copyright (c) 2010 Thomas Chou <thomas@wytron.com.tw>
* Copyright (c) 2005-2008 Analog Devices Inc.
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
/*
* [0]: http://www.xilinx.com/support/documentation
*
* Xilinx SPI Register Definitions
* [1]: [0]/ip_documentation/xps_spi.pdf
* page 8, Register Descriptions
* [2]: [0]/ip_documentation/axi_spi_ds742.pdf
* page 7, Register Overview Table
*/
/* SPI Control Register (spicr), [1] p9, [2] p8 */
#define SPICR_LSB_FIRST BIT(9)
#define SPICR_MASTER_INHIBIT BIT(8)
#define SPICR_MANUAL_SS BIT(7)
#define SPICR_RXFIFO_RESEST BIT(6)
#define SPICR_TXFIFO_RESEST BIT(5)
#define SPICR_CPHA BIT(4)
#define SPICR_CPOL BIT(3)
#define SPICR_MASTER_MODE BIT(2)
#define SPICR_SPE BIT(1)
#define SPICR_LOOP BIT(0)
/* SPI Status Register (spisr), [1] p11, [2] p10 */
#define SPISR_SLAVE_MODE_SELECT BIT(5)
#define SPISR_MODF BIT(4)
#define SPISR_TX_FULL BIT(3)
#define SPISR_TX_EMPTY BIT(2)
#define SPISR_RX_FULL BIT(1)
#define SPISR_RX_EMPTY BIT(0)
/* SPI Data Transmit Register (spidtr), [1] p12, [2] p12 */
#define SPIDTR_8BIT_MASK GENMASK(7, 0)
#define SPIDTR_16BIT_MASK GENMASK(15, 0)
#define SPIDTR_32BIT_MASK GENMASK(31, 0)
/* SPI Data Receive Register (spidrr), [1] p12, [2] p12 */
#define SPIDRR_8BIT_MASK GENMASK(7, 0)
#define SPIDRR_16BIT_MASK GENMASK(15, 0)
#define SPIDRR_32BIT_MASK GENMASK(31, 0)
/* SPI Slave Select Register (spissr), [1] p13, [2] p13 */
#define SPISSR_MASK(cs) (1 << (cs))
#define SPISSR_ACT(cs) ~SPISSR_MASK(cs)
#define SPISSR_OFF ~0UL
/* SPI Software Reset Register (ssr) */
#define SPISSR_RESET_VALUE 0x0a
#define XILSPI_MAX_XFER_BITS 8
#define XILSPI_SPICR_DFLT_ON (SPICR_MANUAL_SS | SPICR_MASTER_MODE | \
SPICR_SPE)
#define XILSPI_SPICR_DFLT_OFF (SPICR_MASTER_INHIBIT | SPICR_MANUAL_SS)
#ifndef CONFIG_XILINX_SPI_IDLE_VAL
#define CONFIG_XILINX_SPI_IDLE_VAL GENMASK(7, 0)
#endif
#ifndef CONFIG_SYS_XILINX_SPI_LIST
#define CONFIG_SYS_XILINX_SPI_LIST { CONFIG_SYS_SPI_BASE }
#endif
/* xilinx spi register set */
struct xilinx_spi_regs {
u32 __space0__[7];
u32 dgier; /* Device Global Interrupt Enable Register (DGIER) */
u32 ipisr; /* IP Interrupt Status Register (IPISR) */
u32 __space1__;
u32 ipier; /* IP Interrupt Enable Register (IPIER) */
u32 __space2__[5];
u32 srr; /* Softare Reset Register (SRR) */
u32 __space3__[7];
u32 spicr; /* SPI Control Register (SPICR) */
u32 spisr; /* SPI Status Register (SPISR) */
u32 spidtr; /* SPI Data Transmit Register (SPIDTR) */
u32 spidrr; /* SPI Data Receive Register (SPIDRR) */
u32 spissr; /* SPI Slave Select Register (SPISSR) */
u32 spitfor; /* SPI Transmit FIFO Occupancy Register (SPITFOR) */
u32 spirfor; /* SPI Receive FIFO Occupancy Register (SPIRFOR) */
};
/* xilinx spi priv */
struct xilinx_spi_priv {
struct xilinx_spi_regs *regs;
unsigned int freq;
unsigned int mode;
};
static unsigned long xilinx_spi_base_list[] = CONFIG_SYS_XILINX_SPI_LIST;
static int xilinx_spi_probe(struct udevice *bus)
{
struct xilinx_spi_priv *priv = dev_get_priv(bus);
struct xilinx_spi_regs *regs = priv->regs;
priv->regs = (struct xilinx_spi_regs *)xilinx_spi_base_list[bus->seq];
writel(SPISSR_RESET_VALUE, &regs->srr);
return 0;
}
static void spi_cs_activate(struct udevice *dev, uint cs)
{
struct udevice *bus = dev_get_parent(dev);
struct xilinx_spi_priv *priv = dev_get_priv(bus);
struct xilinx_spi_regs *regs = priv->regs;
writel(SPISSR_ACT(cs), &regs->spissr);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct xilinx_spi_priv *priv = dev_get_priv(bus);
struct xilinx_spi_regs *regs = priv->regs;
writel(SPISSR_OFF, &regs->spissr);
}
static int xilinx_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct xilinx_spi_priv *priv = dev_get_priv(bus);
struct xilinx_spi_regs *regs = priv->regs;
writel(SPISSR_OFF, &regs->spissr);
writel(XILSPI_SPICR_DFLT_ON, &regs->spicr);
return 0;
}
static int xilinx_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev_get_parent(dev);
struct xilinx_spi_priv *priv = dev_get_priv(bus);
struct xilinx_spi_regs *regs = priv->regs;
writel(SPISSR_OFF, &regs->spissr);
writel(XILSPI_SPICR_DFLT_OFF, &regs->spicr);
return 0;
}
static int xilinx_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev_get_parent(dev);
struct xilinx_spi_priv *priv = dev_get_priv(bus);
struct xilinx_spi_regs *regs = priv->regs;
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
/* assume spi core configured to do 8 bit transfers */
unsigned int bytes = bitlen / XILSPI_MAX_XFER_BITS;
const unsigned char *txp = dout;
unsigned char *rxp = din;
unsigned rxecount = 17; /* max. 16 elements in FIFO, leftover 1 */
unsigned global_timeout;
debug("spi_xfer: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n",
bus->seq, slave_plat->cs, bitlen, bytes, flags);
if (bitlen == 0)
goto done;
if (bitlen % XILSPI_MAX_XFER_BITS) {
printf("XILSPI warning: Not a multiple of %d bits\n",
XILSPI_MAX_XFER_BITS);
flags |= SPI_XFER_END;
goto done;
}
/* empty read buffer */
while (rxecount && !(readl(&regs->spisr) & SPISR_RX_EMPTY)) {
readl(&regs->spidrr);
rxecount--;
}
if (!rxecount) {
printf("XILSPI error: Rx buffer not empty\n");
return -1;
}
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev, slave_plat->cs);
/* at least 1usec or greater, leftover 1 */
global_timeout = priv->freq > XILSPI_MAX_XFER_BITS * 1000000 ? 2 :
(XILSPI_MAX_XFER_BITS * 1000000 / priv->freq) + 1;
while (bytes--) {
unsigned timeout = global_timeout;
/* get Tx element from data out buffer and count up */
unsigned char d = txp ? *txp++ : CONFIG_XILINX_SPI_IDLE_VAL;
debug("spi_xfer: tx:%x ", d);
/* write out and wait for processing (receive data) */
writel(d & SPIDTR_8BIT_MASK, &regs->spidtr);
while (timeout && readl(&regs->spisr)
& SPISR_RX_EMPTY) {
timeout--;
udelay(1);
}
if (!timeout) {
printf("XILSPI error: Xfer timeout\n");
return -1;
}
/* read Rx element and push into data in buffer */
d = readl(&regs->spidrr) & SPIDRR_8BIT_MASK;
if (rxp)
*rxp++ = d;
debug("spi_xfer: rx:%x\n", d);
}
done:
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
return 0;
}
static int xilinx_spi_set_speed(struct udevice *bus, uint speed)
{
struct xilinx_spi_priv *priv = dev_get_priv(bus);
priv->freq = speed;
debug("xilinx_spi_set_speed: regs=%p, speed=%d\n", priv->regs,
priv->freq);
return 0;
}
static int xilinx_spi_set_mode(struct udevice *bus, uint mode)
{
struct xilinx_spi_priv *priv = dev_get_priv(bus);
struct xilinx_spi_regs *regs = priv->regs;
uint32_t spicr;
spicr = readl(&regs->spicr);
if (mode & SPI_LSB_FIRST)
spicr |= SPICR_LSB_FIRST;
if (mode & SPI_CPHA)
spicr |= SPICR_CPHA;
if (mode & SPI_CPOL)
spicr |= SPICR_CPOL;
if (mode & SPI_LOOP)
spicr |= SPICR_LOOP;
writel(spicr, &regs->spicr);
priv->mode = mode;
debug("xilinx_spi_set_mode: regs=%p, mode=%d\n", priv->regs,
priv->mode);
return 0;
}
static const struct dm_spi_ops xilinx_spi_ops = {
.claim_bus = xilinx_spi_claim_bus,
.release_bus = xilinx_spi_release_bus,
.xfer = xilinx_spi_xfer,
.set_speed = xilinx_spi_set_speed,
.set_mode = xilinx_spi_set_mode,
};
static const struct udevice_id xilinx_spi_ids[] = {
{ .compatible = "xlnx,xps-spi-2.00.a" },
{ .compatible = "xlnx,xps-spi-2.00.b" },
{ }
};
U_BOOT_DRIVER(xilinx_spi) = {
.name = "xilinx_spi",
.id = UCLASS_SPI,
.of_match = xilinx_spi_ids,
.ops = &xilinx_spi_ops,
.priv_auto_alloc_size = sizeof(struct xilinx_spi_priv),
.probe = xilinx_spi_probe,
};

View File

@@ -0,0 +1,630 @@
/*
* (C) Copyright 2013 Xilinx, Inc.
* (C) Copyright 2015 Jagan Teki <jteki@openedev.com>
*
* Xilinx Zynq Quad-SPI(QSPI) controller driver (master mode only)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
/* zynq qspi register bit masks ZYNQ_QSPI_<REG>_<BIT>_MASK */
#define ZYNQ_QSPI_CR_IFMODE_MASK BIT(31) /* Flash intrface mode*/
#define ZYNQ_QSPI_CR_MSA_MASK BIT(15) /* Manual start enb */
#define ZYNQ_QSPI_CR_MCS_MASK BIT(14) /* Manual chip select */
#define ZYNQ_QSPI_CR_PCS_MASK BIT(10) /* Peri chip select */
#define ZYNQ_QSPI_CR_FW_MASK GENMASK(7, 6) /* FIFO width */
#define ZYNQ_QSPI_CR_SS_MASK GENMASK(13, 10) /* Slave Select */
#define ZYNQ_QSPI_CR_BAUD_MASK GENMASK(5, 3) /* Baud rate div */
#define ZYNQ_QSPI_CR_CPHA_MASK BIT(2) /* Clock phase */
#define ZYNQ_QSPI_CR_CPOL_MASK BIT(1) /* Clock polarity */
#define ZYNQ_QSPI_CR_MSTREN_MASK BIT(0) /* Mode select */
#define ZYNQ_QSPI_IXR_RXNEMPTY_MASK BIT(4) /* RX_FIFO_not_empty */
#define ZYNQ_QSPI_IXR_TXOW_MASK BIT(2) /* TX_FIFO_not_full */
#define ZYNQ_QSPI_IXR_ALL_MASK GENMASK(6, 0) /* All IXR bits */
#define ZYNQ_QSPI_ENR_SPI_EN_MASK BIT(0) /* SPI Enable */
#define ZYNQ_QSPI_LQSPICFG_LQMODE_MASK BIT(31) /* Linear QSPI Mode */
/* zynq qspi Transmit Data Register */
#define ZYNQ_QSPI_TXD_00_00_OFFSET 0x1C /* Transmit 4-byte inst */
#define ZYNQ_QSPI_TXD_00_01_OFFSET 0x80 /* Transmit 1-byte inst */
#define ZYNQ_QSPI_TXD_00_10_OFFSET 0x84 /* Transmit 2-byte inst */
#define ZYNQ_QSPI_TXD_00_11_OFFSET 0x88 /* Transmit 3-byte inst */
#define ZYNQ_QSPI_TXFIFO_THRESHOLD 1 /* Tx FIFO threshold level*/
#define ZYNQ_QSPI_RXFIFO_THRESHOLD 32 /* Rx FIFO threshold level */
#define ZYNQ_QSPI_CR_BAUD_MAX 8 /* Baud rate divisor max val */
#define ZYNQ_QSPI_CR_BAUD_SHIFT 3 /* Baud rate divisor shift */
#define ZYNQ_QSPI_CR_SS_SHIFT 10 /* Slave select shift */
#define ZYNQ_QSPI_FIFO_DEPTH 63
#ifndef CONFIG_SYS_ZYNQ_QSPI_WAIT
#define CONFIG_SYS_ZYNQ_QSPI_WAIT CONFIG_SYS_HZ/100 /* 10 ms */
#endif
/* zynq qspi register set */
struct zynq_qspi_regs {
u32 cr; /* 0x00 */
u32 isr; /* 0x04 */
u32 ier; /* 0x08 */
u32 idr; /* 0x0C */
u32 imr; /* 0x10 */
u32 enr; /* 0x14 */
u32 dr; /* 0x18 */
u32 txd0r; /* 0x1C */
u32 drxr; /* 0x20 */
u32 sicr; /* 0x24 */
u32 txftr; /* 0x28 */
u32 rxftr; /* 0x2C */
u32 gpior; /* 0x30 */
u32 reserved0[19];
u32 txd1r; /* 0x80 */
u32 txd2r; /* 0x84 */
u32 txd3r; /* 0x88 */
u32 reserved1[5];
u32 lqspicfg; /* 0xA0 */
u32 lqspists; /* 0xA4 */
};
/* zynq qspi platform data */
struct zynq_qspi_platdata {
struct zynq_qspi_regs *regs;
u32 frequency; /* input frequency */
u32 speed_hz;
};
/* zynq qspi priv */
struct zynq_qspi_priv {
struct zynq_qspi_regs *regs;
u8 cs;
u8 mode;
u8 fifo_depth;
u32 freq; /* required frequency */
const void *tx_buf;
void *rx_buf;
unsigned len;
int bytes_to_transfer;
int bytes_to_receive;
unsigned int is_inst;
unsigned cs_change:1;
};
static int zynq_qspi_ofdata_to_platdata(struct udevice *bus)
{
struct zynq_qspi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->regs = (struct zynq_qspi_regs *)fdtdec_get_addr(blob,
node, "reg");
/* FIXME: Use 166MHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
166666666);
plat->speed_hz = plat->frequency / 2;
debug("%s: regs=%p max-frequency=%d\n", __func__,
plat->regs, plat->frequency);
return 0;
}
static void zynq_qspi_init_hw(struct zynq_qspi_priv *priv)
{
struct zynq_qspi_regs *regs = priv->regs;
u32 confr;
/* Disable QSPI */
writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
/* Disable Interrupts */
writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->idr);
/* Clear the TX and RX threshold reg */
writel(ZYNQ_QSPI_TXFIFO_THRESHOLD, &regs->txftr);
writel(ZYNQ_QSPI_RXFIFO_THRESHOLD, &regs->rxftr);
/* Clear the RX FIFO */
while (readl(&regs->isr) & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
readl(&regs->drxr);
/* Clear Interrupts */
writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->isr);
/* Manual slave select and Auto start */
confr = readl(&regs->cr);
confr &= ~ZYNQ_QSPI_CR_MSA_MASK;
confr |= ZYNQ_QSPI_CR_IFMODE_MASK | ZYNQ_QSPI_CR_MCS_MASK |
ZYNQ_QSPI_CR_PCS_MASK | ZYNQ_QSPI_CR_FW_MASK |
ZYNQ_QSPI_CR_MSTREN_MASK;
writel(confr, &regs->cr);
/* Disable the LQSPI feature */
confr = readl(&regs->lqspicfg);
confr &= ~ZYNQ_QSPI_LQSPICFG_LQMODE_MASK;
writel(confr, &regs->lqspicfg);
/* Enable SPI */
writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
}
static int zynq_qspi_probe(struct udevice *bus)
{
struct zynq_qspi_platdata *plat = dev_get_platdata(bus);
struct zynq_qspi_priv *priv = dev_get_priv(bus);
priv->regs = plat->regs;
priv->fifo_depth = ZYNQ_QSPI_FIFO_DEPTH;
/* init the zynq spi hw */
zynq_qspi_init_hw(priv);
return 0;
}
/*
* zynq_qspi_read_data - Copy data to RX buffer
* @zqspi: Pointer to the zynq_qspi structure
* @data: The 32 bit variable where data is stored
* @size: Number of bytes to be copied from data to RX buffer
*/
static void zynq_qspi_read_data(struct zynq_qspi_priv *priv, u32 data, u8 size)
{
u8 byte3;
debug("%s: data 0x%04x rx_buf addr: 0x%08x size %d\n", __func__ ,
data, (unsigned)(priv->rx_buf), size);
if (priv->rx_buf) {
switch (size) {
case 1:
*((u8 *)priv->rx_buf) = data;
priv->rx_buf += 1;
break;
case 2:
*((u16 *)priv->rx_buf) = data;
priv->rx_buf += 2;
break;
case 3:
*((u16 *)priv->rx_buf) = data;
priv->rx_buf += 2;
byte3 = (u8)(data >> 16);
*((u8 *)priv->rx_buf) = byte3;
priv->rx_buf += 1;
break;
case 4:
/* Can not assume word aligned buffer */
memcpy(priv->rx_buf, &data, size);
priv->rx_buf += 4;
break;
default:
/* This will never execute */
break;
}
}
priv->bytes_to_receive -= size;
if (priv->bytes_to_receive < 0)
priv->bytes_to_receive = 0;
}
/*
* zynq_qspi_write_data - Copy data from TX buffer
* @zqspi: Pointer to the zynq_qspi structure
* @data: Pointer to the 32 bit variable where data is to be copied
* @size: Number of bytes to be copied from TX buffer to data
*/
static void zynq_qspi_write_data(struct zynq_qspi_priv *priv,
u32 *data, u8 size)
{
if (priv->tx_buf) {
switch (size) {
case 1:
*data = *((u8 *)priv->tx_buf);
priv->tx_buf += 1;
*data |= 0xFFFFFF00;
break;
case 2:
*data = *((u16 *)priv->tx_buf);
priv->tx_buf += 2;
*data |= 0xFFFF0000;
break;
case 3:
*data = *((u16 *)priv->tx_buf);
priv->tx_buf += 2;
*data |= (*((u8 *)priv->tx_buf) << 16);
priv->tx_buf += 1;
*data |= 0xFF000000;
break;
case 4:
/* Can not assume word aligned buffer */
memcpy(data, priv->tx_buf, size);
priv->tx_buf += 4;
break;
default:
/* This will never execute */
break;
}
} else {
*data = 0;
}
debug("%s: data 0x%08x tx_buf addr: 0x%08x size %d\n", __func__,
*data, (u32)priv->tx_buf, size);
priv->bytes_to_transfer -= size;
if (priv->bytes_to_transfer < 0)
priv->bytes_to_transfer = 0;
}
static void zynq_qspi_chipselect(struct zynq_qspi_priv *priv, int is_on)
{
u32 confr;
struct zynq_qspi_regs *regs = priv->regs;
confr = readl(&regs->cr);
if (is_on) {
/* Select the slave */
confr &= ~ZYNQ_QSPI_CR_SS_MASK;
confr |= (~(1 << priv->cs) << ZYNQ_QSPI_CR_SS_SHIFT) &
ZYNQ_QSPI_CR_SS_MASK;
} else
/* Deselect the slave */
confr |= ZYNQ_QSPI_CR_SS_MASK;
writel(confr, &regs->cr);
}
/*
* zynq_qspi_fill_tx_fifo - Fills the TX FIFO with as many bytes as possible
* @zqspi: Pointer to the zynq_qspi structure
*/
static void zynq_qspi_fill_tx_fifo(struct zynq_qspi_priv *priv, u32 size)
{
u32 data = 0;
u32 fifocount = 0;
unsigned len, offset;
struct zynq_qspi_regs *regs = priv->regs;
static const unsigned offsets[4] = {
ZYNQ_QSPI_TXD_00_00_OFFSET, ZYNQ_QSPI_TXD_00_01_OFFSET,
ZYNQ_QSPI_TXD_00_10_OFFSET, ZYNQ_QSPI_TXD_00_11_OFFSET };
while ((fifocount < size) &&
(priv->bytes_to_transfer > 0)) {
if (priv->bytes_to_transfer >= 4) {
if (priv->tx_buf) {
memcpy(&data, priv->tx_buf, 4);
priv->tx_buf += 4;
} else {
data = 0;
}
writel(data, &regs->txd0r);
priv->bytes_to_transfer -= 4;
fifocount++;
} else {
/* Write TXD1, TXD2, TXD3 only if TxFIFO is empty. */
if (!(readl(&regs->isr)
& ZYNQ_QSPI_IXR_TXOW_MASK) &&
!priv->rx_buf)
return;
len = priv->bytes_to_transfer;
zynq_qspi_write_data(priv, &data, len);
offset = (priv->rx_buf) ? offsets[0] : offsets[len];
writel(data, &regs->cr + (offset / 4));
}
}
}
/*
* zynq_qspi_irq_poll - Interrupt service routine of the QSPI controller
* @zqspi: Pointer to the zynq_qspi structure
*
* This function handles TX empty and Mode Fault interrupts only.
* On TX empty interrupt this function reads the received data from RX FIFO and
* fills the TX FIFO if there is any data remaining to be transferred.
* On Mode Fault interrupt this function indicates that transfer is completed,
* the SPI subsystem will identify the error as the remaining bytes to be
* transferred is non-zero.
*
* returns: 0 for poll timeout
* 1 transfer operation complete
*/
static int zynq_qspi_irq_poll(struct zynq_qspi_priv *priv)
{
struct zynq_qspi_regs *regs = priv->regs;
u32 rxindex = 0;
u32 rxcount;
u32 status, timeout;
/* Poll until any of the interrupt status bits are set */
timeout = get_timer(0);
do {
status = readl(&regs->isr);
} while ((status == 0) &&
(get_timer(timeout) < CONFIG_SYS_ZYNQ_QSPI_WAIT));
if (status == 0) {
printf("zynq_qspi_irq_poll: Timeout!\n");
return -ETIMEDOUT;
}
writel(status, &regs->isr);
/* Disable all interrupts */
writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->idr);
if ((status & ZYNQ_QSPI_IXR_TXOW_MASK) ||
(status & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)) {
/*
* This bit is set when Tx FIFO has < THRESHOLD entries. We have
* the THRESHOLD value set to 1, so this bit indicates Tx FIFO
* is empty
*/
rxcount = priv->bytes_to_receive - priv->bytes_to_transfer;
rxcount = (rxcount % 4) ? ((rxcount/4)+1) : (rxcount/4);
while ((rxindex < rxcount) &&
(rxindex < ZYNQ_QSPI_RXFIFO_THRESHOLD)) {
/* Read out the data from the RX FIFO */
u32 data;
data = readl(&regs->drxr);
if (priv->bytes_to_receive >= 4) {
if (priv->rx_buf) {
memcpy(priv->rx_buf, &data, 4);
priv->rx_buf += 4;
}
priv->bytes_to_receive -= 4;
} else {
zynq_qspi_read_data(priv, data,
priv->bytes_to_receive);
}
rxindex++;
}
if (priv->bytes_to_transfer) {
/* There is more data to send */
zynq_qspi_fill_tx_fifo(priv,
ZYNQ_QSPI_RXFIFO_THRESHOLD);
writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->ier);
} else {
/*
* If transfer and receive is completed then only send
* complete signal
*/
if (!priv->bytes_to_receive) {
/* return operation complete */
writel(ZYNQ_QSPI_IXR_ALL_MASK,
&regs->idr);
return 1;
}
}
}
return 0;
}
/*
* zynq_qspi_start_transfer - Initiates the QSPI transfer
* @qspi: Pointer to the spi_device structure
* @transfer: Pointer to the spi_transfer structure which provide information
* about next transfer parameters
*
* This function fills the TX FIFO, starts the QSPI transfer, and waits for the
* transfer to be completed.
*
* returns: Number of bytes transferred in the last transfer
*/
static int zynq_qspi_start_transfer(struct zynq_qspi_priv *priv)
{
u32 data = 0;
struct zynq_qspi_regs *regs = priv->regs;
debug("%s: qspi: 0x%08x transfer: 0x%08x len: %d\n", __func__,
(u32)priv, (u32)priv, priv->len);
priv->bytes_to_transfer = priv->len;
priv->bytes_to_receive = priv->len;
if (priv->len < 4)
zynq_qspi_fill_tx_fifo(priv, priv->len);
else
zynq_qspi_fill_tx_fifo(priv, priv->fifo_depth);
writel(ZYNQ_QSPI_IXR_ALL_MASK, &regs->ier);
/* wait for completion */
do {
data = zynq_qspi_irq_poll(priv);
} while (data == 0);
return (priv->len) - (priv->bytes_to_transfer);
}
static int zynq_qspi_transfer(struct zynq_qspi_priv *priv)
{
unsigned cs_change = 1;
int status = 0;
while (1) {
/* Select the chip if required */
if (cs_change)
zynq_qspi_chipselect(priv, 1);
cs_change = priv->cs_change;
if (!priv->tx_buf && !priv->rx_buf && priv->len) {
status = -1;
break;
}
/* Request the transfer */
if (priv->len) {
status = zynq_qspi_start_transfer(priv);
priv->is_inst = 0;
}
if (status != priv->len) {
if (status > 0)
status = -EMSGSIZE;
debug("zynq_qspi_transfer:%d len:%d\n",
status, priv->len);
break;
}
status = 0;
if (cs_change)
/* Deselect the chip */
zynq_qspi_chipselect(priv, 0);
break;
}
return 0;
}
static int zynq_qspi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct zynq_qspi_priv *priv = dev_get_priv(bus);
struct zynq_qspi_regs *regs = priv->regs;
writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
return 0;
}
static int zynq_qspi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct zynq_qspi_priv *priv = dev_get_priv(bus);
struct zynq_qspi_regs *regs = priv->regs;
writel(~ZYNQ_QSPI_ENR_SPI_EN_MASK, &regs->enr);
return 0;
}
static int zynq_qspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct zynq_qspi_priv *priv = dev_get_priv(bus);
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
priv->cs = slave_plat->cs;
priv->tx_buf = dout;
priv->rx_buf = din;
priv->len = bitlen / 8;
debug("zynq_qspi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
bus->seq, slave_plat->cs, bitlen, priv->len, flags);
/*
* Festering sore.
* Assume that the beginning of a transfer with bits to
* transmit must contain a device command.
*/
if (dout && flags & SPI_XFER_BEGIN)
priv->is_inst = 1;
else
priv->is_inst = 0;
if (flags & SPI_XFER_END)
priv->cs_change = 1;
else
priv->cs_change = 0;
zynq_qspi_transfer(priv);
return 0;
}
static int zynq_qspi_set_speed(struct udevice *bus, uint speed)
{
struct zynq_qspi_platdata *plat = bus->platdata;
struct zynq_qspi_priv *priv = dev_get_priv(bus);
struct zynq_qspi_regs *regs = priv->regs;
uint32_t confr;
u8 baud_rate_val = 0;
if (speed > plat->frequency)
speed = plat->frequency;
/* Set the clock frequency */
confr = readl(&regs->cr);
if (speed == 0) {
/* Set baudrate x8, if the freq is 0 */
baud_rate_val = 0x2;
} else if (plat->speed_hz != speed) {
while ((baud_rate_val < ZYNQ_QSPI_CR_BAUD_MAX) &&
((plat->frequency /
(2 << baud_rate_val)) > speed))
baud_rate_val++;
plat->speed_hz = speed / (2 << baud_rate_val);
}
confr &= ~ZYNQ_QSPI_CR_BAUD_MASK;
confr |= (baud_rate_val << ZYNQ_QSPI_CR_BAUD_SHIFT);
writel(confr, &regs->cr);
priv->freq = speed;
debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq);
return 0;
}
static int zynq_qspi_set_mode(struct udevice *bus, uint mode)
{
struct zynq_qspi_priv *priv = dev_get_priv(bus);
struct zynq_qspi_regs *regs = priv->regs;
uint32_t confr;
/* Set the SPI Clock phase and polarities */
confr = readl(&regs->cr);
confr &= ~(ZYNQ_QSPI_CR_CPHA_MASK | ZYNQ_QSPI_CR_CPOL_MASK);
if (mode & SPI_CPHA)
confr |= ZYNQ_QSPI_CR_CPHA_MASK;
if (mode & SPI_CPOL)
confr |= ZYNQ_QSPI_CR_CPOL_MASK;
writel(confr, &regs->cr);
priv->mode = mode;
debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops zynq_qspi_ops = {
.claim_bus = zynq_qspi_claim_bus,
.release_bus = zynq_qspi_release_bus,
.xfer = zynq_qspi_xfer,
.set_speed = zynq_qspi_set_speed,
.set_mode = zynq_qspi_set_mode,
};
static const struct udevice_id zynq_qspi_ids[] = {
{ .compatible = "xlnx,zynq-qspi-1.0" },
{ }
};
U_BOOT_DRIVER(zynq_qspi) = {
.name = "zynq_qspi",
.id = UCLASS_SPI,
.of_match = zynq_qspi_ids,
.ops = &zynq_qspi_ops,
.ofdata_to_platdata = zynq_qspi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct zynq_qspi_platdata),
.priv_auto_alloc_size = sizeof(struct zynq_qspi_priv),
.probe = zynq_qspi_probe,
};

View File

@@ -0,0 +1,329 @@
/*
* (C) Copyright 2013 Xilinx, Inc.
* (C) Copyright 2015 Jagan Teki <jteki@openedev.com>
*
* Xilinx Zynq PS SPI controller driver (master mode only)
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <malloc.h>
#include <spi.h>
#include <asm/io.h>
DECLARE_GLOBAL_DATA_PTR;
/* zynq spi register bit masks ZYNQ_SPI_<REG>_<BIT>_MASK */
#define ZYNQ_SPI_CR_MSA_MASK BIT(15) /* Manual start enb */
#define ZYNQ_SPI_CR_MCS_MASK BIT(14) /* Manual chip select */
#define ZYNQ_SPI_CR_CS_MASK GENMASK(13, 10) /* Chip select */
#define ZYNQ_SPI_CR_BAUD_MASK GENMASK(5, 3) /* Baud rate div */
#define ZYNQ_SPI_CR_CPHA_MASK BIT(2) /* Clock phase */
#define ZYNQ_SPI_CR_CPOL_MASK BIT(1) /* Clock polarity */
#define ZYNQ_SPI_CR_MSTREN_MASK BIT(0) /* Mode select */
#define ZYNQ_SPI_IXR_RXNEMPTY_MASK BIT(4) /* RX_FIFO_not_empty */
#define ZYNQ_SPI_IXR_TXOW_MASK BIT(2) /* TX_FIFO_not_full */
#define ZYNQ_SPI_IXR_ALL_MASK GENMASK(6, 0) /* All IXR bits */
#define ZYNQ_SPI_ENR_SPI_EN_MASK BIT(0) /* SPI Enable */
#define ZYNQ_SPI_CR_BAUD_MAX 8 /* Baud rate divisor max val */
#define ZYNQ_SPI_CR_BAUD_SHIFT 3 /* Baud rate divisor shift */
#define ZYNQ_SPI_CR_SS_SHIFT 10 /* Slave select shift */
#define ZYNQ_SPI_FIFO_DEPTH 128
#ifndef CONFIG_SYS_ZYNQ_SPI_WAIT
#define CONFIG_SYS_ZYNQ_SPI_WAIT (CONFIG_SYS_HZ/100) /* 10 ms */
#endif
/* zynq spi register set */
struct zynq_spi_regs {
u32 cr; /* 0x00 */
u32 isr; /* 0x04 */
u32 ier; /* 0x08 */
u32 idr; /* 0x0C */
u32 imr; /* 0x10 */
u32 enr; /* 0x14 */
u32 dr; /* 0x18 */
u32 txdr; /* 0x1C */
u32 rxdr; /* 0x20 */
};
/* zynq spi platform data */
struct zynq_spi_platdata {
struct zynq_spi_regs *regs;
u32 frequency; /* input frequency */
u32 speed_hz;
};
/* zynq spi priv */
struct zynq_spi_priv {
struct zynq_spi_regs *regs;
u8 cs;
u8 mode;
u8 fifo_depth;
u32 freq; /* required frequency */
};
static int zynq_spi_ofdata_to_platdata(struct udevice *bus)
{
struct zynq_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
plat->regs = (struct zynq_spi_regs *)dev_get_addr(bus);
/* FIXME: Use 250MHz as a suitable default */
plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency",
250000000);
plat->speed_hz = plat->frequency / 2;
debug("%s: regs=%p max-frequency=%d\n", __func__,
plat->regs, plat->frequency);
return 0;
}
static void zynq_spi_init_hw(struct zynq_spi_priv *priv)
{
struct zynq_spi_regs *regs = priv->regs;
u32 confr;
/* Disable SPI */
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &regs->enr);
/* Disable Interrupts */
writel(ZYNQ_SPI_IXR_ALL_MASK, &regs->idr);
/* Clear RX FIFO */
while (readl(&regs->isr) &
ZYNQ_SPI_IXR_RXNEMPTY_MASK)
readl(&regs->rxdr);
/* Clear Interrupts */
writel(ZYNQ_SPI_IXR_ALL_MASK, &regs->isr);
/* Manual slave select and Auto start */
confr = ZYNQ_SPI_CR_MCS_MASK | ZYNQ_SPI_CR_CS_MASK |
ZYNQ_SPI_CR_MSTREN_MASK;
confr &= ~ZYNQ_SPI_CR_MSA_MASK;
writel(confr, &regs->cr);
/* Enable SPI */
writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &regs->enr);
}
static int zynq_spi_probe(struct udevice *bus)
{
struct zynq_spi_platdata *plat = dev_get_platdata(bus);
struct zynq_spi_priv *priv = dev_get_priv(bus);
priv->regs = plat->regs;
priv->fifo_depth = ZYNQ_SPI_FIFO_DEPTH;
/* init the zynq spi hw */
zynq_spi_init_hw(priv);
return 0;
}
static void spi_cs_activate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct zynq_spi_priv *priv = dev_get_priv(bus);
struct zynq_spi_regs *regs = priv->regs;
u32 cr;
clrbits_le32(&regs->cr, ZYNQ_SPI_CR_CS_MASK);
cr = readl(&regs->cr);
/*
* CS cal logic: CS[13:10]
* xxx0 - cs0
* xx01 - cs1
* x011 - cs2
*/
cr |= (~(1 << priv->cs) << ZYNQ_SPI_CR_SS_SHIFT) & ZYNQ_SPI_CR_CS_MASK;
writel(cr, &regs->cr);
}
static void spi_cs_deactivate(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct zynq_spi_priv *priv = dev_get_priv(bus);
struct zynq_spi_regs *regs = priv->regs;
setbits_le32(&regs->cr, ZYNQ_SPI_CR_CS_MASK);
}
static int zynq_spi_claim_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct zynq_spi_priv *priv = dev_get_priv(bus);
struct zynq_spi_regs *regs = priv->regs;
writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &regs->enr);
return 0;
}
static int zynq_spi_release_bus(struct udevice *dev)
{
struct udevice *bus = dev->parent;
struct zynq_spi_priv *priv = dev_get_priv(bus);
struct zynq_spi_regs *regs = priv->regs;
writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &regs->enr);
return 0;
}
static int zynq_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct zynq_spi_priv *priv = dev_get_priv(bus);
struct zynq_spi_regs *regs = priv->regs;
struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev);
u32 len = bitlen / 8;
u32 tx_len = len, rx_len = len, tx_tvl;
const u8 *tx_buf = dout;
u8 *rx_buf = din, buf;
u32 ts, status;
debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n",
bus->seq, slave_plat->cs, bitlen, len, flags);
if (bitlen % 8) {
debug("spi_xfer: Non byte aligned SPI transfer\n");
return -1;
}
priv->cs = slave_plat->cs;
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(dev);
while (rx_len > 0) {
/* Write the data into TX FIFO - tx threshold is fifo_depth */
tx_tvl = 0;
while ((tx_tvl < priv->fifo_depth) && tx_len) {
if (tx_buf)
buf = *tx_buf++;
else
buf = 0;
writel(buf, &regs->txdr);
tx_len--;
tx_tvl++;
}
/* Check TX FIFO completion */
ts = get_timer(0);
status = readl(&regs->isr);
while (!(status & ZYNQ_SPI_IXR_TXOW_MASK)) {
if (get_timer(ts) > CONFIG_SYS_ZYNQ_SPI_WAIT) {
printf("spi_xfer: Timeout! TX FIFO not full\n");
return -1;
}
status = readl(&regs->isr);
}
/* Read the data from RX FIFO */
status = readl(&regs->isr);
while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) {
buf = readl(&regs->rxdr);
if (rx_buf)
*rx_buf++ = buf;
status = readl(&regs->isr);
rx_len--;
}
}
if (flags & SPI_XFER_END)
spi_cs_deactivate(dev);
return 0;
}
static int zynq_spi_set_speed(struct udevice *bus, uint speed)
{
struct zynq_spi_platdata *plat = bus->platdata;
struct zynq_spi_priv *priv = dev_get_priv(bus);
struct zynq_spi_regs *regs = priv->regs;
uint32_t confr;
u8 baud_rate_val = 0;
if (speed > plat->frequency)
speed = plat->frequency;
/* Set the clock frequency */
confr = readl(&regs->cr);
if (speed == 0) {
/* Set baudrate x8, if the freq is 0 */
baud_rate_val = 0x2;
} else if (plat->speed_hz != speed) {
while ((baud_rate_val < ZYNQ_SPI_CR_BAUD_MAX) &&
((plat->frequency /
(2 << baud_rate_val)) > speed))
baud_rate_val++;
plat->speed_hz = speed / (2 << baud_rate_val);
}
confr &= ~ZYNQ_SPI_CR_BAUD_MASK;
confr |= (baud_rate_val << ZYNQ_SPI_CR_BAUD_SHIFT);
writel(confr, &regs->cr);
priv->freq = speed;
debug("zynq_spi_set_speed: regs=%p, speed=%d\n",
priv->regs, priv->freq);
return 0;
}
static int zynq_spi_set_mode(struct udevice *bus, uint mode)
{
struct zynq_spi_priv *priv = dev_get_priv(bus);
struct zynq_spi_regs *regs = priv->regs;
uint32_t confr;
/* Set the SPI Clock phase and polarities */
confr = readl(&regs->cr);
confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK);
if (mode & SPI_CPHA)
confr |= ZYNQ_SPI_CR_CPHA_MASK;
if (mode & SPI_CPOL)
confr |= ZYNQ_SPI_CR_CPOL_MASK;
writel(confr, &regs->cr);
priv->mode = mode;
debug("zynq_spi_set_mode: regs=%p, mode=%d\n", priv->regs, priv->mode);
return 0;
}
static const struct dm_spi_ops zynq_spi_ops = {
.claim_bus = zynq_spi_claim_bus,
.release_bus = zynq_spi_release_bus,
.xfer = zynq_spi_xfer,
.set_speed = zynq_spi_set_speed,
.set_mode = zynq_spi_set_mode,
};
static const struct udevice_id zynq_spi_ids[] = {
{ .compatible = "xlnx,zynq-spi-r1p6" },
{ .compatible = "cdns,spi-r1p6" },
{ }
};
U_BOOT_DRIVER(zynq_spi) = {
.name = "zynq_spi",
.id = UCLASS_SPI,
.of_match = zynq_spi_ids,
.ops = &zynq_spi_ops,
.ofdata_to_platdata = zynq_spi_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct zynq_spi_platdata),
.priv_auto_alloc_size = sizeof(struct zynq_spi_priv),
.probe = zynq_spi_probe,
};