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:
197
u-boot/drivers/spi/Kconfig
Normal file
197
u-boot/drivers/spi/Kconfig
Normal 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"
|
||||
55
u-boot/drivers/spi/Makefile
Normal file
55
u-boot/drivers/spi/Makefile
Normal 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
|
||||
210
u-boot/drivers/spi/altera_spi.c
Normal file
210
u-boot/drivers/spi/altera_spi.c
Normal 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, ®s->slave_sel);
|
||||
writel(ALTERA_SPI_CONTROL_SSO_MSK, ®s->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, ®s->control);
|
||||
writel(0, ®s->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, ®s->control);
|
||||
writel(0, ®s->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, ®s->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(®s->status) & ALTERA_SPI_STATUS_RRDY_MSK)
|
||||
readl(®s->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, ®s->txdata);
|
||||
|
||||
start = get_timer(0);
|
||||
while (1) {
|
||||
reg = readl(®s->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(®s->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,
|
||||
};
|
||||
203
u-boot/drivers/spi/armada100_spi.c
Normal file
203
u-boot/drivers/spi/armada100_spi.c
Normal 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;
|
||||
}
|
||||
228
u-boot/drivers/spi/ath79_spi.c
Normal file
228
u-boot/drivers/spi/ath79_spi.c
Normal 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,
|
||||
};
|
||||
184
u-boot/drivers/spi/atmel_dataflash_spi.c
Normal file
184
u-boot/drivers/spi/atmel_dataflash_spi.c
Normal 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;
|
||||
}
|
||||
211
u-boot/drivers/spi/atmel_spi.c
Normal file
211
u-boot/drivers/spi/atmel_spi.c
Normal 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;
|
||||
}
|
||||
100
u-boot/drivers/spi/atmel_spi.h
Normal file
100
u-boot/drivers/spi/atmel_spi.h
Normal 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
|
||||
309
u-boot/drivers/spi/bfin_spi.c
Normal file
309
u-boot/drivers/spi/bfin_spi.c
Normal 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;
|
||||
}
|
||||
305
u-boot/drivers/spi/bfin_spi6xx.c
Normal file
305
u-boot/drivers/spi/bfin_spi6xx.c
Normal 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;
|
||||
}
|
||||
352
u-boot/drivers/spi/cadence_qspi.c
Normal file
352
u-boot/drivers/spi/cadence_qspi.c
Normal 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,
|
||||
};
|
||||
78
u-boot/drivers/spi/cadence_qspi.h
Normal file
78
u-boot/drivers/spi/cadence_qspi.h
Normal 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__ */
|
||||
808
u-boot/drivers/spi/cadence_qspi_apb.c
Normal file
808
u-boot/drivers/spi/cadence_qspi_apb.c
Normal 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, ®, read_len);
|
||||
rxbuf += read_len;
|
||||
|
||||
if (rxlen > 4) {
|
||||
reg = readl(reg_base + CQSPI_REG_CMDREADDATAUPPER);
|
||||
|
||||
read_len = rxlen - read_len;
|
||||
memcpy(rxbuf, ®, read_len);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* For commands: WRSR, WREN, WRDI, CHIP_ERASE, BE, etc. */
|
||||
int cadence_qspi_apb_command_write(void *reg_base, unsigned int cmdlen,
|
||||
const u8 *cmdbuf, unsigned int txlen, const u8 *txbuf)
|
||||
{
|
||||
unsigned int reg = 0;
|
||||
unsigned int addr_value;
|
||||
unsigned int wr_data;
|
||||
unsigned int wr_len;
|
||||
|
||||
if (!cmdlen || cmdlen > 5 || txlen > 8 || cmdbuf == NULL) {
|
||||
printf("QSPI: Invalid input arguments cmdlen %d txlen %d\n",
|
||||
cmdlen, txlen);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
reg |= cmdbuf[0] << CQSPI_REG_CMDCTRL_OPCODE_LSB;
|
||||
|
||||
if (cmdlen == 4 || cmdlen == 5) {
|
||||
/* Command with address */
|
||||
reg |= (0x1 << CQSPI_REG_CMDCTRL_ADDR_EN_LSB);
|
||||
/* Number of bytes to write. */
|
||||
reg |= ((cmdlen - 2) & CQSPI_REG_CMDCTRL_ADD_BYTES_MASK)
|
||||
<< CQSPI_REG_CMDCTRL_ADD_BYTES_LSB;
|
||||
/* Get address */
|
||||
addr_value = cadence_qspi_apb_cmd2addr(&cmdbuf[1],
|
||||
cmdlen >= 5 ? 4 : 3);
|
||||
|
||||
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);
|
||||
}
|
||||
354
u-boot/drivers/spi/cf_qspi.c
Normal file
354
u-boot/drivers/spi/cf_qspi.c
Normal 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
348
u-boot/drivers/spi/cf_spi.c
Normal 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 */
|
||||
427
u-boot/drivers/spi/davinci_spi.c
Normal file
427
u-boot/drivers/spi/davinci_spi.c
Normal 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;
|
||||
}
|
||||
425
u-boot/drivers/spi/designware_spi.c
Normal file
425
u-boot/drivers/spi/designware_spi.c
Normal 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,
|
||||
};
|
||||
272
u-boot/drivers/spi/ep93xx_spi.c
Normal file
272
u-boot/drivers/spi/ep93xx_spi.c
Normal 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;
|
||||
}
|
||||
431
u-boot/drivers/spi/exynos_spi.c
Normal file
431
u-boot/drivers/spi/exynos_spi.c
Normal 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(®s->ch_cfg, SPI_CH_HS_EN, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
setbits_le32(®s->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(®s->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(®s->mode_cfg,
|
||||
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
|
||||
count /= 4;
|
||||
setbits_le32(®s->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(®s->mode_cfg,
|
||||
SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
|
||||
writel(0, ®s->swap_cfg);
|
||||
}
|
||||
|
||||
assert(count && count < (1 << 16));
|
||||
setbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
clrbits_le32(®s->ch_cfg, SPI_CH_RST);
|
||||
|
||||
writel(count | SPI_PACKET_CNT_EN, ®s->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, ®s->tx_data);
|
||||
out_bytes -= step;
|
||||
if (txp)
|
||||
txp += step;
|
||||
tx_lvl += step;
|
||||
}
|
||||
if (rx_lvl >= step) {
|
||||
while (rx_lvl >= step) {
|
||||
temp = readl(®s->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,
|
||||
};
|
||||
737
u-boot/drivers/spi/fsl_dspi.c
Normal file
737
u-boot/drivers/spi/fsl_dspi.c
Normal 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
|
||||
389
u-boot/drivers/spi/fsl_espi.c
Normal file
389
u-boot/drivers/spi/fsl_espi.c
Normal 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);
|
||||
}
|
||||
1201
u-boot/drivers/spi/fsl_qspi.c
Normal file
1201
u-boot/drivers/spi/fsl_qspi.c
Normal file
File diff suppressed because it is too large
Load Diff
142
u-boot/drivers/spi/fsl_qspi.h
Normal file
142
u-boot/drivers/spi/fsl_qspi.h
Normal 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
705
u-boot/drivers/spi/ich.c
Normal 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
161
u-boot/drivers/spi/ich.h
Normal 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_ */
|
||||
348
u-boot/drivers/spi/kirkwood_spi.c
Normal file
348
u-boot/drivers/spi/kirkwood_spi.c
Normal 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(®->ctrl, KWSPI_CSN_ACT);
|
||||
}
|
||||
|
||||
static void _spi_cs_deactivate(struct kwspi_registers *reg)
|
||||
{
|
||||
clrbits_le32(®->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(®->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(®->irq_cause, KWSPI_SMEMRDIRQ);
|
||||
writel(tmpdout, ®->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(®->irq_cause) & KWSPI_SMEMRDIRQ) {
|
||||
isread = 1;
|
||||
tmpdin = readl(®->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, ®->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, ®->ctrl);
|
||||
writel(KWSPI_SMEMRDIRQ, ®->irq_cause);
|
||||
writel(KWSPI_IRQMASK, ®->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
|
||||
144
u-boot/drivers/spi/lpc32xx_ssp.c
Normal file
144
u-boot/drivers/spi/lpc32xx_ssp.c
Normal 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 */
|
||||
}
|
||||
90
u-boot/drivers/spi/mpc52xx_spi.c
Normal file
90
u-boot/drivers/spi/mpc52xx_spi.c
Normal 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;
|
||||
}
|
||||
166
u-boot/drivers/spi/mpc8xxx_spi.c
Normal file
166
u-boot/drivers/spi/mpc8xxx_spi.c
Normal 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;
|
||||
}
|
||||
466
u-boot/drivers/spi/mxc_spi.c
Normal file
466
u-boot/drivers/spi/mxc_spi.c
Normal 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(®s->ctrl, reg_ctrl);
|
||||
reg_ctrl |= MXC_CSPICTRL_EN;
|
||||
reg_write(®s->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(®s->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(®s->ctrl, reg_ctrl);
|
||||
debug("reg_config = 0x%x\n", reg_config);
|
||||
reg_write(®s->cfg, reg_config);
|
||||
|
||||
/* save config register and control register */
|
||||
mxcs->ctrl_reg = reg_ctrl;
|
||||
mxcs->cfg_reg = reg_config;
|
||||
|
||||
/* clear interrupt reg */
|
||||
reg_write(®s->intr, 0);
|
||||
reg_write(®s->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(®s->ctrl, mxcs->ctrl_reg | MXC_CSPICTRL_EN);
|
||||
#ifdef MXC_ECSPI
|
||||
reg_write(®s->cfg, mxcs->cfg_reg);
|
||||
#endif
|
||||
|
||||
/* Clear interrupt register */
|
||||
reg_write(®s->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(®s->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(®s->txdata, data);
|
||||
nbytes -= 4;
|
||||
}
|
||||
|
||||
/* FIFO is written, now starts the transfer setting the XCH bit */
|
||||
reg_write(®s->ctrl, mxcs->ctrl_reg |
|
||||
MXC_CSPICTRL_EN | MXC_CSPICTRL_XCH);
|
||||
|
||||
ts = get_timer(0);
|
||||
status = reg_read(®s->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(®s->stat);
|
||||
}
|
||||
|
||||
/* Transfer completed, clear any pending request */
|
||||
reg_write(®s->stat, MXC_CSPICTRL_TC | MXC_CSPICTRL_RXOVF);
|
||||
|
||||
nbytes = DIV_ROUND_UP(bitlen, 8);
|
||||
|
||||
cnt = nbytes % 32;
|
||||
|
||||
if (bitlen % 32) {
|
||||
data = reg_read(®s->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(®s->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(®s->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(®s->period, MXC_CSPIPERIOD_32KHZ);
|
||||
reg_write(®s->intr, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_release_bus(struct spi_slave *slave)
|
||||
{
|
||||
/* TODO: Shut the controller down */
|
||||
}
|
||||
363
u-boot/drivers/spi/mxs_spi.c
Normal file
363
u-boot/drivers/spi/mxs_spi.c
Normal 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);
|
||||
}
|
||||
}
|
||||
697
u-boot/drivers/spi/omap3_spi.c
Normal file
697
u-boot/drivers/spi/omap3_spi.c
Normal 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, ®s->sysconfig);
|
||||
do {
|
||||
tmp = readl(®s->sysstatus);
|
||||
} while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE));
|
||||
|
||||
writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE |
|
||||
OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP |
|
||||
OMAP3_MCSPI_SYSCONFIG_SMARTIDLE, ®s->sysconfig);
|
||||
|
||||
writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, ®s->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
|
||||
448
u-boot/drivers/spi/pic32_spi.c
Normal file
448
u-boot/drivers/spi/pic32_spi.c
Normal 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
380
u-boot/drivers/spi/rk_spi.c
Normal 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(®s->ctrlr0));
|
||||
debug("ctrl1: \t\t0x%08x\n", readl(®s->ctrlr1));
|
||||
debug("ssienr: \t\t0x%08x\n", readl(®s->enr));
|
||||
debug("ser: \t\t0x%08x\n", readl(®s->ser));
|
||||
debug("baudr: \t\t0x%08x\n", readl(®s->baudr));
|
||||
debug("txftlr: \t\t0x%08x\n", readl(®s->txftlr));
|
||||
debug("rxftlr: \t\t0x%08x\n", readl(®s->rxftlr));
|
||||
debug("txflr: \t\t0x%08x\n", readl(®s->txflr));
|
||||
debug("rxflr: \t\t0x%08x\n", readl(®s->rxflr));
|
||||
debug("sr: \t\t0x%08x\n", readl(®s->sr));
|
||||
debug("imr: \t\t0x%08x\n", readl(®s->imr));
|
||||
debug("isr: \t\t0x%08x\n", readl(®s->isr));
|
||||
debug("dmacr: \t\t0x%08x\n", readl(®s->dmacr));
|
||||
debug("dmatdlr: \t0x%08x\n", readl(®s->dmatdlr));
|
||||
debug("dmardlr: \t0x%08x\n", readl(®s->dmardlr));
|
||||
}
|
||||
|
||||
static void rkspi_enable_chip(struct rockchip_spi *regs, bool enable)
|
||||
{
|
||||
writel(enable ? 1 : 0, ®s->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(®s->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, ®s->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, ®s->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, ®s->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, ®s->ctrlr1);
|
||||
rkspi_enable_chip(regs, true);
|
||||
|
||||
toread = todo;
|
||||
towrite = todo;
|
||||
while (toread || towrite) {
|
||||
u32 status = readl(®s->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
124
u-boot/drivers/spi/rk_spi.h
Normal 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 */
|
||||
164
u-boot/drivers/spi/sandbox_spi.c
Normal file
164
u-boot/drivers/spi/sandbox_spi.c
Normal 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,
|
||||
};
|
||||
279
u-boot/drivers/spi/sh_qspi.c
Normal file
279
u-boot/drivers/spi/sh_qspi.c
Normal 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
254
u-boot/drivers/spi/sh_spi.c
Normal 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)
|
||||
{
|
||||
|
||||
}
|
||||
68
u-boot/drivers/spi/sh_spi.h
Normal file
68
u-boot/drivers/spi/sh_spi.h
Normal 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
|
||||
259
u-boot/drivers/spi/soft_spi.c
Normal file
259
u-boot/drivers/spi/soft_spi.c
Normal 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,
|
||||
};
|
||||
176
u-boot/drivers/spi/soft_spi_legacy.c
Normal file
176
u-boot/drivers/spi/soft_spi_legacy.c
Normal 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);
|
||||
}
|
||||
15
u-boot/drivers/spi/spi-emul-uclass.c
Normal file
15
u-boot/drivers/spi/spi-emul-uclass.c
Normal 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",
|
||||
};
|
||||
463
u-boot/drivers/spi/spi-uclass.c
Normal file
463
u-boot/drivers/spi/spi-uclass.c
Normal 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
60
u-boot/drivers/spi/spi.c
Normal 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
|
||||
401
u-boot/drivers/spi/tegra114_spi.c
Normal file
401
u-boot/drivers/spi/tegra114_spi.c
Normal 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(®s->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(®s->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(®s->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(®s->fifo_status);
|
||||
writel(reg, ®s->fifo_status);
|
||||
|
||||
clrsetbits_le32(®s->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, ®s->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(®s->xfer_status, SPI_XFER_STS_RDY);
|
||||
|
||||
clrsetbits_le32(®s->command1,
|
||||
SPI_CMD1_BIT_LEN_MASK << SPI_CMD1_BIT_LEN_SHIFT,
|
||||
(bytes * 8 - 1) << SPI_CMD1_BIT_LEN_SHIFT);
|
||||
writel(tmpdout, ®s->tx_fifo);
|
||||
setbits_le32(®s->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(®s->xfer_status);
|
||||
if (!(xfer_status & SPI_XFER_STS_RDY))
|
||||
continue;
|
||||
|
||||
fifo_status = readl(®s->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(®s->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(®s->fifo_status), ®s->fifo_status);
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END)
|
||||
spi_cs_deactivate(dev);
|
||||
|
||||
debug("%s: transfer ended. Value=%08x, fifo_status = %08x\n",
|
||||
__func__, tmpdin, readl(®s->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,
|
||||
};
|
||||
353
u-boot/drivers/spi/tegra20_sflash.c
Normal file
353
u-boot/drivers/spi/tegra20_sflash.c
Normal 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, ®s->status);
|
||||
debug("%s: STATUS = %08x\n", __func__, readl(®s->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(®s->command, SPI_CMD_ACTIVE_SCLK_MASK |
|
||||
SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg);
|
||||
debug("%s: COMMAND = %08x\n", __func__, readl(®s->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(®s->status);
|
||||
writel(reg, ®s->status); /* Clear all SPI events via R/W */
|
||||
debug("spi_xfer entry: STATUS = %08x\n", reg);
|
||||
|
||||
reg = readl(®s->command);
|
||||
reg |= SPI_CMD_TXEN | SPI_CMD_RXEN;
|
||||
writel(reg, ®s->command);
|
||||
debug("spi_xfer: COMMAND = %08x\n", readl(®s->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(®s->command, SPI_CMD_BIT_LENGTH_MASK,
|
||||
bytes * 8 - 1);
|
||||
writel(tmpdout, ®s->tx_fifo);
|
||||
setbits_le32(®s->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(®s->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(®s->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(®s->status), ®s->status);
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END)
|
||||
spi_cs_deactivate(dev);
|
||||
|
||||
debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n",
|
||||
tmpdin, readl(®s->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,
|
||||
};
|
||||
372
u-boot/drivers/spi/tegra20_slink.c
Normal file
372
u-boot/drivers/spi/tegra20_slink.c
Normal 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, ®s->status);
|
||||
debug("%s: STATUS = %08x\n", __func__, readl(®s->status));
|
||||
|
||||
/* Set master mode and sw controlled CS */
|
||||
reg = readl(®s->command);
|
||||
reg |= SLINK_CMD_M_S | SLINK_CMD_CS_SOFT;
|
||||
writel(reg, ®s->command);
|
||||
debug("%s: COMMAND = %08x\n", __func__, readl(®s->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(®s->status);
|
||||
writel(reg, ®s->status); /* Clear all SPI events via R/W */
|
||||
debug("%s entry: STATUS = %08x\n", __func__, reg);
|
||||
|
||||
reg = readl(®s->status2);
|
||||
writel(reg, ®s->status2); /* Clear all STATUS2 events via R/W */
|
||||
debug("%s entry: STATUS2 = %08x\n", __func__, reg);
|
||||
|
||||
debug("%s entry: COMMAND = %08x\n", __func__, readl(®s->command));
|
||||
|
||||
clrsetbits_le32(®s->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(®s->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(®s->command, SLINK_CMD_BIT_LENGTH_MASK,
|
||||
bytes * 8 - 1);
|
||||
writel(tmpdout, ®s->tx_fifo);
|
||||
setbits_le32(®s->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(®s->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(®s->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(®s->status), ®s->status);
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END)
|
||||
spi_cs_deactivate(dev);
|
||||
|
||||
debug("%s: transfer ended. Value=%08x, status = %08x\n",
|
||||
__func__, tmpdin, readl(®s->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(®s->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, ®s->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,
|
||||
};
|
||||
417
u-boot/drivers/spi/tegra210_qspi.c
Normal file
417
u-boot/drivers/spi/tegra210_qspi.c
Normal 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(®s->fifo_status));
|
||||
|
||||
/* Set master mode and sw controlled CS */
|
||||
setbits_le32(®s->command1, QSPI_CMD1_M_S | QSPI_CMD1_CS_SW_HW |
|
||||
(priv->mode << QSPI_CMD1_MODE_SHIFT));
|
||||
debug("%s: COMMAND1 = %08x\n", __func__, readl(®s->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(®s->fifo_status);
|
||||
writel(reg, ®s->fifo_status);
|
||||
|
||||
/* flush RX/TX FIFOs */
|
||||
setbits_le32(®s->fifo_status,
|
||||
(QSPI_FIFO_STS_RX_FIFO_FLUSH |
|
||||
QSPI_FIFO_STS_TX_FIFO_FLUSH));
|
||||
|
||||
tm = QSPI_TIMEOUT;
|
||||
while ((tm && readl(®s->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(®s->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, ®s->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, ®s->tx_fifo);
|
||||
setbits_le32(®s->command1, QSPI_CMD1_TX_EN);
|
||||
}
|
||||
|
||||
if (din != NULL)
|
||||
setbits_le32(®s->command1, QSPI_CMD1_RX_EN);
|
||||
|
||||
/* clear ready bit */
|
||||
setbits_le32(®s->xfer_status, QSPI_XFER_STS_RDY);
|
||||
|
||||
clrsetbits_le32(®s->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(®s->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(®s->xfer_status);
|
||||
if (!(xfer_status & QSPI_XFER_STS_RDY))
|
||||
continue;
|
||||
|
||||
fifo_status = readl(®s->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(®s->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(®s->fifo_status), ®s->fifo_status);
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END)
|
||||
spi_cs_deactivate(dev);
|
||||
|
||||
debug("%s: transfer ended. Value=%08x, fifo_status = %08x\n",
|
||||
__func__, tmpdin, readl(®s->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,
|
||||
};
|
||||
12
u-boot/drivers/spi/tegra_spi.h
Normal file
12
u-boot/drivers/spi/tegra_spi.h
Normal 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 */
|
||||
};
|
||||
594
u-boot/drivers/spi/ti_qspi.c
Normal file
594
u-boot/drivers/spi/ti_qspi.c
Normal 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 */
|
||||
302
u-boot/drivers/spi/xilinx_spi.c
Normal file
302
u-boot/drivers/spi/xilinx_spi.c
Normal 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, ®s->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), ®s->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, ®s->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, ®s->spissr);
|
||||
writel(XILSPI_SPICR_DFLT_ON, ®s->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, ®s->spissr);
|
||||
writel(XILSPI_SPICR_DFLT_OFF, ®s->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(®s->spisr) & SPISR_RX_EMPTY)) {
|
||||
readl(®s->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, ®s->spidtr);
|
||||
while (timeout && readl(®s->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(®s->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(®s->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, ®s->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,
|
||||
};
|
||||
630
u-boot/drivers/spi/zynq_qspi.c
Normal file
630
u-boot/drivers/spi/zynq_qspi.c
Normal 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, ®s->enr);
|
||||
|
||||
/* Disable Interrupts */
|
||||
writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->idr);
|
||||
|
||||
/* Clear the TX and RX threshold reg */
|
||||
writel(ZYNQ_QSPI_TXFIFO_THRESHOLD, ®s->txftr);
|
||||
writel(ZYNQ_QSPI_RXFIFO_THRESHOLD, ®s->rxftr);
|
||||
|
||||
/* Clear the RX FIFO */
|
||||
while (readl(®s->isr) & ZYNQ_QSPI_IXR_RXNEMPTY_MASK)
|
||||
readl(®s->drxr);
|
||||
|
||||
/* Clear Interrupts */
|
||||
writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->isr);
|
||||
|
||||
/* Manual slave select and Auto start */
|
||||
confr = readl(®s->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, ®s->cr);
|
||||
|
||||
/* Disable the LQSPI feature */
|
||||
confr = readl(®s->lqspicfg);
|
||||
confr &= ~ZYNQ_QSPI_LQSPICFG_LQMODE_MASK;
|
||||
writel(confr, ®s->lqspicfg);
|
||||
|
||||
/* Enable SPI */
|
||||
writel(ZYNQ_QSPI_ENR_SPI_EN_MASK, ®s->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(®s->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, ®s->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, ®s->txd0r);
|
||||
priv->bytes_to_transfer -= 4;
|
||||
fifocount++;
|
||||
} else {
|
||||
/* Write TXD1, TXD2, TXD3 only if TxFIFO is empty. */
|
||||
if (!(readl(®s->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, ®s->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(®s->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, ®s->isr);
|
||||
|
||||
/* Disable all interrupts */
|
||||
writel(ZYNQ_QSPI_IXR_ALL_MASK, ®s->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(®s->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, ®s->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,
|
||||
®s->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, ®s->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, ®s->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, ®s->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(®s->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, ®s->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(®s->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, ®s->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,
|
||||
};
|
||||
329
u-boot/drivers/spi/zynq_spi.c
Normal file
329
u-boot/drivers/spi/zynq_spi.c
Normal 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, ®s->enr);
|
||||
|
||||
/* Disable Interrupts */
|
||||
writel(ZYNQ_SPI_IXR_ALL_MASK, ®s->idr);
|
||||
|
||||
/* Clear RX FIFO */
|
||||
while (readl(®s->isr) &
|
||||
ZYNQ_SPI_IXR_RXNEMPTY_MASK)
|
||||
readl(®s->rxdr);
|
||||
|
||||
/* Clear Interrupts */
|
||||
writel(ZYNQ_SPI_IXR_ALL_MASK, ®s->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, ®s->cr);
|
||||
|
||||
/* Enable SPI */
|
||||
writel(ZYNQ_SPI_ENR_SPI_EN_MASK, ®s->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(®s->cr, ZYNQ_SPI_CR_CS_MASK);
|
||||
cr = readl(®s->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, ®s->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(®s->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, ®s->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, ®s->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, ®s->txdr);
|
||||
tx_len--;
|
||||
tx_tvl++;
|
||||
}
|
||||
|
||||
/* Check TX FIFO completion */
|
||||
ts = get_timer(0);
|
||||
status = readl(®s->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(®s->isr);
|
||||
}
|
||||
|
||||
/* Read the data from RX FIFO */
|
||||
status = readl(®s->isr);
|
||||
while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) {
|
||||
buf = readl(®s->rxdr);
|
||||
if (rx_buf)
|
||||
*rx_buf++ = buf;
|
||||
status = readl(®s->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(®s->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, ®s->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(®s->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, ®s->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,
|
||||
};
|
||||
Reference in New Issue
Block a user