Merge branch 'master' of https://gitlab.denx.de/u-boot/custodians/u-boot-spi
- fix for fsl_qspi read timeout (Thomas) - spi-mem read data size fix (Ye Li) - SiFive SPI driver, mmc_spi flags (Bhargav, Anup) - Micron spi-nor parts (Ashish) - MT7629 spi-mem driver(Weijie)
This commit is contained in:
commit
222701e157
@ -205,7 +205,7 @@ F: drivers/mmc/mtk-sd.c
|
||||
F: drivers/pinctrl/mediatek/
|
||||
F: drivers/power/domain/mtk-power-domain.c
|
||||
F: drivers/ram/mediatek/
|
||||
F: drivers/spi/mtk_qspi.c
|
||||
F: drivers/spi/mtk_snfi_spi.c
|
||||
F: drivers/timer/mtk_timer.c
|
||||
F: drivers/watchdog/mtk_wdt.c
|
||||
F: drivers/net/mtk_eth.c
|
||||
|
@ -64,7 +64,7 @@ CONFIG_DM_SERIAL=y
|
||||
CONFIG_MTK_SERIAL=y
|
||||
CONFIG_SPI=y
|
||||
CONFIG_DM_SPI=y
|
||||
CONFIG_MTK_QSPI=y
|
||||
CONFIG_MTK_SNFI_SPI=y
|
||||
CONFIG_SYSRESET=y
|
||||
CONFIG_SPL_SYSRESET=y
|
||||
CONFIG_SYSRESET_WATCHDOG=y
|
||||
|
@ -84,7 +84,7 @@ static int mmc_spi_sendcmd(struct udevice *dev,
|
||||
cmdo[4] = cmdarg >> 8;
|
||||
cmdo[5] = cmdarg;
|
||||
cmdo[6] = (crc7(0, &cmdo[1], 5) << 1) | 0x01;
|
||||
ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, 0);
|
||||
ret = dm_spi_xfer(dev, sizeof(cmdo) * 8, cmdo, NULL, SPI_XFER_BEGIN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@ -360,6 +360,8 @@ static int dm_mmc_spi_request(struct udevice *dev, struct mmc_cmd *cmd,
|
||||
}
|
||||
|
||||
done:
|
||||
dm_spi_xfer(dev, 0, NULL, NULL, SPI_XFER_END);
|
||||
|
||||
dm_spi_release_bus(dev);
|
||||
|
||||
return ret;
|
||||
|
@ -163,11 +163,15 @@ const struct flash_info spi_nor_ids[] = {
|
||||
{ INFO("n25q128a13", 0x20ba18, 0, 64 * 1024, 256, SECT_4K | SPI_NOR_QUAD_READ) },
|
||||
{ INFO("n25q256a", 0x20ba19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ INFO("n25q256ax1", 0x20bb19, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ INFO6("mt25qu512a", 0x20bb20, 0x104400, 64 * 1024, 1024,
|
||||
SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ INFO("n25q512a", 0x20bb20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ INFO("n25q512ax3", 0x20ba20, 0, 64 * 1024, 1024, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | SPI_NOR_4B_OPCODES) },
|
||||
{ INFO("n25q00", 0x20ba21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
{ INFO("n25q00a", 0x20bb21, 0, 64 * 1024, 2048, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
{ INFO("mt25qu02g", 0x20bb22, 0, 64 * 1024, 4096, SECT_4K | USE_FSR | SPI_NOR_QUAD_READ | NO_CHIP_ERASE) },
|
||||
{ INFO("mt35xu512aba", 0x2c5b1a, 0, 128 * 1024, 512, USE_FSR | SPI_NOR_4B_OPCODES) },
|
||||
{ INFO("mt35xu02g", 0x2c5b1c, 0, 128 * 1024, 2048, USE_FSR | SPI_NOR_4B_OPCODES) },
|
||||
#endif
|
||||
#ifdef CONFIG_SPI_FLASH_SPANSION /* SPANSION */
|
||||
/* Spansion/Cypress -- single (large) sector size only, at least
|
||||
|
@ -158,13 +158,14 @@ config MT7621_SPI
|
||||
the SPI NOR flash on platforms embedding this Ralink / MediaTek
|
||||
SPI core, like MT7621/7628/7688.
|
||||
|
||||
config MTK_QSPI
|
||||
bool "Mediatek QSPI driver"
|
||||
imply SPI_FLASH_BAR
|
||||
config MTK_SNFI_SPI
|
||||
bool "Mediatek SPI memory controller driver"
|
||||
depends on SPI_MEM
|
||||
help
|
||||
Enable the Mediatek QSPI driver. This driver can be
|
||||
used to access the SPI NOR flash on platforms embedding this
|
||||
Mediatek QSPI IP core.
|
||||
Enable the Mediatek SPI memory controller driver. This driver is
|
||||
originally based on the MediaTek SNFI IP core. It can only be
|
||||
used to access SPI memory devices like SPI-NOR or SPI-NAND on
|
||||
platforms embedding this IP core, like MT7622/M7629.
|
||||
|
||||
config MVEBU_A3700_SPI
|
||||
bool "Marvell Armada 3700 SPI driver"
|
||||
@ -232,6 +233,14 @@ config SANDBOX_SPI
|
||||
};
|
||||
};
|
||||
|
||||
config SPI_SIFIVE
|
||||
bool "SiFive SPI driver"
|
||||
help
|
||||
This driver supports the SiFive SPI IP. If unsure say N.
|
||||
Enable the SiFive SPI controller driver.
|
||||
|
||||
The SiFive SPI controller driver is found on various SiFive SoCs.
|
||||
|
||||
config SPI_SUNXI
|
||||
bool "Allwinner SoC SPI controllers"
|
||||
help
|
||||
|
@ -37,7 +37,7 @@ obj-$(CONFIG_LPC32XX_SSP) += lpc32xx_ssp.o
|
||||
obj-$(CONFIG_MESON_SPIFC) += meson_spifc.o
|
||||
obj-$(CONFIG_MPC8XX_SPI) += mpc8xx_spi.o
|
||||
obj-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
|
||||
obj-$(CONFIG_MTK_QSPI) += mtk_qspi.o
|
||||
obj-$(CONFIG_MTK_SNFI_SPI) += mtk_snfi_spi.o
|
||||
obj-$(CONFIG_MT7621_SPI) += mt7621_spi.o
|
||||
obj-$(CONFIG_MSCC_BB_SPI) += mscc_bb_spi.o
|
||||
obj-$(CONFIG_MVEBU_A3700_SPI) += mvebu_a3700_spi.o
|
||||
@ -50,6 +50,7 @@ obj-$(CONFIG_PL022_SPI) += pl022_spi.o
|
||||
obj-$(CONFIG_RENESAS_RPC_SPI) += renesas_rpc_spi.o
|
||||
obj-$(CONFIG_ROCKCHIP_SPI) += rk_spi.o
|
||||
obj-$(CONFIG_SANDBOX_SPI) += sandbox_spi.o
|
||||
obj-$(CONFIG_SPI_SIFIVE) += spi-sifive.o
|
||||
obj-$(CONFIG_SPI_SUNXI) += spi-sunxi.o
|
||||
obj-$(CONFIG_SH_SPI) += sh_spi.o
|
||||
obj-$(CONFIG_SH_QSPI) += sh_qspi.o
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <spi.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/sizes.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <watchdog.h>
|
||||
@ -150,20 +151,13 @@ static void qspi_write32(u32 flags, u32 *addr, u32 val)
|
||||
static inline int is_controller_busy(const struct fsl_qspi_priv *priv)
|
||||
{
|
||||
u32 val;
|
||||
const u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK |
|
||||
QSPI_SR_IP_ACC_MASK;
|
||||
unsigned int retry = 5;
|
||||
u32 mask = QSPI_SR_BUSY_MASK | QSPI_SR_AHB_ACC_MASK |
|
||||
QSPI_SR_IP_ACC_MASK;
|
||||
|
||||
do {
|
||||
val = qspi_read32(priv->flags, &priv->regs->sr);
|
||||
if (priv->flags & QSPI_FLAG_REGMAP_ENDIAN_BIG)
|
||||
mask = (u32)cpu_to_be32(mask);
|
||||
|
||||
if ((~val & mask) == mask)
|
||||
return 0;
|
||||
|
||||
udelay(1);
|
||||
} while (--retry);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
return readl_poll_timeout(&priv->regs->sr, val, !(val & mask), 1000);
|
||||
}
|
||||
|
||||
/* QSPI support swapping the flash read/write data
|
||||
|
@ -1,359 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2018 MediaTek, Inc.
|
||||
* Author : Guochun.Mao@mediatek.com
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/ioport.h>
|
||||
|
||||
/* Register Offset */
|
||||
struct mtk_qspi_regs {
|
||||
u32 cmd;
|
||||
u32 cnt;
|
||||
u32 rdsr;
|
||||
u32 rdata;
|
||||
u32 radr[3];
|
||||
u32 wdata;
|
||||
u32 prgdata[6];
|
||||
u32 shreg[10];
|
||||
u32 cfg[2];
|
||||
u32 shreg10;
|
||||
u32 mode_mon;
|
||||
u32 status[4];
|
||||
u32 flash_time;
|
||||
u32 flash_cfg;
|
||||
u32 reserved_0[3];
|
||||
u32 sf_time;
|
||||
u32 pp_dw_data;
|
||||
u32 reserved_1;
|
||||
u32 delsel_0[2];
|
||||
u32 intrstus;
|
||||
u32 intren;
|
||||
u32 reserved_2;
|
||||
u32 cfg3;
|
||||
u32 reserved_3;
|
||||
u32 chksum;
|
||||
u32 aaicmd;
|
||||
u32 wrprot;
|
||||
u32 radr3;
|
||||
u32 dual;
|
||||
u32 delsel_1[3];
|
||||
};
|
||||
|
||||
struct mtk_qspi_platdata {
|
||||
fdt_addr_t reg_base;
|
||||
fdt_addr_t mem_base;
|
||||
};
|
||||
|
||||
struct mtk_qspi_priv {
|
||||
struct mtk_qspi_regs *regs;
|
||||
unsigned long *mem_base;
|
||||
u8 op;
|
||||
u8 tx[3]; /* only record max 3 bytes paras, when it's address. */
|
||||
u32 txlen; /* dout buffer length - op code length */
|
||||
u8 *rx;
|
||||
u32 rxlen;
|
||||
};
|
||||
|
||||
#define MTK_QSPI_CMD_POLLINGREG_US 500000
|
||||
#define MTK_QSPI_WRBUF_SIZE 256
|
||||
#define MTK_QSPI_COMMAND_ENABLE 0x30
|
||||
|
||||
/* NOR flash controller commands */
|
||||
#define MTK_QSPI_RD_TRIGGER BIT(0)
|
||||
#define MTK_QSPI_READSTATUS BIT(1)
|
||||
#define MTK_QSPI_PRG_CMD BIT(2)
|
||||
#define MTK_QSPI_WR_TRIGGER BIT(4)
|
||||
#define MTK_QSPI_WRITESTATUS BIT(5)
|
||||
#define MTK_QSPI_AUTOINC BIT(7)
|
||||
|
||||
#define MTK_QSPI_MAX_RX_TX_SHIFT 0x6
|
||||
#define MTK_QSPI_MAX_SHIFT 0x8
|
||||
|
||||
#define MTK_QSPI_WR_BUF_ENABLE 0x1
|
||||
#define MTK_QSPI_WR_BUF_DISABLE 0x0
|
||||
|
||||
static int mtk_qspi_execute_cmd(struct mtk_qspi_priv *priv, u8 cmd)
|
||||
{
|
||||
u8 tmp;
|
||||
u8 val = cmd & ~MTK_QSPI_AUTOINC;
|
||||
|
||||
writeb(cmd, &priv->regs->cmd);
|
||||
|
||||
return readb_poll_timeout(&priv->regs->cmd, tmp, !(val & tmp),
|
||||
MTK_QSPI_CMD_POLLINGREG_US);
|
||||
}
|
||||
|
||||
static int mtk_qspi_tx_rx(struct mtk_qspi_priv *priv)
|
||||
{
|
||||
int len = 1 + priv->txlen + priv->rxlen;
|
||||
int i, ret, idx;
|
||||
|
||||
if (len > MTK_QSPI_MAX_SHIFT)
|
||||
return -ERR_INVAL;
|
||||
|
||||
writeb(len * 8, &priv->regs->cnt);
|
||||
|
||||
/* start at PRGDATA5, go down to PRGDATA0 */
|
||||
idx = MTK_QSPI_MAX_RX_TX_SHIFT - 1;
|
||||
|
||||
/* opcode */
|
||||
writeb(priv->op, &priv->regs->prgdata[idx]);
|
||||
idx--;
|
||||
|
||||
/* program TX data */
|
||||
for (i = 0; i < priv->txlen; i++, idx--)
|
||||
writeb(priv->tx[i], &priv->regs->prgdata[idx]);
|
||||
|
||||
/* clear out rest of TX registers */
|
||||
while (idx >= 0) {
|
||||
writeb(0, &priv->regs->prgdata[idx]);
|
||||
idx--;
|
||||
}
|
||||
|
||||
ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_PRG_CMD);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* restart at first RX byte */
|
||||
idx = priv->rxlen - 1;
|
||||
|
||||
/* read out RX data */
|
||||
for (i = 0; i < priv->rxlen; i++, idx--)
|
||||
priv->rx[i] = readb(&priv->regs->shreg[idx]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_read(struct mtk_qspi_priv *priv,
|
||||
u32 addr, u8 *buf, u32 len)
|
||||
{
|
||||
memcpy(buf, (u8 *)priv->mem_base + addr, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void mtk_qspi_set_addr(struct mtk_qspi_priv *priv, u32 addr)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
writeb(addr & 0xff, &priv->regs->radr[i]);
|
||||
addr >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
static int mtk_qspi_write_single_byte(struct mtk_qspi_priv *priv,
|
||||
u32 addr, u32 length, const u8 *data)
|
||||
{
|
||||
int i, ret;
|
||||
|
||||
mtk_qspi_set_addr(priv, addr);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
writeb(*data++, &priv->regs->wdata);
|
||||
ret = mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_write_buffer(struct mtk_qspi_priv *priv, u32 addr,
|
||||
const u8 *buf)
|
||||
{
|
||||
int i, data;
|
||||
|
||||
mtk_qspi_set_addr(priv, addr);
|
||||
|
||||
for (i = 0; i < MTK_QSPI_WRBUF_SIZE; i += 4) {
|
||||
data = buf[i + 3] << 24 | buf[i + 2] << 16 |
|
||||
buf[i + 1] << 8 | buf[i];
|
||||
writel(data, &priv->regs->pp_dw_data);
|
||||
}
|
||||
|
||||
return mtk_qspi_execute_cmd(priv, MTK_QSPI_WR_TRIGGER);
|
||||
}
|
||||
|
||||
static int mtk_qspi_write(struct mtk_qspi_priv *priv,
|
||||
u32 addr, const u8 *buf, u32 len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* setting pre-fetch buffer for page program */
|
||||
writel(MTK_QSPI_WR_BUF_ENABLE, &priv->regs->cfg[1]);
|
||||
while (len >= MTK_QSPI_WRBUF_SIZE) {
|
||||
ret = mtk_qspi_write_buffer(priv, addr, buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
len -= MTK_QSPI_WRBUF_SIZE;
|
||||
addr += MTK_QSPI_WRBUF_SIZE;
|
||||
buf += MTK_QSPI_WRBUF_SIZE;
|
||||
}
|
||||
/* disable pre-fetch buffer for page program */
|
||||
writel(MTK_QSPI_WR_BUF_DISABLE, &priv->regs->cfg[1]);
|
||||
|
||||
if (len)
|
||||
return mtk_qspi_write_single_byte(priv, addr, len, buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_claim_bus(struct udevice *dev)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_release_bus(struct udevice *dev)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_transfer(struct mtk_qspi_priv *priv, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
u32 bytes = DIV_ROUND_UP(bitlen, 8);
|
||||
u32 addr;
|
||||
|
||||
if (!bytes)
|
||||
return -ERR_INVAL;
|
||||
|
||||
if (dout) {
|
||||
if (flags & SPI_XFER_BEGIN) {
|
||||
/* parse op code and potential paras first */
|
||||
priv->op = *(u8 *)dout;
|
||||
if (bytes > 1)
|
||||
memcpy(priv->tx, (u8 *)dout + 1,
|
||||
bytes <= 4 ? bytes - 1 : 3);
|
||||
priv->txlen = bytes - 1;
|
||||
}
|
||||
|
||||
if (flags == SPI_XFER_ONCE) {
|
||||
/* operations without receiving or sending data.
|
||||
* for example: erase, write flash register or write
|
||||
* enable...
|
||||
*/
|
||||
priv->rx = NULL;
|
||||
priv->rxlen = 0;
|
||||
return mtk_qspi_tx_rx(priv);
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END) {
|
||||
/* here, dout should be data to be written.
|
||||
* and priv->tx should be filled 3Bytes address.
|
||||
*/
|
||||
addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
|
||||
priv->tx[2];
|
||||
return mtk_qspi_write(priv, addr, (u8 *)dout, bytes);
|
||||
}
|
||||
}
|
||||
|
||||
if (din) {
|
||||
if (priv->txlen >= 3) {
|
||||
/* if run to here, priv->tx[] should be the address
|
||||
* where read data from,
|
||||
* and, din is the buf to receive data.
|
||||
*/
|
||||
addr = priv->tx[0] << 16 | priv->tx[1] << 8 |
|
||||
priv->tx[2];
|
||||
return mtk_qspi_read(priv, addr, (u8 *)din, bytes);
|
||||
}
|
||||
|
||||
/* should be reading flash's register */
|
||||
priv->rx = (u8 *)din;
|
||||
priv->rxlen = bytes;
|
||||
return mtk_qspi_tx_rx(priv);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_xfer(struct udevice *dev, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct mtk_qspi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
return mtk_qspi_transfer(priv, bitlen, dout, din, flags);
|
||||
}
|
||||
|
||||
static int mtk_qspi_set_speed(struct udevice *bus, uint speed)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_set_mode(struct udevice *bus, uint mode)
|
||||
{
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_ofdata_to_platdata(struct udevice *bus)
|
||||
{
|
||||
struct resource res_reg, res_mem;
|
||||
struct mtk_qspi_platdata *plat = bus->platdata;
|
||||
int ret;
|
||||
|
||||
ret = dev_read_resource_byname(bus, "reg_base", &res_reg);
|
||||
if (ret) {
|
||||
debug("can't get reg_base resource(ret = %d)\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
ret = dev_read_resource_byname(bus, "mem_base", &res_mem);
|
||||
if (ret) {
|
||||
debug("can't get map_base resource(ret = %d)\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
plat->mem_base = res_mem.start;
|
||||
plat->reg_base = res_reg.start;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_qspi_probe(struct udevice *bus)
|
||||
{
|
||||
struct mtk_qspi_platdata *plat = dev_get_platdata(bus);
|
||||
struct mtk_qspi_priv *priv = dev_get_priv(bus);
|
||||
|
||||
priv->regs = (struct mtk_qspi_regs *)plat->reg_base;
|
||||
priv->mem_base = (unsigned long *)plat->mem_base;
|
||||
|
||||
writel(MTK_QSPI_COMMAND_ENABLE, &priv->regs->wrprot);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops mtk_qspi_ops = {
|
||||
.claim_bus = mtk_qspi_claim_bus,
|
||||
.release_bus = mtk_qspi_release_bus,
|
||||
.xfer = mtk_qspi_xfer,
|
||||
.set_speed = mtk_qspi_set_speed,
|
||||
.set_mode = mtk_qspi_set_mode,
|
||||
};
|
||||
|
||||
static const struct udevice_id mtk_qspi_ids[] = {
|
||||
{ .compatible = "mediatek,mt7629-qspi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mtk_qspi) = {
|
||||
.name = "mtk_qspi",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = mtk_qspi_ids,
|
||||
.ops = &mtk_qspi_ops,
|
||||
.ofdata_to_platdata = mtk_qspi_ofdata_to_platdata,
|
||||
.platdata_auto_alloc_size = sizeof(struct mtk_qspi_platdata),
|
||||
.priv_auto_alloc_size = sizeof(struct mtk_qspi_priv),
|
||||
.probe = mtk_qspi_probe,
|
||||
};
|
318
drivers/spi/mtk_snfi_spi.c
Normal file
318
drivers/spi/mtk_snfi_spi.c
Normal file
@ -0,0 +1,318 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (C) 2019 MediaTek Inc. All Rights Reserved.
|
||||
*
|
||||
* Author: Weijie Gao <weijie.gao@mediatek.com>
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <clk.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <spi.h>
|
||||
#include <spi-mem.h>
|
||||
#include <stdbool.h>
|
||||
#include <watchdog.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
|
||||
#define SNFI_MAC_CTL 0x500
|
||||
#define MAC_XIO_SEL BIT(4)
|
||||
#define SF_MAC_EN BIT(3)
|
||||
#define SF_TRIG BIT(2)
|
||||
#define WIP_READY BIT(1)
|
||||
#define WIP BIT(0)
|
||||
|
||||
#define SNFI_MAC_OUTL 0x504
|
||||
#define SNFI_MAC_INL 0x508
|
||||
|
||||
#define SNFI_MISC_CTL 0x538
|
||||
#define SW_RST BIT(28)
|
||||
#define FIFO_RD_LTC_SHIFT 25
|
||||
#define FIFO_RD_LTC GENMASK(26, 25)
|
||||
#define LATCH_LAT_SHIFT 8
|
||||
#define LATCH_LAT GENMASK(9, 8)
|
||||
#define CS_DESELECT_CYC_SHIFT 0
|
||||
#define CS_DESELECT_CYC GENMASK(4, 0)
|
||||
|
||||
#define SNF_STA_CTL1 0x550
|
||||
#define SPI_STATE GENMASK(3, 0)
|
||||
|
||||
#define SNFI_GPRAM_OFFSET 0x800
|
||||
#define SNFI_GPRAM_SIZE 0x80
|
||||
|
||||
#define SNFI_POLL_INTERVAL 500000
|
||||
#define SNFI_RST_POLL_INTERVAL 1000000
|
||||
|
||||
struct mtk_snfi_priv {
|
||||
void __iomem *base;
|
||||
|
||||
struct clk nfi_clk;
|
||||
struct clk pad_clk;
|
||||
};
|
||||
|
||||
static int mtk_snfi_adjust_op_size(struct spi_slave *slave,
|
||||
struct spi_mem_op *op)
|
||||
{
|
||||
u32 nbytes;
|
||||
|
||||
/*
|
||||
* When there is input data, it will be appended after the output
|
||||
* data in the GPRAM. So the total size of either pure output data
|
||||
* or the output+input data must not exceed the GPRAM size.
|
||||
*/
|
||||
|
||||
nbytes = sizeof(op->cmd.opcode) + op->addr.nbytes +
|
||||
op->dummy.nbytes;
|
||||
|
||||
if (nbytes + op->data.nbytes <= SNFI_GPRAM_SIZE)
|
||||
return 0;
|
||||
|
||||
if (nbytes >= SNFI_GPRAM_SIZE)
|
||||
return -ENOTSUPP;
|
||||
|
||||
op->data.nbytes = SNFI_GPRAM_SIZE - nbytes;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mtk_snfi_supports_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
if (op->cmd.buswidth > 1 || op->addr.buswidth > 1 ||
|
||||
op->dummy.buswidth > 1 || op->data.buswidth > 1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int mtk_snfi_mac_trigger(struct mtk_snfi_priv *priv,
|
||||
struct udevice *bus, u32 outlen, u32 inlen)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
#ifdef CONFIG_PINCTRL
|
||||
pinctrl_select_state(bus, "snfi");
|
||||
#endif
|
||||
|
||||
writel(SF_MAC_EN, priv->base + SNFI_MAC_CTL);
|
||||
writel(outlen, priv->base + SNFI_MAC_OUTL);
|
||||
writel(inlen, priv->base + SNFI_MAC_INL);
|
||||
|
||||
writel(SF_MAC_EN | SF_TRIG, priv->base + SNFI_MAC_CTL);
|
||||
|
||||
ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val,
|
||||
val & WIP_READY, SNFI_POLL_INTERVAL);
|
||||
if (ret) {
|
||||
printf("%s: timed out waiting for WIP_READY\n", __func__);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = readl_poll_timeout(priv->base + SNFI_MAC_CTL, val,
|
||||
!(val & WIP), SNFI_POLL_INTERVAL);
|
||||
if (ret)
|
||||
printf("%s: timed out waiting for WIP cleared\n", __func__);
|
||||
|
||||
writel(0, priv->base + SNFI_MAC_CTL);
|
||||
|
||||
cleanup:
|
||||
#ifdef CONFIG_PINCTRL
|
||||
pinctrl_select_state(bus, "default");
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_snfi_mac_reset(struct mtk_snfi_priv *priv)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
setbits_32(priv->base + SNFI_MISC_CTL, SW_RST);
|
||||
|
||||
ret = readl_poll_timeout(priv->base + SNF_STA_CTL1, val,
|
||||
!(val & SPI_STATE), SNFI_POLL_INTERVAL);
|
||||
if (ret)
|
||||
printf("%s: failed to reset snfi mac\n", __func__);
|
||||
|
||||
writel((2 << FIFO_RD_LTC_SHIFT) |
|
||||
(10 << CS_DESELECT_CYC_SHIFT),
|
||||
priv->base + SNFI_MISC_CTL);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void mtk_snfi_copy_to_gpram(struct mtk_snfi_priv *priv,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET;
|
||||
size_t i, n = (len + sizeof(u32) - 1) / sizeof(u32);
|
||||
const u32 *buff = data;
|
||||
|
||||
/*
|
||||
* The output data will always be copied to the beginning of
|
||||
* the GPRAM. Uses word write for better performace.
|
||||
*
|
||||
* Trailing bytes in the last word are not cared.
|
||||
*/
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
writel(buff[i], gpram + i * sizeof(u32));
|
||||
}
|
||||
|
||||
static void mtk_snfi_copy_from_gpram(struct mtk_snfi_priv *priv, u8 *cache,
|
||||
void *data, size_t pos, size_t len)
|
||||
{
|
||||
void __iomem *gpram = priv->base + SNFI_GPRAM_OFFSET;
|
||||
u32 *buff = (u32 *)cache;
|
||||
size_t i, off, end;
|
||||
|
||||
/* Start position in the buffer */
|
||||
off = pos & (sizeof(u32) - 1);
|
||||
|
||||
/* End position for copy */
|
||||
end = (len + pos + sizeof(u32) - 1) & (~(sizeof(u32) - 1));
|
||||
|
||||
/* Start position for copy */
|
||||
pos &= ~(sizeof(u32) - 1);
|
||||
|
||||
/*
|
||||
* Read aligned data from GPRAM to buffer first.
|
||||
* Uses word read for better performace.
|
||||
*/
|
||||
i = 0;
|
||||
while (pos < end) {
|
||||
buff[i++] = readl(gpram + pos);
|
||||
pos += sizeof(u32);
|
||||
}
|
||||
|
||||
/* Copy rx data */
|
||||
memcpy(data, cache + off, len);
|
||||
}
|
||||
|
||||
static int mtk_snfi_exec_op(struct spi_slave *slave,
|
||||
const struct spi_mem_op *op)
|
||||
{
|
||||
struct udevice *bus = dev_get_parent(slave->dev);
|
||||
struct mtk_snfi_priv *priv = dev_get_priv(bus);
|
||||
u8 gpram_cache[SNFI_GPRAM_SIZE];
|
||||
u32 i, len = 0, inlen = 0;
|
||||
int addr_sh;
|
||||
int ret;
|
||||
|
||||
WATCHDOG_RESET();
|
||||
|
||||
ret = mtk_snfi_mac_reset(priv);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Put opcode */
|
||||
gpram_cache[len++] = op->cmd.opcode;
|
||||
|
||||
/* Put address */
|
||||
addr_sh = (op->addr.nbytes - 1) * 8;
|
||||
while (addr_sh >= 0) {
|
||||
gpram_cache[len++] = (op->addr.val >> addr_sh) & 0xff;
|
||||
addr_sh -= 8;
|
||||
}
|
||||
|
||||
/* Put dummy bytes */
|
||||
for (i = 0; i < op->dummy.nbytes; i++)
|
||||
gpram_cache[len++] = 0;
|
||||
|
||||
/* Put output data */
|
||||
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_OUT) {
|
||||
memcpy(gpram_cache + len, op->data.buf.out, op->data.nbytes);
|
||||
len += op->data.nbytes;
|
||||
}
|
||||
|
||||
/* Copy final output data to GPRAM */
|
||||
mtk_snfi_copy_to_gpram(priv, gpram_cache, len);
|
||||
|
||||
/* Start one SPI transaction */
|
||||
if (op->data.nbytes && op->data.dir == SPI_MEM_DATA_IN)
|
||||
inlen = op->data.nbytes;
|
||||
|
||||
ret = mtk_snfi_mac_trigger(priv, bus, len, inlen);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Copy input data from GPRAM */
|
||||
if (inlen)
|
||||
mtk_snfi_copy_from_gpram(priv, gpram_cache, op->data.buf.in,
|
||||
len, inlen);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_snfi_spi_probe(struct udevice *bus)
|
||||
{
|
||||
struct mtk_snfi_priv *priv = dev_get_priv(bus);
|
||||
int ret;
|
||||
|
||||
priv->base = (void __iomem *)devfdt_get_addr(bus);
|
||||
if (!priv->base)
|
||||
return -EINVAL;
|
||||
|
||||
ret = clk_get_by_name(bus, "nfi_clk", &priv->nfi_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = clk_get_by_name(bus, "pad_clk", &priv->pad_clk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
clk_enable(&priv->nfi_clk);
|
||||
clk_enable(&priv->pad_clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_snfi_set_speed(struct udevice *bus, uint speed)
|
||||
{
|
||||
/*
|
||||
* The SNFI does not have a bus clock divider.
|
||||
* The bus clock is set in dts (pad_clk, UNIVPLL2_D8 = 50MHz).
|
||||
*/
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_snfi_set_mode(struct udevice *bus, uint mode)
|
||||
{
|
||||
/* The SNFI supports only mode 0 */
|
||||
|
||||
if (mode)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct spi_controller_mem_ops mtk_snfi_mem_ops = {
|
||||
.adjust_op_size = mtk_snfi_adjust_op_size,
|
||||
.supports_op = mtk_snfi_supports_op,
|
||||
.exec_op = mtk_snfi_exec_op,
|
||||
};
|
||||
|
||||
static const struct dm_spi_ops mtk_snfi_spi_ops = {
|
||||
.mem_ops = &mtk_snfi_mem_ops,
|
||||
.set_speed = mtk_snfi_set_speed,
|
||||
.set_mode = mtk_snfi_set_mode,
|
||||
};
|
||||
|
||||
static const struct udevice_id mtk_snfi_spi_ids[] = {
|
||||
{ .compatible = "mediatek,mtk-snfi-spi" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(mtk_snfi_spi) = {
|
||||
.name = "mtk_snfi_spi",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = mtk_snfi_spi_ids,
|
||||
.ops = &mtk_snfi_spi_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct mtk_snfi_priv),
|
||||
.probe = mtk_snfi_spi_probe,
|
||||
};
|
@ -430,12 +430,14 @@ int spi_mem_adjust_op_size(struct spi_slave *slave, struct spi_mem_op *op)
|
||||
if (slave->max_write_size && len > slave->max_write_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (op->data.dir == SPI_MEM_DATA_IN && slave->max_read_size)
|
||||
op->data.nbytes = min(op->data.nbytes,
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
if (slave->max_read_size)
|
||||
op->data.nbytes = min(op->data.nbytes,
|
||||
slave->max_read_size);
|
||||
else if (slave->max_write_size)
|
||||
} else if (slave->max_write_size) {
|
||||
op->data.nbytes = min(op->data.nbytes,
|
||||
slave->max_write_size - len);
|
||||
}
|
||||
|
||||
if (!op->data.nbytes)
|
||||
return -EINVAL;
|
||||
|
370
drivers/spi/spi-sifive.c
Normal file
370
drivers/spi/spi-sifive.c
Normal file
@ -0,0 +1,370 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright 2018 SiFive, Inc.
|
||||
* Copyright 2019 Bhargav Shah <bhargavshah1988@gmail.com>
|
||||
*
|
||||
* SiFive SPI controller driver (master mode only)
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <malloc.h>
|
||||
#include <spi.h>
|
||||
#include <asm/io.h>
|
||||
#include <linux/log2.h>
|
||||
#include <clk.h>
|
||||
|
||||
#define SIFIVE_SPI_MAX_CS 32
|
||||
|
||||
#define SIFIVE_SPI_DEFAULT_DEPTH 8
|
||||
#define SIFIVE_SPI_DEFAULT_BITS 8
|
||||
|
||||
/* register offsets */
|
||||
#define SIFIVE_SPI_REG_SCKDIV 0x00 /* Serial clock divisor */
|
||||
#define SIFIVE_SPI_REG_SCKMODE 0x04 /* Serial clock mode */
|
||||
#define SIFIVE_SPI_REG_CSID 0x10 /* Chip select ID */
|
||||
#define SIFIVE_SPI_REG_CSDEF 0x14 /* Chip select default */
|
||||
#define SIFIVE_SPI_REG_CSMODE 0x18 /* Chip select mode */
|
||||
#define SIFIVE_SPI_REG_DELAY0 0x28 /* Delay control 0 */
|
||||
#define SIFIVE_SPI_REG_DELAY1 0x2c /* Delay control 1 */
|
||||
#define SIFIVE_SPI_REG_FMT 0x40 /* Frame format */
|
||||
#define SIFIVE_SPI_REG_TXDATA 0x48 /* Tx FIFO data */
|
||||
#define SIFIVE_SPI_REG_RXDATA 0x4c /* Rx FIFO data */
|
||||
#define SIFIVE_SPI_REG_TXMARK 0x50 /* Tx FIFO watermark */
|
||||
#define SIFIVE_SPI_REG_RXMARK 0x54 /* Rx FIFO watermark */
|
||||
#define SIFIVE_SPI_REG_FCTRL 0x60 /* SPI flash interface control */
|
||||
#define SIFIVE_SPI_REG_FFMT 0x64 /* SPI flash instruction format */
|
||||
#define SIFIVE_SPI_REG_IE 0x70 /* Interrupt Enable Register */
|
||||
#define SIFIVE_SPI_REG_IP 0x74 /* Interrupt Pendings Register */
|
||||
|
||||
/* sckdiv bits */
|
||||
#define SIFIVE_SPI_SCKDIV_DIV_MASK 0xfffU
|
||||
|
||||
/* sckmode bits */
|
||||
#define SIFIVE_SPI_SCKMODE_PHA BIT(0)
|
||||
#define SIFIVE_SPI_SCKMODE_POL BIT(1)
|
||||
#define SIFIVE_SPI_SCKMODE_MODE_MASK (SIFIVE_SPI_SCKMODE_PHA | \
|
||||
SIFIVE_SPI_SCKMODE_POL)
|
||||
|
||||
/* csmode bits */
|
||||
#define SIFIVE_SPI_CSMODE_MODE_AUTO 0U
|
||||
#define SIFIVE_SPI_CSMODE_MODE_HOLD 2U
|
||||
#define SIFIVE_SPI_CSMODE_MODE_OFF 3U
|
||||
|
||||
/* delay0 bits */
|
||||
#define SIFIVE_SPI_DELAY0_CSSCK(x) ((u32)(x))
|
||||
#define SIFIVE_SPI_DELAY0_CSSCK_MASK 0xffU
|
||||
#define SIFIVE_SPI_DELAY0_SCKCS(x) ((u32)(x) << 16)
|
||||
#define SIFIVE_SPI_DELAY0_SCKCS_MASK (0xffU << 16)
|
||||
|
||||
/* delay1 bits */
|
||||
#define SIFIVE_SPI_DELAY1_INTERCS(x) ((u32)(x))
|
||||
#define SIFIVE_SPI_DELAY1_INTERCS_MASK 0xffU
|
||||
#define SIFIVE_SPI_DELAY1_INTERXFR(x) ((u32)(x) << 16)
|
||||
#define SIFIVE_SPI_DELAY1_INTERXFR_MASK (0xffU << 16)
|
||||
|
||||
/* fmt bits */
|
||||
#define SIFIVE_SPI_FMT_PROTO_SINGLE 0U
|
||||
#define SIFIVE_SPI_FMT_PROTO_DUAL 1U
|
||||
#define SIFIVE_SPI_FMT_PROTO_QUAD 2U
|
||||
#define SIFIVE_SPI_FMT_PROTO_MASK 3U
|
||||
#define SIFIVE_SPI_FMT_ENDIAN BIT(2)
|
||||
#define SIFIVE_SPI_FMT_DIR BIT(3)
|
||||
#define SIFIVE_SPI_FMT_LEN(x) ((u32)(x) << 16)
|
||||
#define SIFIVE_SPI_FMT_LEN_MASK (0xfU << 16)
|
||||
|
||||
/* txdata bits */
|
||||
#define SIFIVE_SPI_TXDATA_DATA_MASK 0xffU
|
||||
#define SIFIVE_SPI_TXDATA_FULL BIT(31)
|
||||
|
||||
/* rxdata bits */
|
||||
#define SIFIVE_SPI_RXDATA_DATA_MASK 0xffU
|
||||
#define SIFIVE_SPI_RXDATA_EMPTY BIT(31)
|
||||
|
||||
/* ie and ip bits */
|
||||
#define SIFIVE_SPI_IP_TXWM BIT(0)
|
||||
#define SIFIVE_SPI_IP_RXWM BIT(1)
|
||||
|
||||
struct sifive_spi {
|
||||
void *regs; /* base address of the registers */
|
||||
u32 fifo_depth;
|
||||
u32 bits_per_word;
|
||||
u32 cs_inactive; /* Level of the CS pins when inactive*/
|
||||
u32 freq;
|
||||
u32 num_cs;
|
||||
};
|
||||
|
||||
static void sifive_spi_prep_device(struct sifive_spi *spi,
|
||||
struct dm_spi_slave_platdata *slave)
|
||||
{
|
||||
/* Update the chip select polarity */
|
||||
if (slave->mode & SPI_CS_HIGH)
|
||||
spi->cs_inactive &= ~BIT(slave->cs);
|
||||
else
|
||||
spi->cs_inactive |= BIT(slave->cs);
|
||||
writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
|
||||
|
||||
/* Select the correct device */
|
||||
writel(slave->cs, spi->regs + SIFIVE_SPI_REG_CSID);
|
||||
}
|
||||
|
||||
static int sifive_spi_set_cs(struct sifive_spi *spi,
|
||||
struct dm_spi_slave_platdata *slave)
|
||||
{
|
||||
u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD;
|
||||
|
||||
if (slave->mode & SPI_CS_HIGH)
|
||||
cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO;
|
||||
|
||||
writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_spi_clear_cs(struct sifive_spi *spi)
|
||||
{
|
||||
writel(SIFIVE_SPI_CSMODE_MODE_AUTO, spi->regs + SIFIVE_SPI_REG_CSMODE);
|
||||
}
|
||||
|
||||
static void sifive_spi_prep_transfer(struct sifive_spi *spi,
|
||||
bool is_rx_xfer,
|
||||
struct dm_spi_slave_platdata *slave)
|
||||
{
|
||||
u32 cr;
|
||||
|
||||
/* Modify the SPI protocol mode */
|
||||
cr = readl(spi->regs + SIFIVE_SPI_REG_FMT);
|
||||
|
||||
/* Bits per word ? */
|
||||
cr &= ~SIFIVE_SPI_FMT_LEN_MASK;
|
||||
cr |= SIFIVE_SPI_FMT_LEN(spi->bits_per_word);
|
||||
|
||||
/* LSB first? */
|
||||
cr &= ~SIFIVE_SPI_FMT_ENDIAN;
|
||||
if (slave->mode & SPI_LSB_FIRST)
|
||||
cr |= SIFIVE_SPI_FMT_ENDIAN;
|
||||
|
||||
/* Number of wires ? */
|
||||
cr &= ~SIFIVE_SPI_FMT_PROTO_MASK;
|
||||
if ((slave->mode & SPI_TX_QUAD) || (slave->mode & SPI_RX_QUAD))
|
||||
cr |= SIFIVE_SPI_FMT_PROTO_QUAD;
|
||||
else if ((slave->mode & SPI_TX_DUAL) || (slave->mode & SPI_RX_DUAL))
|
||||
cr |= SIFIVE_SPI_FMT_PROTO_DUAL;
|
||||
else
|
||||
cr |= SIFIVE_SPI_FMT_PROTO_SINGLE;
|
||||
|
||||
/* SPI direction in/out ? */
|
||||
cr &= ~SIFIVE_SPI_FMT_DIR;
|
||||
if (!is_rx_xfer)
|
||||
cr |= SIFIVE_SPI_FMT_DIR;
|
||||
|
||||
writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
|
||||
}
|
||||
|
||||
static void sifive_spi_rx(struct sifive_spi *spi, u8 *rx_ptr)
|
||||
{
|
||||
u32 data;
|
||||
|
||||
do {
|
||||
data = readl(spi->regs + SIFIVE_SPI_REG_RXDATA);
|
||||
} while (data & SIFIVE_SPI_RXDATA_EMPTY);
|
||||
|
||||
if (rx_ptr)
|
||||
*rx_ptr = data & SIFIVE_SPI_RXDATA_DATA_MASK;
|
||||
}
|
||||
|
||||
static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
|
||||
{
|
||||
u32 data;
|
||||
u8 tx_data = (tx_ptr) ? *tx_ptr & SIFIVE_SPI_TXDATA_DATA_MASK :
|
||||
SIFIVE_SPI_TXDATA_DATA_MASK;
|
||||
|
||||
do {
|
||||
data = readl(spi->regs + SIFIVE_SPI_REG_TXDATA);
|
||||
} while (data & SIFIVE_SPI_TXDATA_FULL);
|
||||
|
||||
writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
|
||||
}
|
||||
|
||||
static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
|
||||
const void *dout, void *din, unsigned long flags)
|
||||
{
|
||||
struct udevice *bus = dev->parent;
|
||||
struct sifive_spi *spi = dev_get_priv(bus);
|
||||
struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
|
||||
const unsigned char *tx_ptr = dout;
|
||||
u8 *rx_ptr = din;
|
||||
u32 remaining_len;
|
||||
int ret;
|
||||
|
||||
if (flags & SPI_XFER_BEGIN) {
|
||||
sifive_spi_prep_device(spi, slave);
|
||||
|
||||
ret = sifive_spi_set_cs(spi, slave);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
sifive_spi_prep_transfer(spi, true, slave);
|
||||
|
||||
remaining_len = bitlen / 8;
|
||||
|
||||
while (remaining_len) {
|
||||
int n_words, tx_words, rx_words;
|
||||
|
||||
n_words = min(remaining_len, spi->fifo_depth);
|
||||
|
||||
/* Enqueue n_words for transmission */
|
||||
if (tx_ptr) {
|
||||
for (tx_words = 0; tx_words < n_words; ++tx_words) {
|
||||
sifive_spi_tx(spi, tx_ptr);
|
||||
sifive_spi_rx(spi, NULL);
|
||||
tx_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Read out all the data from the RX FIFO */
|
||||
if (rx_ptr) {
|
||||
for (rx_words = 0; rx_words < n_words; ++rx_words) {
|
||||
sifive_spi_tx(spi, NULL);
|
||||
sifive_spi_rx(spi, rx_ptr);
|
||||
rx_ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
remaining_len -= n_words;
|
||||
}
|
||||
|
||||
if (flags & SPI_XFER_END)
|
||||
sifive_spi_clear_cs(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_spi_set_speed(struct udevice *bus, uint speed)
|
||||
{
|
||||
struct sifive_spi *spi = dev_get_priv(bus);
|
||||
u32 scale;
|
||||
|
||||
if (speed > spi->freq)
|
||||
speed = spi->freq;
|
||||
|
||||
/* Cofigure max speed */
|
||||
scale = (DIV_ROUND_UP(spi->freq >> 1, speed) - 1)
|
||||
& SIFIVE_SPI_SCKDIV_DIV_MASK;
|
||||
writel(scale, spi->regs + SIFIVE_SPI_REG_SCKDIV);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_spi_set_mode(struct udevice *bus, uint mode)
|
||||
{
|
||||
struct sifive_spi *spi = dev_get_priv(bus);
|
||||
u32 cr;
|
||||
|
||||
/* Switch clock mode bits */
|
||||
cr = readl(spi->regs + SIFIVE_SPI_REG_SCKMODE) &
|
||||
~SIFIVE_SPI_SCKMODE_MODE_MASK;
|
||||
if (mode & SPI_CPHA)
|
||||
cr |= SIFIVE_SPI_SCKMODE_PHA;
|
||||
if (mode & SPI_CPOL)
|
||||
cr |= SIFIVE_SPI_SCKMODE_POL;
|
||||
|
||||
writel(cr, spi->regs + SIFIVE_SPI_REG_SCKMODE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sifive_spi_cs_info(struct udevice *bus, uint cs,
|
||||
struct spi_cs_info *info)
|
||||
{
|
||||
struct sifive_spi *spi = dev_get_priv(bus);
|
||||
|
||||
if (cs >= spi->num_cs)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void sifive_spi_init_hw(struct sifive_spi *spi)
|
||||
{
|
||||
u32 cs_bits;
|
||||
|
||||
/* probe the number of CS lines */
|
||||
spi->cs_inactive = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
|
||||
writel(0xffffffffU, spi->regs + SIFIVE_SPI_REG_CSDEF);
|
||||
cs_bits = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
|
||||
writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
|
||||
if (!cs_bits) {
|
||||
printf("Could not auto probe CS lines\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spi->num_cs = ilog2(cs_bits) + 1;
|
||||
if (spi->num_cs > SIFIVE_SPI_MAX_CS) {
|
||||
printf("Invalid number of spi slaves\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Watermark interrupts are disabled by default */
|
||||
writel(0, spi->regs + SIFIVE_SPI_REG_IE);
|
||||
|
||||
/* Set CS/SCK Delays and Inactive Time to defaults */
|
||||
writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
|
||||
spi->regs + SIFIVE_SPI_REG_DELAY0);
|
||||
writel(SIFIVE_SPI_DELAY1_INTERCS(1) | SIFIVE_SPI_DELAY1_INTERXFR(0),
|
||||
spi->regs + SIFIVE_SPI_REG_DELAY1);
|
||||
|
||||
/* Exit specialized memory-mapped SPI flash mode */
|
||||
writel(0, spi->regs + SIFIVE_SPI_REG_FCTRL);
|
||||
}
|
||||
|
||||
static int sifive_spi_probe(struct udevice *bus)
|
||||
{
|
||||
struct sifive_spi *spi = dev_get_priv(bus);
|
||||
struct clk clkdev;
|
||||
int ret;
|
||||
|
||||
spi->regs = (void *)(ulong)dev_remap_addr(bus);
|
||||
if (!spi->regs)
|
||||
return -ENODEV;
|
||||
|
||||
spi->fifo_depth = dev_read_u32_default(bus,
|
||||
"sifive,fifo-depth",
|
||||
SIFIVE_SPI_DEFAULT_DEPTH);
|
||||
|
||||
spi->bits_per_word = dev_read_u32_default(bus,
|
||||
"sifive,max-bits-per-word",
|
||||
SIFIVE_SPI_DEFAULT_BITS);
|
||||
|
||||
ret = clk_get_by_index(bus, 0, &clkdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
spi->freq = clk_get_rate(&clkdev);
|
||||
|
||||
/* init the sifive spi hw */
|
||||
sifive_spi_init_hw(spi);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dm_spi_ops sifive_spi_ops = {
|
||||
.xfer = sifive_spi_xfer,
|
||||
.set_speed = sifive_spi_set_speed,
|
||||
.set_mode = sifive_spi_set_mode,
|
||||
.cs_info = sifive_spi_cs_info,
|
||||
};
|
||||
|
||||
static const struct udevice_id sifive_spi_ids[] = {
|
||||
{ .compatible = "sifive,spi0" },
|
||||
{ }
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(sifive_spi) = {
|
||||
.name = "sifive_spi",
|
||||
.id = UCLASS_SPI,
|
||||
.of_match = sifive_spi_ids,
|
||||
.ops = &sifive_spi_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct sifive_spi),
|
||||
.probe = sifive_spi_probe,
|
||||
};
|
Loading…
Reference in New Issue
Block a user