u-boot-brain/drivers/spi/sh_qspi.c
Simon Glass caa4daa2ae dm: treewide: Rename 'platdata' variables to just 'plat'
We use 'priv' for private data but often use 'platdata' for platform data.
We can't really use 'pdata' since that is ambiguous (it could mean private
or platform data).

Rename some of the latter variables to end with 'plat' for consistency.

Signed-off-by: Simon Glass <sjg@chromium.org>
2020-12-13 16:51:08 -07:00

361 lines
7.5 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* SH QSPI (Quad SPI) driver
*
* Copyright (C) 2013 Renesas Electronics Corporation
* Copyright (C) 2013 Nobuhiro Iwamatsu <nobuhiro.iwamatsu.yj@renesas.com>
*/
#include <common.h>
#include <console.h>
#include <malloc.h>
#include <spi.h>
#include <wait_bit.h>
#include <asm/arch/rmobile.h>
#include <asm/io.h>
#include <linux/bitops.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)
#define SPBFCR_TXTRG 0x30
#define SPBFCR_RXTRG 0x07
/* SH QSPI register set */
struct sh_qspi_regs {
u8 spcr;
u8 sslp;
u8 sppcr;
u8 spsr;
u32 spdr;
u8 spscr;
u8 spssr;
u8 spbr;
u8 spdcr;
u8 spckd;
u8 sslnd;
u8 spnd;
u8 dummy0;
u16 spcmd0;
u16 spcmd1;
u16 spcmd2;
u16 spcmd3;
u8 spbfcr;
u8 dummy1;
u16 spbdcr;
u32 spbmul0;
u32 spbmul1;
u32 spbmul2;
u32 spbmul3;
};
struct sh_qspi_slave {
#if !CONFIG_IS_ENABLED(DM_SPI)
struct spi_slave slave;
#endif
struct sh_qspi_regs *regs;
};
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);
}
static void sh_qspi_cs_activate(struct sh_qspi_slave *ss)
{
/* 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);
}
static void sh_qspi_cs_deactivate(struct sh_qspi_slave *ss)
{
/* Disable SPI Function */
clrbits_8(&ss->regs->spcr, SPCR_SPE);
}
static int sh_qspi_xfer_common(struct sh_qspi_slave *ss, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
u32 nbyte, chunk;
int i, ret = 0;
u8 dtdata = 0, drdata;
u8 *tdata = &dtdata, *rdata = &drdata;
u32 *spbmul0 = &ss->regs->spbmul0;
if (dout == NULL && din == NULL) {
if (flags & SPI_XFER_END)
sh_qspi_cs_deactivate(ss);
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) {
sh_qspi_cs_activate(ss);
/* Set 1048576 byte */
writel(0x100000, spbmul0);
}
if (flags & SPI_XFER_END)
writel(nbyte, spbmul0);
if (dout != NULL)
tdata = (u8 *)dout;
if (din != NULL)
rdata = din;
while (nbyte > 0) {
/*
* Check if there is 32 Byte chunk and if there is, transfer
* it in one burst, otherwise transfer on byte-by-byte basis.
*/
chunk = (nbyte >= 32) ? 32 : 1;
clrsetbits_8(&ss->regs->spbfcr, SPBFCR_TXTRG | SPBFCR_RXTRG,
chunk == 32 ? SPBFCR_TXTRG | SPBFCR_RXTRG : 0);
ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPTEF,
true, 1000, true);
if (ret)
return ret;
for (i = 0; i < chunk; i++) {
writeb(*tdata, &ss->regs->spdr);
if (dout != NULL)
tdata++;
}
ret = wait_for_bit_8(&ss->regs->spsr, SPSR_SPRFF,
true, 1000, true);
if (ret)
return ret;
for (i = 0; i < chunk; i++) {
*rdata = readb(&ss->regs->spdr);
if (din != NULL)
rdata++;
}
nbyte -= chunk;
}
if (flags & SPI_XFER_END)
sh_qspi_cs_deactivate(ss);
return ret;
}
#if !CONFIG_IS_ENABLED(DM_SPI)
static inline struct sh_qspi_slave *to_sh_qspi(struct spi_slave *slave)
{
return container_of(slave, struct sh_qspi_slave, slave);
}
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);
sh_qspi_cs_activate(ss);
}
void spi_cs_deactivate(struct spi_slave *slave)
{
struct sh_qspi_slave *ss = to_sh_qspi(slave);
sh_qspi_cs_deactivate(ss);
}
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);
return sh_qspi_xfer_common(ss, bitlen, dout, din, flags);
}
#else
#include <dm.h>
static int sh_qspi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
struct udevice *bus = dev->parent;
struct sh_qspi_slave *ss = dev_get_platdata(bus);
return sh_qspi_xfer_common(ss, bitlen, dout, din, flags);
}
static int sh_qspi_set_speed(struct udevice *dev, uint speed)
{
/* This is a SPI NOR controller, do nothing. */
return 0;
}
static int sh_qspi_set_mode(struct udevice *dev, uint mode)
{
/* This is a SPI NOR controller, do nothing. */
return 0;
}
static int sh_qspi_probe(struct udevice *dev)
{
struct sh_qspi_slave *ss = dev_get_platdata(dev);
sh_qspi_init(ss);
return 0;
}
static int sh_qspi_ofdata_to_platdata(struct udevice *dev)
{
struct sh_qspi_slave *plat = dev_get_platdata(dev);
plat->regs = (struct sh_qspi_regs *)dev_read_addr(dev);
return 0;
}
static const struct dm_spi_ops sh_qspi_ops = {
.xfer = sh_qspi_xfer,
.set_speed = sh_qspi_set_speed,
.set_mode = sh_qspi_set_mode,
};
static const struct udevice_id sh_qspi_ids[] = {
{ .compatible = "renesas,qspi" },
{ }
};
U_BOOT_DRIVER(sh_qspi) = {
.name = "sh_qspi",
.id = UCLASS_SPI,
.of_match = sh_qspi_ids,
.ops = &sh_qspi_ops,
.ofdata_to_platdata = sh_qspi_ofdata_to_platdata,
.plat_auto = sizeof(struct sh_qspi_slave),
.probe = sh_qspi_probe,
};
#endif