
Hi Jagan,
On 31 August 2015 at 23:23, Jagan Teki jteki@openedev.com wrote:
On 31 August 2015 at 04:25, Simon Glass sjg@chromium.org wrote:
Add a SPI driver for the Rockchip RK3288, using driver model. It should work for other Rockchip SoCs also.
Signed-off-by: Simon Glass sjg@chromium.org
Changes in v5:
- Drop unnecessary 'depends on DM_SPI' in Kconfig
- Tidy up rkspi_dump_regs() debug output
- Tidy up the delay in rkspi_wait_till_not_busy()
Changes in v4:
- Rename pinctrl.h to dm/pinctrl.h
Changes in v3: None Changes in v2: None
drivers/spi/Kconfig | 8 ++ drivers/spi/Makefile | 1 + drivers/spi/rk_spi.c | 372 +++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/spi/rk_spi.h | 121 +++++++++++++++++ 4 files changed, 502 insertions(+) create mode 100644 drivers/spi/rk_spi.c create mode 100644 drivers/spi/rk_spi.h
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index c84a7b7..8e04fce 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -58,6 +58,14 @@ config ICH_SPI access the SPI NOR flash on platforms embedding this Intel ICH IP core.
+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 diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index ee88aa1..de241be 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -39,6 +39,7 @@ 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_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 diff --git a/drivers/spi/rk_spi.c b/drivers/spi/rk_spi.c new file mode 100644 index 0000000..46b1a37 --- /dev/null +++ b/drivers/spi/rk_spi.c @@ -0,0 +1,372 @@ +/*
- 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"
Please move the header file data here
I would prefer to keep the declarations separate. They are not needed in order to understand this code, and in fact just clutter it up. This pattern is used widely in U-Boot and Linux.
+DECLARE_GLOBAL_DATA_PTR;
+struct rockchip_spi_platdata {
enum periph_id periph_id;
struct udevice *pinctrl;
s32 frequency; /* Default clock frequency, -1 for none */
fdt_addr_t base;
uint deactivate_delay_us; /* Delay to wait after deactivate */
+};
+struct rockchip_spi_priv {
struct rockchip_spi *regs;
struct udevice *clk_gpll;
unsigned int max_freq;
unsigned int mode;
enum periph_id periph_id; /* Peripheral ID for this device */
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 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 = priv->input_rate / speed;
clk_div = (clk_div + 1) & 0xfffe;
Define a clk divisor macro.
OK done.
debug("spi speed %u, div %u\n", speed, clk_div);
writel(clk_div, &priv->regs->baudr);
+}
+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) > 1000) {
Define macro similar to CONFIG_SYS_ROCKCHIP_SPI_WAIT
OK done
debug("RK SPI: Status keeps busy for 1000us after a read/write!\n");
return -ETIMEDOUT;
}
}
return 0;
+}
+static void spi_cs_activate(struct rockchip_spi *regs, uint cs) +{
debug("activate cs%u\n", cs);
writel(1 << cs, ®s->ser);
+}
+static void spi_cs_deactivate(struct rockchip_spi *regs, uint cs) +{
debug("deactivate cs%u\n", cs);
writel(0, ®s->ser);
+}
+static int rockchip_spi_ofdata_to_platdata(struct udevice *bus) +{
struct rockchip_spi_platdata *plat = bus->platdata;
const void *blob = gd->fdt_blob;
int node = bus->of_offset;
int ret;
plat->base = dev_get_addr(bus);
ret = uclass_get_device(UCLASS_PINCTRL, 0, &plat->pinctrl);
if (ret)
return ret;
ret = pinctrl_get_periph_id(plat->pinctrl, bus);
if (ret < 0) {
debug("%s: Could not get peripheral ID for %s: %d\n", __func__,
bus->name, ret);
return -FDT_ERR_NOTFOUND;
}
plat->periph_id = 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);
debug("%s: base=%x, 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 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;
priv->periph_id = plat->periph_id;
ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk_gpll);
if (ret) {
debug("%s: Failed to find CLK_GENERAL: %d\n", __func__, ret);
return ret;
}
/*
* Use 99 MHz as our clock since it divides nicely into 594 MHz which
* is the assumed speed for CLK_GENERAL.
*/
ret = clk_set_periph_rate(priv->clk_gpll, plat->periph_id, 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_platdata *plat = dev_get_platdata(bus);
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);
u8 spi_dfs, spi_tf;
uint ctrlr0;
int ret;
/* 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;
}
rkspi_set_clk(priv, priv->speed_hz);
/* Operation Mode */
ctrlr0 = OMOD_MASTER << OMOD_SHIFT;
/* Data Frame Size */
ctrlr0 |= spi_dfs & DFS_MASK << 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_MASK) << 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);
ret = pinctrl_request(plat->pinctrl, priv->periph_id, slave_plat->cs);
if (ret) {
debug("%s: Cannot request pinctrl: %d\n", __func__, ret);
return ret;
}
return 0;
+}
+static int rockchip_spi_release_bus(struct udevice *dev) +{
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 (0)
rkspi_dump_regs(regs);
if (0) is really needed?
I'll change it to use #ifdef above.
/* Assert CS before transfer */
if (flags & SPI_XFER_BEGIN)
spi_cs_activate(regs, slave_plat->cs);
while (len > 0) {
int todo = min(len, 0xffff);
rkspi_enable_chip(regs, true);
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(regs, 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 > 48000000)
Comment or macro for this numerical.
OK done.
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,
+};
[snip]
Regards, Simon