summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <trini@konsulko.com>2015-09-03 14:57:09 -0400
committerTom Rini <trini@konsulko.com>2015-09-03 14:57:09 -0400
commitc9feb427aba860ebc79f9851a1bb49cc456a2d48 (patch)
tree6a80def1806386e2854d57f740264c6744d96e1d /drivers
parentda9d8580ff9ce17452ef931072e5799a9df8807f (diff)
parentf2acc55e3d28e96a6fcc060a7081eb4e2ad96350 (diff)
downloadtalos-obmc-uboot-c9feb427aba860ebc79f9851a1bb49cc456a2d48.tar.gz
talos-obmc-uboot-c9feb427aba860ebc79f9851a1bb49cc456a2d48.zip
Merge git://git.denx.de/u-boot-rockchip
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk_rk3288.c618
-rw-r--r--drivers/core/device.c4
-rw-r--r--drivers/core/root.c5
-rw-r--r--drivers/core/uclass.c7
-rw-r--r--drivers/gpio/Kconfig9
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/rk_gpio.c123
-rw-r--r--drivers/i2c/Kconfig9
-rw-r--r--drivers/i2c/Makefile1
-rw-r--r--drivers/i2c/rk_i2c.c391
-rw-r--r--drivers/led/Kconfig9
-rw-r--r--drivers/led/Makefile4
-rw-r--r--drivers/mmc/Kconfig9
-rw-r--r--drivers/mmc/Makefile1
-rw-r--r--drivers/mmc/dw_mmc.c2
-rw-r--r--drivers/mmc/exynos_dw_mmc.c2
-rw-r--r--drivers/mmc/mmc.c43
-rw-r--r--drivers/mmc/rockchip_dw_mmc.c98
-rw-r--r--drivers/pinctrl/Kconfig20
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-uclass.c40
-rw-r--r--drivers/pinctrl/rockchip/Makefile8
-rw-r--r--drivers/pinctrl/rockchip/pinctrl_rk3288.c441
-rw-r--r--drivers/power/pmic/Kconfig9
-rw-r--r--drivers/power/pmic/Makefile1
-rw-r--r--drivers/power/pmic/act8846.c90
-rw-r--r--drivers/power/regulator/Kconfig9
-rw-r--r--drivers/power/regulator/Makefile1
-rw-r--r--drivers/power/regulator/act8846.c155
-rw-r--r--drivers/serial/Kconfig9
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/serial_rockchip.c43
-rw-r--r--drivers/spi/Kconfig8
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/rk_spi.c374
-rw-r--r--drivers/spi/rk_spi.h124
37 files changed, 2650 insertions, 22 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index bb89fb918b..008ec100a1 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -6,4 +6,5 @@
#
obj-$(CONFIG_CLK) += clk-uclass.o
+obj-$(CONFIG_ROCKCHIP_RK3288) += clk_rk3288.o
obj-$(CONFIG_SANDBOX) += clk_sandbox.o
diff --git a/drivers/clk/clk_rk3288.c b/drivers/clk/clk_rk3288.c
new file mode 100644
index 0000000000..54d49305b0
--- /dev/null
+++ b/drivers/clk/clk_rk3288.c
@@ -0,0 +1,618 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/cru_rk3288.h>
+#include <asm/arch/grf_rk3288.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/periph.h>
+#include <dm/lists.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rk3288_clk_plat {
+ enum rk_clk_id clk_id;
+};
+
+struct rk3288_clk_priv {
+ struct rk3288_grf *grf;
+ struct rk3288_cru *cru;
+ ulong rate;
+};
+
+struct pll_div {
+ u32 nr;
+ u32 nf;
+ u32 no;
+};
+
+enum {
+ VCO_MAX_HZ = 2200U * 1000000,
+ VCO_MIN_HZ = 440 * 1000000,
+ OUTPUT_MAX_HZ = 2200U * 1000000,
+ OUTPUT_MIN_HZ = 27500000,
+ FREF_MAX_HZ = 2200U * 1000000,
+ FREF_MIN_HZ = 269 * 1000000,
+};
+
+enum {
+ /* PLL CON0 */
+ PLL_OD_MASK = 0x0f,
+
+ /* PLL CON1 */
+ PLL_NF_MASK = 0x1fff,
+
+ /* PLL CON2 */
+ PLL_BWADJ_MASK = 0x0fff,
+
+ /* PLL CON3 */
+ PLL_RESET_SHIFT = 5,
+
+ /* CLKSEL1: pd bus clk pll sel: codec or general */
+ PD_BUS_SEL_PLL_MASK = 15,
+ PD_BUS_SEL_CPLL = 0,
+ PD_BUS_SEL_GPLL,
+
+ /* pd bus pclk div: pclk = pd_bus_aclk /(div + 1) */
+ PD_BUS_PCLK_DIV_SHIFT = 12,
+ PD_BUS_PCLK_DIV_MASK = 7,
+
+ /* pd bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */
+ PD_BUS_HCLK_DIV_SHIFT = 8,
+ PD_BUS_HCLK_DIV_MASK = 3,
+
+ /* pd bus aclk div: pd_bus_aclk = pd_bus_src_clk /(div0 * div1) */
+ PD_BUS_ACLK_DIV0_SHIFT = 3,
+ PD_BUS_ACLK_DIV0_MASK = 0x1f,
+ PD_BUS_ACLK_DIV1_SHIFT = 0,
+ PD_BUS_ACLK_DIV1_MASK = 0x7,
+
+ /*
+ * CLKSEL10
+ * peripheral bus pclk div:
+ * aclk_bus: pclk_bus = 1:1 or 2:1 or 4:1 or 8:1
+ */
+ PERI_PCLK_DIV_SHIFT = 12,
+ PERI_PCLK_DIV_MASK = 7,
+
+ /* peripheral bus hclk div: aclk_bus: hclk_bus = 1:1 or 2:1 or 4:1 */
+ PERI_HCLK_DIV_SHIFT = 8,
+ PERI_HCLK_DIV_MASK = 3,
+
+ /*
+ * peripheral bus aclk div:
+ * aclk_periph = periph_clk_src / (peri_aclk_div_con + 1)
+ */
+ PERI_ACLK_DIV_SHIFT = 0,
+ PERI_ACLK_DIV_MASK = 0x1f,
+
+ /* CLKSEL37 */
+ DPLL_MODE_MASK = 0x3,
+ DPLL_MODE_SHIFT = 4,
+ DPLL_MODE_SLOW = 0,
+ DPLL_MODE_NORM,
+
+ CPLL_MODE_MASK = 3,
+ CPLL_MODE_SHIFT = 8,
+ CPLL_MODE_SLOW = 0,
+ CPLL_MODE_NORM,
+
+ GPLL_MODE_MASK = 3,
+ GPLL_MODE_SHIFT = 12,
+ GPLL_MODE_SLOW = 0,
+ GPLL_MODE_NORM,
+
+ NPLL_MODE_MASK = 3,
+ NPLL_MODE_SHIFT = 14,
+ NPLL_MODE_SLOW = 0,
+ NPLL_MODE_NORM,
+
+ SOCSTS_DPLL_LOCK = 1 << 5,
+ SOCSTS_APLL_LOCK = 1 << 6,
+ SOCSTS_CPLL_LOCK = 1 << 7,
+ SOCSTS_GPLL_LOCK = 1 << 8,
+ SOCSTS_NPLL_LOCK = 1 << 9,
+};
+
+#define RATE_TO_DIV(input_rate, output_rate) \
+ ((input_rate) / (output_rate) - 1);
+
+#define DIV_TO_RATE(input_rate, div) ((input_rate) / ((div) + 1))
+
+#define PLL_DIVISORS(hz, _nr, _no) {\
+ .nr = _nr, .nf = (u32)((u64)hz * _nr * _no / OSC_HZ), .no = _no};\
+ _Static_assert(((u64)hz * _nr * _no / OSC_HZ) * OSC_HZ /\
+ (_nr * _no) == hz, #hz "Hz cannot be hit with PLL "\
+ "divisors on line " __stringify(__LINE__));
+
+/* Keep divisors as low as possible to reduce jitter and power usage */
+static const struct pll_div apll_init_cfg = PLL_DIVISORS(APLL_HZ, 1, 1);
+static const struct pll_div gpll_init_cfg = PLL_DIVISORS(GPLL_HZ, 2, 2);
+static const struct pll_div cpll_init_cfg = PLL_DIVISORS(CPLL_HZ, 1, 2);
+
+static int rkclk_set_pll(struct rk3288_cru *cru, enum rk_clk_id clk_id,
+ const struct pll_div *div)
+{
+ int pll_id = rk_pll_id(clk_id);
+ struct rk3288_pll *pll = &cru->pll[pll_id];
+ /* All PLLs have same VCO and output frequency range restrictions. */
+ uint vco_hz = OSC_HZ / 1000 * div->nf / div->nr * 1000;
+ uint output_hz = vco_hz / div->no;
+
+ debug("PLL at %p: nf=%d, nr=%d, no=%d, vco=%u Hz, output=%u Hz\n",
+ pll, div->nf, div->nr, div->no, vco_hz, output_hz);
+ assert(vco_hz >= VCO_MIN_HZ && vco_hz <= VCO_MAX_HZ &&
+ output_hz >= OUTPUT_MIN_HZ && output_hz <= OUTPUT_MAX_HZ &&
+ (div->no == 1 || !(div->no % 2)));
+
+ /* enter rest */
+ rk_setreg(&pll->con3, 1 << PLL_RESET_SHIFT);
+
+ rk_clrsetreg(&pll->con0,
+ CLKR_MASK << CLKR_SHIFT | PLL_OD_MASK,
+ ((div->nr - 1) << CLKR_SHIFT) | (div->no - 1));
+ rk_clrsetreg(&pll->con1, CLKF_MASK, div->nf - 1);
+ rk_clrsetreg(&pll->con2, PLL_BWADJ_MASK, (div->nf >> 1) - 1);
+
+ udelay(10);
+
+ /* return form rest */
+ rk_clrreg(&pll->con3, 1 << PLL_RESET_SHIFT);
+
+ return 0;
+}
+
+static inline unsigned int log2(unsigned int value)
+{
+ return fls(value) - 1;
+}
+
+static int rkclk_configure_ddr(struct rk3288_cru *cru, struct rk3288_grf *grf,
+ unsigned int hz)
+{
+ static const struct pll_div dpll_cfg[] = {
+ {.nf = 25, .nr = 2, .no = 1},
+ {.nf = 400, .nr = 9, .no = 2},
+ {.nf = 500, .nr = 9, .no = 2},
+ {.nf = 100, .nr = 3, .no = 1},
+ };
+ int cfg;
+
+ debug("%s: cru=%p, grf=%p, hz=%u\n", __func__, cru, grf, hz);
+ switch (hz) {
+ case 300000000:
+ cfg = 0;
+ break;
+ case 533000000: /* actually 533.3P MHz */
+ cfg = 1;
+ break;
+ case 666000000: /* actually 666.6P MHz */
+ cfg = 2;
+ break;
+ case 800000000:
+ cfg = 3;
+ break;
+ default:
+ debug("Unsupported SDRAM frequency, add to clock.c!");
+ return -EINVAL;
+ }
+
+ /* pll enter slow-mode */
+ rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
+ DPLL_MODE_SLOW << DPLL_MODE_SHIFT);
+
+ rkclk_set_pll(cru, CLK_DDR, &dpll_cfg[cfg]);
+
+ /* wait for pll lock */
+ while (!(readl(&grf->soc_status[1]) & SOCSTS_DPLL_LOCK))
+ udelay(1);
+
+ /* PLL enter normal-mode */
+ rk_clrsetreg(&cru->cru_mode_con, DPLL_MODE_MASK << DPLL_MODE_SHIFT,
+ DPLL_MODE_NORM << DPLL_MODE_SHIFT);
+
+ return 0;
+}
+
+#ifdef CONFIG_SPL_BUILD
+static void rkclk_init(struct rk3288_cru *cru, struct rk3288_grf *grf)
+{
+ u32 aclk_div;
+ u32 hclk_div;
+ u32 pclk_div;
+
+ /* pll enter slow-mode */
+ rk_clrsetreg(&cru->cru_mode_con,
+ GPLL_MODE_MASK << GPLL_MODE_SHIFT |
+ CPLL_MODE_MASK << CPLL_MODE_SHIFT,
+ GPLL_MODE_SLOW << GPLL_MODE_SHIFT |
+ CPLL_MODE_SLOW << CPLL_MODE_SHIFT);
+
+ /* init pll */
+ rkclk_set_pll(cru, CLK_GENERAL, &gpll_init_cfg);
+ rkclk_set_pll(cru, CLK_CODEC, &cpll_init_cfg);
+
+ /* waiting for pll lock */
+ while ((readl(&grf->soc_status[1]) &
+ (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK)) !=
+ (SOCSTS_CPLL_LOCK | SOCSTS_GPLL_LOCK))
+ udelay(1);
+
+ /*
+ * pd_bus clock pll source selection and
+ * set up dependent divisors for PCLK/HCLK and ACLK clocks.
+ */
+ aclk_div = GPLL_HZ / PD_BUS_ACLK_HZ - 1;
+ assert((aclk_div + 1) * PD_BUS_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
+ hclk_div = PD_BUS_ACLK_HZ / PD_BUS_HCLK_HZ - 1;
+ assert((hclk_div + 1) * PD_BUS_HCLK_HZ ==
+ PD_BUS_ACLK_HZ && (hclk_div < 0x4) && (hclk_div != 0x2));
+
+ pclk_div = PD_BUS_ACLK_HZ / PD_BUS_PCLK_HZ - 1;
+ assert((pclk_div + 1) * PD_BUS_PCLK_HZ ==
+ PD_BUS_ACLK_HZ && pclk_div < 0x7);
+
+ rk_clrsetreg(&cru->cru_clksel_con[1],
+ PD_BUS_PCLK_DIV_MASK << PD_BUS_PCLK_DIV_SHIFT |
+ PD_BUS_HCLK_DIV_MASK << PD_BUS_HCLK_DIV_SHIFT |
+ PD_BUS_ACLK_DIV0_MASK << PD_BUS_ACLK_DIV0_SHIFT |
+ PD_BUS_ACLK_DIV1_MASK << PD_BUS_ACLK_DIV1_SHIFT,
+ pclk_div << PD_BUS_PCLK_DIV_SHIFT |
+ hclk_div << PD_BUS_HCLK_DIV_SHIFT |
+ aclk_div << PD_BUS_ACLK_DIV0_SHIFT |
+ 0 << 0);
+
+ /*
+ * peri clock pll source selection and
+ * set up dependent divisors for PCLK/HCLK and ACLK clocks.
+ */
+ aclk_div = GPLL_HZ / PERI_ACLK_HZ - 1;
+ assert((aclk_div + 1) * PERI_ACLK_HZ == GPLL_HZ && aclk_div < 0x1f);
+
+ hclk_div = log2(PERI_ACLK_HZ / PERI_HCLK_HZ);
+ assert((1 << hclk_div) * PERI_HCLK_HZ ==
+ PERI_ACLK_HZ && (hclk_div < 0x4));
+
+ pclk_div = log2(PERI_ACLK_HZ / PERI_PCLK_HZ);
+ assert((1 << pclk_div) * PERI_PCLK_HZ ==
+ PERI_ACLK_HZ && (pclk_div < 0x4));
+
+ rk_clrsetreg(&cru->cru_clksel_con[10],
+ PERI_PCLK_DIV_MASK << PERI_PCLK_DIV_SHIFT |
+ PERI_HCLK_DIV_MASK << PERI_HCLK_DIV_SHIFT |
+ PERI_ACLK_DIV_MASK << PERI_ACLK_DIV_SHIFT,
+ pclk_div << PERI_PCLK_DIV_SHIFT |
+ hclk_div << PERI_HCLK_DIV_SHIFT |
+ aclk_div << PERI_ACLK_DIV_SHIFT);
+
+ /* PLL enter normal-mode */
+ rk_clrsetreg(&cru->cru_mode_con,
+ GPLL_MODE_MASK << GPLL_MODE_SHIFT |
+ CPLL_MODE_MASK << CPLL_MODE_SHIFT,
+ GPLL_MODE_NORM << GPLL_MODE_SHIFT |
+ GPLL_MODE_NORM << CPLL_MODE_SHIFT);
+}
+#endif
+
+/* Get pll rate by id */
+static uint32_t rkclk_pll_get_rate(struct rk3288_cru *cru,
+ enum rk_clk_id clk_id)
+{
+ uint32_t nr, no, nf;
+ uint32_t con;
+ int pll_id = rk_pll_id(clk_id);
+ struct rk3288_pll *pll = &cru->pll[pll_id];
+ static u8 clk_shift[CLK_COUNT] = {
+ 0xff, APLL_WORK_SHIFT, DPLL_WORK_SHIFT, CPLL_WORK_SHIFT,
+ GPLL_WORK_SHIFT, NPLL_WORK_SHIFT
+ };
+ uint shift;
+
+ con = readl(&cru->cru_mode_con);
+ shift = clk_shift[clk_id];
+ switch ((con >> shift) & APLL_WORK_MASK) {
+ case APLL_WORK_SLOW:
+ return OSC_HZ;
+ case APLL_WORK_NORMAL:
+ /* normal mode */
+ con = readl(&pll->con0);
+ no = ((con >> CLKOD_SHIFT) & CLKOD_MASK) + 1;
+ nr = ((con >> CLKR_SHIFT) & CLKR_MASK) + 1;
+ con = readl(&pll->con1);
+ nf = ((con >> CLKF_SHIFT) & CLKF_MASK) + 1;
+
+ return (24 * nf / (nr * no)) * 1000000;
+ case APLL_WORK_DEEP:
+ default:
+ return 32768;
+ }
+}
+
+static ulong rk3288_clk_get_rate(struct udevice *dev)
+{
+ struct rk3288_clk_plat *plat = dev_get_platdata(dev);
+ struct rk3288_clk_priv *priv = dev_get_priv(dev);
+
+ debug("%s\n", dev->name);
+ return rkclk_pll_get_rate(priv->cru, plat->clk_id);
+}
+
+static ulong rk3288_clk_set_rate(struct udevice *dev, ulong rate)
+{
+ struct rk3288_clk_plat *plat = dev_get_platdata(dev);
+ struct rk3288_clk_priv *priv = dev_get_priv(dev);
+
+ debug("%s\n", dev->name);
+ switch (plat->clk_id) {
+ case CLK_DDR:
+ rkclk_configure_ddr(priv->cru, priv->grf, rate);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return 0;
+}
+
+static ulong rockchip_mmc_get_clk(struct rk3288_cru *cru, uint clk_general_rate,
+ enum periph_id periph)
+{
+ uint src_rate;
+ uint div, mux;
+ u32 con;
+
+ switch (periph) {
+ case PERIPH_ID_EMMC:
+ con = readl(&cru->cru_clksel_con[12]);
+ mux = (con >> EMMC_PLL_SHIFT) & EMMC_PLL_MASK;
+ div = (con >> EMMC_DIV_SHIFT) & EMMC_DIV_MASK;
+ break;
+ case PERIPH_ID_SDCARD:
+ con = readl(&cru->cru_clksel_con[12]);
+ mux = (con >> MMC0_PLL_SHIFT) & MMC0_PLL_MASK;
+ div = (con >> MMC0_DIV_SHIFT) & MMC0_DIV_MASK;
+ break;
+ case PERIPH_ID_SDMMC2:
+ con = readl(&cru->cru_clksel_con[12]);
+ mux = (con >> SDIO0_PLL_SHIFT) & SDIO0_PLL_MASK;
+ div = (con >> SDIO0_DIV_SHIFT) & SDIO0_DIV_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ src_rate = mux == EMMC_PLL_SELECT_24MHZ ? OSC_HZ : clk_general_rate;
+ return DIV_TO_RATE(src_rate, div);
+}
+
+static ulong rockchip_mmc_set_clk(struct rk3288_cru *cru, uint clk_general_rate,
+ enum periph_id periph, uint freq)
+{
+ int src_clk_div;
+ int mux;
+
+ debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate);
+ src_clk_div = RATE_TO_DIV(clk_general_rate, freq);
+
+ if (src_clk_div > 0x3f) {
+ src_clk_div = RATE_TO_DIV(OSC_HZ, freq);
+ mux = EMMC_PLL_SELECT_24MHZ;
+ assert((int)EMMC_PLL_SELECT_24MHZ ==
+ (int)MMC0_PLL_SELECT_24MHZ);
+ } else {
+ mux = EMMC_PLL_SELECT_GENERAL;
+ assert((int)EMMC_PLL_SELECT_GENERAL ==
+ (int)MMC0_PLL_SELECT_GENERAL);
+ }
+ switch (periph) {
+ case PERIPH_ID_EMMC:
+ rk_clrsetreg(&cru->cru_clksel_con[12],
+ EMMC_PLL_MASK << EMMC_PLL_SHIFT |
+ EMMC_DIV_MASK << EMMC_DIV_SHIFT,
+ mux << EMMC_PLL_SHIFT |
+ (src_clk_div - 1) << EMMC_DIV_SHIFT);
+ break;
+ case PERIPH_ID_SDCARD:
+ rk_clrsetreg(&cru->cru_clksel_con[11],
+ MMC0_PLL_MASK << MMC0_PLL_SHIFT |
+ MMC0_DIV_MASK << MMC0_DIV_SHIFT,
+ mux << MMC0_PLL_SHIFT |
+ (src_clk_div - 1) << MMC0_DIV_SHIFT);
+ break;
+ case PERIPH_ID_SDMMC2:
+ rk_clrsetreg(&cru->cru_clksel_con[12],
+ SDIO0_PLL_MASK << SDIO0_PLL_SHIFT |
+ SDIO0_DIV_MASK << SDIO0_DIV_SHIFT,
+ mux << SDIO0_PLL_SHIFT |
+ (src_clk_div - 1) << SDIO0_DIV_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rockchip_mmc_get_clk(cru, clk_general_rate, periph);
+}
+
+static ulong rockchip_spi_get_clk(struct rk3288_cru *cru, uint clk_general_rate,
+ enum periph_id periph)
+{
+ uint div, mux;
+ u32 con;
+
+ switch (periph) {
+ case PERIPH_ID_SPI0:
+ con = readl(&cru->cru_clksel_con[25]);
+ mux = (con >> SPI0_PLL_SHIFT) & SPI0_PLL_MASK;
+ div = (con >> SPI0_DIV_SHIFT) & SPI0_DIV_MASK;
+ break;
+ case PERIPH_ID_SPI1:
+ con = readl(&cru->cru_clksel_con[25]);
+ mux = (con >> SPI1_PLL_SHIFT) & SPI1_PLL_MASK;
+ div = (con >> SPI1_DIV_SHIFT) & SPI1_DIV_MASK;
+ break;
+ case PERIPH_ID_SPI2:
+ con = readl(&cru->cru_clksel_con[39]);
+ mux = (con >> SPI2_PLL_SHIFT) & SPI2_PLL_MASK;
+ div = (con >> SPI2_DIV_SHIFT) & SPI2_DIV_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+ assert(mux == SPI0_PLL_SELECT_GENERAL);
+
+ return DIV_TO_RATE(clk_general_rate, div);
+}
+
+static ulong rockchip_spi_set_clk(struct rk3288_cru *cru, uint clk_general_rate,
+ enum periph_id periph, uint freq)
+{
+ int src_clk_div;
+
+ debug("%s: clk_general_rate=%u\n", __func__, clk_general_rate);
+ src_clk_div = RATE_TO_DIV(clk_general_rate, freq);
+ switch (periph) {
+ case PERIPH_ID_SPI0:
+ rk_clrsetreg(&cru->cru_clksel_con[25],
+ SPI0_PLL_MASK << SPI0_PLL_SHIFT |
+ SPI0_DIV_MASK << SPI0_DIV_SHIFT,
+ SPI0_PLL_SELECT_GENERAL << SPI0_PLL_SHIFT |
+ src_clk_div << SPI0_DIV_SHIFT);
+ break;
+ case PERIPH_ID_SPI1:
+ rk_clrsetreg(&cru->cru_clksel_con[25],
+ SPI1_PLL_MASK << SPI1_PLL_SHIFT |
+ SPI1_DIV_MASK << SPI1_DIV_SHIFT,
+ SPI1_PLL_SELECT_GENERAL << SPI1_PLL_SHIFT |
+ src_clk_div << SPI1_DIV_SHIFT);
+ break;
+ case PERIPH_ID_SPI2:
+ rk_clrsetreg(&cru->cru_clksel_con[39],
+ SPI2_PLL_MASK << SPI2_PLL_SHIFT |
+ SPI2_DIV_MASK << SPI2_DIV_SHIFT,
+ SPI2_PLL_SELECT_GENERAL << SPI2_PLL_SHIFT |
+ src_clk_div << SPI2_DIV_SHIFT);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return rockchip_spi_get_clk(cru, clk_general_rate, periph);
+}
+
+ulong rk3288_set_periph_rate(struct udevice *dev, int periph, ulong rate)
+{
+ struct rk3288_clk_priv *priv = dev_get_priv(dev);
+ ulong new_rate;
+
+ switch (periph) {
+ case PERIPH_ID_EMMC:
+ case PERIPH_ID_SDCARD:
+ new_rate = rockchip_mmc_set_clk(priv->cru, clk_get_rate(dev),
+ periph, rate);
+ break;
+ case PERIPH_ID_SPI0:
+ case PERIPH_ID_SPI1:
+ case PERIPH_ID_SPI2:
+ new_rate = rockchip_spi_set_clk(priv->cru, clk_get_rate(dev),
+ periph, rate);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ return new_rate;
+}
+
+static struct clk_ops rk3288_clk_ops = {
+ .get_rate = rk3288_clk_get_rate,
+ .set_rate = rk3288_clk_set_rate,
+ .set_periph_rate = rk3288_set_periph_rate,
+};
+
+static int rk3288_clk_probe(struct udevice *dev)
+{
+ struct rk3288_clk_plat *plat = dev_get_platdata(dev);
+ struct rk3288_clk_priv *priv = dev_get_priv(dev);
+
+ if (plat->clk_id != CLK_OSC) {
+ struct rk3288_clk_priv *parent_priv = dev_get_priv(dev->parent);
+
+ priv->cru = parent_priv->cru;
+ priv->grf = parent_priv->grf;
+ return 0;
+ }
+ priv->cru = (struct rk3288_cru *)dev_get_addr(dev);
+ priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+#ifdef CONFIG_SPL_BUILD
+ rkclk_init(priv->cru, priv->grf);
+#endif
+
+ return 0;
+}
+
+static const char *const clk_name[CLK_COUNT] = {
+ "osc",
+ "apll",
+ "dpll",
+ "cpll",
+ "gpll",
+ "mpll",
+};
+
+static int rk3288_clk_bind(struct udevice *dev)
+{
+ struct rk3288_clk_plat *plat = dev_get_platdata(dev);
+ int pll, ret;
+
+ /* We only need to set up the root clock */
+ if (dev->of_offset == -1) {
+ plat->clk_id = CLK_OSC;
+ return 0;
+ }
+
+ /* Create devices for P main clocks */
+ for (pll = 1; pll < CLK_COUNT; pll++) {
+ struct udevice *child;
+ struct rk3288_clk_plat *cplat;
+
+ debug("%s %s\n", __func__, clk_name[pll]);
+ ret = device_bind_driver(dev, "clk_rk3288", clk_name[pll],
+ &child);
+ if (ret)
+ return ret;
+ cplat = dev_get_platdata(child);
+ cplat->clk_id = pll;
+ }
+
+ /* The reset driver does not have a device node, so bind it here */
+ ret = device_bind_driver(gd->dm_root, "rk3288_reset", "reset", &dev);
+ if (ret)
+ debug("Warning: No RK3288 reset driver: ret=%d\n", ret);
+
+ return 0;
+}
+
+static const struct udevice_id rk3288_clk_ids[] = {
+ { .compatible = "rockchip,rk3288-cru" },
+ { }
+};
+
+U_BOOT_DRIVER(clk_rk3288) = {
+ .name = "clk_rk3288",
+ .id = UCLASS_CLK,
+ .of_match = rk3288_clk_ids,
+ .priv_auto_alloc_size = sizeof(struct rk3288_clk_priv),
+ .platdata_auto_alloc_size = sizeof(struct rk3288_clk_plat),
+ .ops = &rk3288_clk_ops,
+ .bind = rk3288_clk_bind,
+ .probe = rk3288_clk_probe,
+};
diff --git a/drivers/core/device.c b/drivers/core/device.c
index a6cd93698f..0ccd443f25 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -39,8 +39,10 @@ int device_bind(struct udevice *parent, const struct driver *drv,
return -EINVAL;
ret = uclass_get(drv->id, &uc);
- if (ret)
+ if (ret) {
+ debug("Missing uclass for driver %s\n", drv->name);
return ret;
+ }
dev = calloc(1, sizeof(struct udevice));
if (!dev)
diff --git a/drivers/core/root.c b/drivers/core/root.c
index 78ab00c7bf..bdb394a9ae 100644
--- a/drivers/core/root.c
+++ b/drivers/core/root.c
@@ -162,8 +162,11 @@ int dm_scan_fdt_node(struct udevice *parent, const void *blob, int offset,
continue;
}
err = lists_bind_fdt(parent, blob, offset, NULL);
- if (err && !ret)
+ if (err && !ret) {
ret = err;
+ debug("%s: ret=%d\n", fdt_get_name(blob, offset, NULL),
+ ret);
+ }
}
if (ret)
diff --git a/drivers/core/uclass.c b/drivers/core/uclass.c
index f63ff599a6..e800c28653 100644
--- a/drivers/core/uclass.c
+++ b/drivers/core/uclass.c
@@ -58,7 +58,12 @@ static int uclass_add(enum uclass_id id, struct uclass **ucp)
if (!uc_drv) {
debug("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n",
id);
- return -ENOENT;
+ /*
+ * Use a strange error to make this case easier to find. When
+ * a uclass is not available it can prevent driver model from
+ * starting up and this failure is otherwise hard to debug.
+ */
+ return -EPFNOSUPPORT;
}
uc = calloc(1, sizeof(*uc));
if (!uc)
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 9a5d29debb..ef57a89ea2 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -28,6 +28,15 @@ config LPC32XX_GPIO
help
Support for the LPC32XX GPIO driver.
+config ROCKCHIP_GPIO
+ bool "Rockchip GPIO driver"
+ depends on DM_GPIO
+ help
+ Support GPIO access on Rockchip SoCs. The GPIOs are arranged into
+ a number of banks (different for each SoC type) each with 32 GPIOs.
+ The GPIOs for a device are defined in the device tree with one node
+ for each bank.
+
config SANDBOX_GPIO
bool "Enable sandbox GPIO driver"
depends on SANDBOX && DM && DM_GPIO
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index ba185949bf..c58aa4dfd5 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_MXC_GPIO) += mxc_gpio.o
obj-$(CONFIG_MXS_GPIO) += mxs_gpio.o
obj-$(CONFIG_PCA953X) += pca953x.o
obj-$(CONFIG_PCA9698) += pca9698.o
+obj-$(CONFIG_ROCKCHIP_GPIO) += rk_gpio.o
obj-$(CONFIG_S5P) += s5p_gpio.o
obj-$(CONFIG_SANDBOX_GPIO) += sandbox.o
obj-$(CONFIG_SPEAR_GPIO) += spear_gpio.o
diff --git a/drivers/gpio/rk_gpio.c b/drivers/gpio/rk_gpio.c
new file mode 100644
index 0000000000..fbdf9f3fd9
--- /dev/null
+++ b/drivers/gpio/rk_gpio.c
@@ -0,0 +1,123 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * (C) Copyright 2008-2014 Rockchip Electronics
+ * Peter, Software Engineering, <superpeter.cai@gmail.com>.
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <asm/errno.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dt-bindings/gpio/gpio.h>
+
+enum {
+ ROCKCHIP_GPIOS_PER_BANK = 32,
+};
+
+#define OFFSET_TO_BIT(bit) (1UL << (bit))
+
+struct rockchip_gpio_priv {
+ struct rockchip_gpio_regs *regs;
+ char name[2];
+};
+
+static int rockchip_gpio_direction_input(struct udevice *dev, unsigned offset)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ struct rockchip_gpio_regs *regs = priv->regs;
+
+ clrbits_le32(&regs->swport_ddr, OFFSET_TO_BIT(offset));
+
+ return 0;
+}
+
+static int rockchip_gpio_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ struct rockchip_gpio_regs *regs = priv->regs;
+ int mask = OFFSET_TO_BIT(offset);
+
+ clrsetbits_le32(&regs->swport_dr, mask, value ? mask : 0);
+ setbits_le32(&regs->swport_ddr, mask);
+
+ return 0;
+}
+
+static int rockchip_gpio_get_value(struct udevice *dev, unsigned offset)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ struct rockchip_gpio_regs *regs = priv->regs;
+
+ return readl(&regs->ext_port) & OFFSET_TO_BIT(offset);
+}
+
+static int rockchip_gpio_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ struct rockchip_gpio_regs *regs = priv->regs;
+ int mask = OFFSET_TO_BIT(offset);
+
+ clrsetbits_le32(&regs->swport_dr, mask, value ? mask : 0);
+
+ return 0;
+}
+
+static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset)
+{
+ return -ENOSYS;
+}
+
+static int rockchip_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct fdtdec_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static int rockchip_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct rockchip_gpio_priv *priv = dev_get_priv(dev);
+ char *end;
+ int bank;
+
+ priv->regs = (struct rockchip_gpio_regs *)dev_get_addr(dev);
+ uc_priv->gpio_count = ROCKCHIP_GPIOS_PER_BANK;
+ end = strrchr(dev->name, '@');
+ bank = trailing_strtoln(dev->name, end);
+ priv->name[0] = 'A' + bank;
+ uc_priv->bank_name = priv->name;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gpio_rockchip_ops = {
+ .direction_input = rockchip_gpio_direction_input,
+ .direction_output = rockchip_gpio_direction_output,
+ .get_value = rockchip_gpio_get_value,
+ .set_value = rockchip_gpio_set_value,
+ .get_function = rockchip_gpio_get_function,
+ .xlate = rockchip_gpio_xlate,
+};
+
+static const struct udevice_id rockchip_gpio_ids[] = {
+ { .compatible = "rockchip,gpio-bank" },
+ { }
+};
+
+U_BOOT_DRIVER(gpio_rockchip) = {
+ .name = "gpio_rockchip",
+ .id = UCLASS_GPIO,
+ .of_match = rockchip_gpio_ids,
+ .ops = &gpio_rockchip_ops,
+ .priv_auto_alloc_size = sizeof(struct rockchip_gpio_priv),
+ .probe = rockchip_gpio_probe,
+};
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index c40bd5c1e0..14adda2857 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -58,6 +58,15 @@ config DM_I2C_GPIO
bindings are supported.
Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
+config SYS_I2C_ROCKCHIP
+ bool "Rockchip I2C driver"
+ depends on DM_I2C
+ help
+ Add support for the Rockchip I2C driver. This is used with various
+ Rockchip parts such as RK3126, RK3128, RK3036 and RK3288. All chips
+ have several I2C ports and all are provided, controled by the
+ device tree.
+
config SYS_I2C_SANDBOX
bool "Sandbox I2C driver"
depends on SANDBOX && DM_I2C
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index 9b45248e2e..8ce294b02a 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
obj-$(CONFIG_SYS_I2C_OMAP34XX) += omap24xx_i2c.o
obj-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o
obj-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o
+obj-$(CONFIG_SYS_I2C_ROCKCHIP) += rk_i2c.o
obj-$(CONFIG_SYS_I2C_S3C24X0) += s3c24x0_i2c.o
obj-$(CONFIG_SYS_I2C_SANDBOX) += sandbox_i2c.o i2c-emul-uclass.o
obj-$(CONFIG_SYS_I2C_SH) += sh_i2c.o
diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c
new file mode 100644
index 0000000000..ebdba35dc6
--- /dev/null
+++ b/drivers/i2c/rk_i2c.c
@@ -0,0 +1,391 @@
+/*
+ * (C) Copyright 2015 Google, Inc
+ *
+ * (C) Copyright 2008-2014 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 <i2c.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/i2c.h>
+#include <asm/arch/periph.h>
+#include <dm/pinctrl.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* i2c timerout */
+#define I2C_TIMEOUT_MS 100
+#define I2C_RETRY_COUNT 3
+
+/* rk i2c fifo max transfer bytes */
+#define RK_I2C_FIFO_SIZE 32
+
+struct rk_i2c {
+ struct udevice *clk;
+ struct udevice *pinctrl;
+ struct i2c_regs *regs;
+ unsigned int speed;
+ enum periph_id id;
+};
+
+static inline void rk_i2c_get_div(int div, int *divh, int *divl)
+{
+ *divl = div / 2;
+ if (div % 2 == 0)
+ *divh = div / 2;
+ else
+ *divh = DIV_ROUND_UP(div, 2);
+}
+
+/*
+ * SCL Divisor = 8 * (CLKDIVL+1 + CLKDIVH+1)
+ * SCL = PCLK / SCLK Divisor
+ * i2c_rate = PCLK
+ */
+static void rk_i2c_set_clk(struct rk_i2c *i2c, uint32_t scl_rate)
+{
+ uint32_t i2c_rate;
+ int div, divl, divh;
+
+ /* First get i2c rate from pclk */
+ i2c_rate = clk_get_periph_rate(i2c->clk, i2c->id);
+
+ div = DIV_ROUND_UP(i2c_rate, scl_rate * 8) - 2;
+ divh = 0;
+ divl = 0;
+ if (div >= 0)
+ rk_i2c_get_div(div, &divh, &divl);
+ writel(I2C_CLKDIV_VAL(divl, divh), &i2c->regs->clkdiv);
+
+ debug("rk_i2c_set_clk: i2c rate = %d, scl rate = %d\n", i2c_rate,
+ scl_rate);
+ debug("set i2c clk div = %d, divh = %d, divl = %d\n", div, divh, divl);
+ debug("set clk(I2C_CLKDIV: 0x%08x)\n", readl(&i2c->regs->clkdiv));
+}
+
+static void rk_i2c_show_regs(struct i2c_regs *regs)
+{
+#ifdef DEBUG
+ uint i;
+
+ debug("i2c_con: 0x%08x\n", readl(&regs->con));
+ debug("i2c_clkdiv: 0x%08x\n", readl(&regs->clkdiv));
+ debug("i2c_mrxaddr: 0x%08x\n", readl(&regs->mrxaddr));
+ debug("i2c_mrxraddR: 0x%08x\n", readl(&regs->mrxraddr));
+ debug("i2c_mtxcnt: 0x%08x\n", readl(&regs->mtxcnt));
+ debug("i2c_mrxcnt: 0x%08x\n", readl(&regs->mrxcnt));
+ debug("i2c_ien: 0x%08x\n", readl(&regs->ien));
+ debug("i2c_ipd: 0x%08x\n", readl(&regs->ipd));
+ debug("i2c_fcnt: 0x%08x\n", readl(&regs->fcnt));
+ for (i = 0; i < 8; i++)
+ debug("i2c_txdata%d: 0x%08x\n", i, readl(&regs->txdata[i]));
+ for (i = 0; i < 8; i++)
+ debug("i2c_rxdata%d: 0x%08x\n", i, readl(&regs->rxdata[i]));
+#endif
+}
+
+static int rk_i2c_send_start_bit(struct rk_i2c *i2c)
+{
+ struct i2c_regs *regs = i2c->regs;
+ ulong start;
+
+ debug("I2c Send Start bit.\n");
+ writel(I2C_IPD_ALL_CLEAN, &regs->ipd);
+
+ writel(I2C_CON_EN | I2C_CON_START, &regs->con);
+ writel(I2C_STARTIEN, &regs->ien);
+
+ start = get_timer(0);
+ while (1) {
+ if (readl(&regs->ipd) & I2C_STARTIPD) {
+ writel(I2C_STARTIPD, &regs->ipd);
+ break;
+ }
+ if (get_timer(start) > I2C_TIMEOUT_MS) {
+ debug("I2C Send Start Bit Timeout\n");
+ rk_i2c_show_regs(regs);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ return 0;
+}
+
+static int rk_i2c_send_stop_bit(struct rk_i2c *i2c)
+{
+ struct i2c_regs *regs = i2c->regs;
+ ulong start;
+
+ debug("I2c Send Stop bit.\n");
+ writel(I2C_IPD_ALL_CLEAN, &regs->ipd);
+
+ writel(I2C_CON_EN | I2C_CON_STOP, &regs->con);
+ writel(I2C_CON_STOP, &regs->ien);
+
+ start = get_timer(0);
+ while (1) {
+ if (readl(&regs->ipd) & I2C_STOPIPD) {
+ writel(I2C_STOPIPD, &regs->ipd);
+ break;
+ }
+ if (get_timer(start) > I2C_TIMEOUT_MS) {
+ debug("I2C Send Start Bit Timeout\n");
+ rk_i2c_show_regs(regs);
+ return -ETIMEDOUT;
+ }
+ udelay(1);
+ }
+
+ return 0;
+}
+
+static inline void rk_i2c_disable(struct rk_i2c *i2c)
+{
+ writel(0, &i2c->regs->con);
+}
+
+static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
+ uchar *buf, uint b_len)
+{
+ struct i2c_regs *regs = i2c->regs;
+ uchar *pbuf = buf;
+ uint bytes_remain_len = b_len;
+ uint bytes_xferred = 0;
+ uint words_xferred = 0;
+ ulong start;
+ uint con = 0;
+ uint rxdata;
+ uint i, j;
+ int err;
+
+ debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
+ chip, reg, r_len, b_len);
+
+ err = rk_i2c_send_start_bit(i2c);
+ if (err)
+ return err;
+
+ writel(I2C_MRXADDR_SET(1, chip << 1 | 1), &regs->mrxaddr);
+ if (r_len == 0) {
+ writel(0, &regs->mrxraddr);
+ } else if (r_len < 4) {
+ writel(I2C_MRXRADDR_SET(r_len, reg), &regs->mrxraddr);
+ } else {
+ debug("I2C Read: addr len %d not supported\n", r_len);
+ return -EIO;
+ }
+
+ while (bytes_remain_len) {
+ if (bytes_remain_len > RK_I2C_FIFO_SIZE) {
+ con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX);
+ bytes_xferred = 32;
+ } else {
+ con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) |
+ I2C_CON_LASTACK;
+ bytes_xferred = bytes_remain_len;
+ }
+ words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
+
+ writel(con, &regs->con);
+ writel(bytes_xferred, &regs->mrxcnt);
+ writel(I2C_MBRFIEN | I2C_NAKRCVIEN, &regs->ien);
+
+ start = get_timer(0);
+ while (1) {
+ if (readl(&regs->ipd) & I2C_NAKRCVIPD) {
+ writel(I2C_NAKRCVIPD, &regs->ipd);
+ err = -EREMOTEIO;
+ }
+ if (readl(&regs->ipd) & I2C_MBRFIPD) {
+ writel(I2C_MBRFIPD, &regs->ipd);
+ break;
+ }
+ if (get_timer(start) > I2C_TIMEOUT_MS) {
+ debug("I2C Read Data Timeout\n");
+ err = -ETIMEDOUT;
+ rk_i2c_show_regs(regs);
+ goto i2c_exit;
+ }
+ udelay(1);
+ }
+
+ for (i = 0; i < words_xferred; i++) {
+ rxdata = readl(&regs->rxdata[i]);
+ debug("I2c Read RXDATA[%d] = 0x%x\n", i, rxdata);
+ for (j = 0; j < 4; j++) {
+ if ((i * 4 + j) == bytes_xferred)
+ break;
+ *pbuf++ = (rxdata >> (j * 8)) & 0xff;
+ }
+ }
+
+ bytes_remain_len -= bytes_xferred;
+ debug("I2C Read bytes_remain_len %d\n", bytes_remain_len);
+ }
+
+i2c_exit:
+ rk_i2c_send_stop_bit(i2c);
+ rk_i2c_disable(i2c);
+
+ return err;
+}
+
+static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
+ uchar *buf, uint b_len)
+{
+ struct i2c_regs *regs = i2c->regs;
+ int err;
+ uchar *pbuf = buf;
+ uint bytes_remain_len = b_len + r_len + 1;
+ uint bytes_xferred = 0;
+ uint words_xferred = 0;
+ ulong start;
+ uint txdata;
+ uint i, j;
+
+ debug("rk_i2c_write: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
+ chip, reg, r_len, b_len);
+ err = rk_i2c_send_start_bit(i2c);
+ if (err)
+ return err;
+
+ while (bytes_remain_len) {
+ if (bytes_remain_len > RK_I2C_FIFO_SIZE)
+ bytes_xferred = 32;
+ else
+ bytes_xferred = bytes_remain_len;
+ words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
+
+ for (i = 0; i < words_xferred; i++) {
+ txdata = 0;
+ for (j = 0; j < 4; j++) {
+ if ((i * 4 + j) == bytes_xferred)
+ break;
+
+ if (i == 0 && j == 0) {
+ txdata |= (chip << 1);
+ } else if (i == 0 && j <= r_len) {
+ txdata |= (reg &
+ (0xff << ((j - 1) * 8))) << 8;
+ } else {
+ txdata |= (*pbuf++)<<(j * 8);
+ }
+ writel(txdata, &regs->txdata[i]);
+ }
+ debug("I2c Write TXDATA[%d] = 0x%x\n", i, txdata);
+ }
+
+ writel(I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), &regs->con);
+ writel(bytes_xferred, &regs->mtxcnt);
+ writel(I2C_MBTFIEN | I2C_NAKRCVIEN, &regs->ien);
+
+ start = get_timer(0);
+ while (1) {
+ if (readl(&regs->ipd) & I2C_NAKRCVIPD) {
+ writel(I2C_NAKRCVIPD, &regs->ipd);
+ err = -EREMOTEIO;
+ }
+ if (readl(&regs->ipd) & I2C_MBTFIPD) {
+ writel(I2C_MBTFIPD, &regs->ipd);
+ break;
+ }
+ if (get_timer(start) > I2C_TIMEOUT_MS) {
+ debug("I2C Write Data Timeout\n");
+ err = -ETIMEDOUT;
+ rk_i2c_show_regs(regs);
+ goto i2c_exit;
+ }
+ udelay(1);
+ }
+
+ bytes_remain_len -= bytes_xferred;
+ debug("I2C Write bytes_remain_len %d\n", bytes_remain_len);
+ }
+
+i2c_exit:
+ rk_i2c_send_stop_bit(i2c);
+ rk_i2c_disable(i2c);
+
+ return err;
+}
+
+static int rockchip_i2c_xfer(struct udevice *bus, struct i2c_msg *msg,
+ int nmsgs)
+{
+ struct rk_i2c *i2c = dev_get_priv(bus);
+ int ret;
+
+ debug("i2c_xfer: %d messages\n", nmsgs);
+ for (; nmsgs > 0; nmsgs--, msg++) {
+ debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
+ if (msg->flags & I2C_M_RD) {
+ ret = rk_i2c_read(i2c, msg->addr, 0, 0, msg->buf,
+ msg->len);
+ } else {
+ ret = rk_i2c_write(i2c, msg->addr, 0, 0, msg->buf,
+ msg->len);
+ }
+ if (ret) {
+ debug("i2c_write: error sending\n");
+ return -EREMOTEIO;
+ }
+ }
+
+ return 0;
+}
+
+int rockchip_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+ struct rk_i2c *i2c = dev_get_priv(bus);
+
+ rk_i2c_set_clk(i2c, speed);
+
+ return 0;
+}
+
+static int rockchip_i2c_probe(struct udevice *bus)
+{
+ struct rk_i2c *i2c = dev_get_priv(bus);
+ int ret;
+
+ ret = uclass_get_device(UCLASS_PINCTRL, 0, &i2c->pinctrl);
+ if (ret)
+ return ret;
+ ret = uclass_get_device(UCLASS_CLK, 0, &i2c->clk);
+ if (ret)
+ return ret;
+ ret = pinctrl_get_periph_id(i2c->pinctrl, bus);
+ if (ret < 0)
+ return ret;
+ i2c->id = ret;
+ i2c->regs = (void *)dev_get_addr(bus);
+ return pinctrl_request(i2c->pinctrl, i2c->id, 0);
+}
+
+static const struct dm_i2c_ops rockchip_i2c_ops = {
+ .xfer = rockchip_i2c_xfer,
+ .set_bus_speed = rockchip_i2c_set_bus_speed,
+};
+
+static const struct udevice_id rockchip_i2c_ids[] = {
+ { .compatible = "rockchip,rk3288-i2c" },
+ { }
+};
+
+U_BOOT_DRIVER(i2c_rockchip) = {
+ .name = "i2c_rockchip",
+ .id = UCLASS_I2C,
+ .of_match = rockchip_i2c_ids,
+ .probe = rockchip_i2c_probe,
+ .priv_auto_alloc_size = sizeof(struct rk_i2c),
+ .ops = &rockchip_i2c_ops,
+};
diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig
index 2987337219..fe74403460 100644
--- a/drivers/led/Kconfig
+++ b/drivers/led/Kconfig
@@ -11,7 +11,7 @@ config LED
config SPL_LED
bool "Enable LED support in SPL"
- depends on LED
+ depends on SPL && SPL_DM
help
The LED subsystem adds a small amount of overhead to the image.
If this is acceptable and you have a need to use LEDs in SPL,
@@ -27,4 +27,11 @@ config LED_GPIO
The GPIO driver must used driver model. LEDs are configured using
the device tree.
+config SPL_LED_GPIO
+ bool "LED support for GPIO-connected LEDs in SPL"
+ depends on SPL_LED && DM_GPIO
+ help
+ This option is an SPL-variant of the LED_GPIO option.
+ See the help of LED_GPIO for details.
+
endmenu
diff --git a/drivers/led/Makefile b/drivers/led/Makefile
index 990129e08d..02367fdacb 100644
--- a/drivers/led/Makefile
+++ b/drivers/led/Makefile
@@ -5,5 +5,5 @@
# SPDX-License-Identifier: GPL-2.0+
#
-obj-$(CONFIG_LED) += led-uclass.o
-obj-$(CONFIG_LED_GPIO) += led_gpio.o
+obj-y += led-uclass.o
+obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 3e835f7bca..6277f92ef5 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -10,6 +10,15 @@ config DM_MMC
appear as block devices in U-Boot and can support filesystems such
as EXT4 and FAT.
+config ROCKCHIP_DWMMC
+ bool "Rockchip SD/MMC controller support"
+ depends on DM_MMC && OF_CONTROL
+ help
+ This enables support for the Rockchip SD/MMM controller, which is
+ based on Designware IP. The device is compatible with at least
+ SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well
+ as removeable SD and micro-SD cards.
+
config SH_SDHI
bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support"
depends on RMOBILE
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index cae207c303..99d02954ed 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -29,6 +29,7 @@ obj-$(CONFIG_MXS_MMC) += mxsmmc.o
obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_X86) += pci_mmc.o
obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o
+obj-$(CONFIG_ROCKCHIP_DWMMC) += rockchip_dw_mmc.o
obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c
index 77b87e0365..1117fedefe 100644
--- a/drivers/mmc/dw_mmc.c
+++ b/drivers/mmc/dw_mmc.c
@@ -266,7 +266,7 @@ static int dwmci_setup_bus(struct dwmci_host *host, u32 freq)
* host->bus_hz should be set by user.
*/
if (host->get_mmc_clk)
- sclk = host->get_mmc_clk(host);
+ sclk = host->get_mmc_clk(host, freq);
else if (host->bus_hz)
sclk = host->bus_hz;
else {
diff --git a/drivers/mmc/exynos_dw_mmc.c b/drivers/mmc/exynos_dw_mmc.c
index cde2ba7118..863bbb3f64 100644
--- a/drivers/mmc/exynos_dw_mmc.c
+++ b/drivers/mmc/exynos_dw_mmc.c
@@ -39,7 +39,7 @@ static void exynos_dwmci_clksel(struct dwmci_host *host)
dwmci_writel(host, DWMCI_CLKSEL, priv->sdr_timing);
}
-unsigned int exynos_dwmci_get_clk(struct dwmci_host *host)
+unsigned int exynos_dwmci_get_clk(struct dwmci_host *host, uint freq)
{
unsigned long sclk;
int8_t clk_div;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index f12546ac51..371c1ec212 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -10,6 +10,8 @@
#include <config.h>
#include <common.h>
#include <command.h>
+#include <dm.h>
+#include <dm/device-internal.h>
#include <errno.h>
#include <mmc.h>
#include <part.h>
@@ -1759,10 +1761,44 @@ static void do_preinit(void)
}
}
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_SPL_BUILD)
+static int mmc_probe(bd_t *bis)
+{
+ return 0;
+}
+#elif defined(CONFIG_DM_MMC)
+static int mmc_probe(bd_t *bis)
+{
+ int ret;
+ struct uclass *uc;
+ struct udevice *m;
+
+ ret = uclass_get(UCLASS_MMC, &uc);
+ if (ret)
+ return ret;
+
+ uclass_foreach_dev(m, uc) {
+ ret = device_probe(m);
+ if (ret)
+ printf("%s - probe failed: %d\n", m->name, ret);
+ }
+
+ return 0;
+}
+#else
+static int mmc_probe(bd_t *bis)
+{
+ if (board_mmc_init(bis) < 0)
+ cpu_mmc_init(bis);
+
+ return 0;
+}
+#endif
int mmc_initialize(bd_t *bis)
{
static int initialized = 0;
+ int ret;
if (initialized) /* Avoid initializing mmc multiple times */
return 0;
initialized = 1;
@@ -1770,10 +1806,9 @@ int mmc_initialize(bd_t *bis)
INIT_LIST_HEAD (&mmc_devices);
cur_dev_num = 0;
-#ifndef CONFIG_DM_MMC
- if (board_mmc_init(bis) < 0)
- cpu_mmc_init(bis);
-#endif
+ ret = mmc_probe(bis);
+ if (ret)
+ return ret;
#ifndef CONFIG_SPL_BUILD
print_mmc_devices(',');
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c
new file mode 100644
index 0000000000..f11c8e0039
--- /dev/null
+++ b/drivers/mmc/rockchip_dw_mmc.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dwmmc.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/periph.h>
+#include <linux/err.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rockchip_dwmmc_priv {
+ struct udevice *clk;
+ struct rk3288_grf *grf;
+ struct dwmci_host host;
+};
+
+static uint rockchip_dwmmc_get_mmc_clk(struct dwmci_host *host, uint freq)
+{
+ struct udevice *dev = host->priv;
+ struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = clk_set_periph_rate(priv->clk, PERIPH_ID_SDMMC0 + host->dev_index,
+ freq);
+ if (ret < 0) {
+ debug("%s: err=%d\n", __func__, ret);
+ return ret;
+ }
+
+ return freq;
+}
+
+static int rockchip_dwmmc_ofdata_to_platdata(struct udevice *dev)
+{
+ struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
+ struct dwmci_host *host = &priv->host;
+
+ host->name = dev->name;
+ host->ioaddr = (void *)dev_get_addr(dev);
+ host->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+ "bus-width", 4);
+ host->get_mmc_clk = rockchip_dwmmc_get_mmc_clk;
+ host->priv = dev;
+
+ /* TODO(sjg@chromium.org): Remove the need for this hack */
+ host->dev_index = (ulong)host->ioaddr == 0xff0f0000 ? 0 : 1;
+
+ return 0;
+}
+
+static int rockchip_dwmmc_probe(struct udevice *dev)
+{
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct rockchip_dwmmc_priv *priv = dev_get_priv(dev);
+ struct dwmci_host *host = &priv->host;
+ u32 minmax[2];
+ int ret;
+
+ priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+ if (IS_ERR(priv->grf))
+ return PTR_ERR(priv->grf);
+ ret = uclass_get_device(UCLASS_CLK, CLK_GENERAL, &priv->clk);
+ if (ret)
+ return ret;
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
+ "clock-freq-min-max", minmax, 2);
+ if (!ret)
+ ret = add_dwmci(host, minmax[1], minmax[0]);
+ if (ret)
+ return ret;
+
+ upriv->mmc = host->mmc;
+
+ return 0;
+}
+
+static const struct udevice_id rockchip_dwmmc_ids[] = {
+ { .compatible = "rockchip,rk3288-dw-mshc" },
+ { }
+};
+
+U_BOOT_DRIVER(rockchip_dwmmc_drv) = {
+ .name = "rockchip_dwmmc",
+ .id = UCLASS_MMC,
+ .of_match = rockchip_dwmmc_ids,
+ .ofdata_to_platdata = rockchip_dwmmc_ofdata_to_platdata,
+ .probe = rockchip_dwmmc_probe,
+ .priv_auto_alloc_size = sizeof(struct rockchip_dwmmc_priv),
+};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 30b8e452ef..b8146df99b 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -47,7 +47,10 @@ config PINMUX
default y
help
This option enables pin multiplexing through the generic pinctrl
- framework.
+ framework. Most SoCs have their own own multiplexing arrangement
+ where a single pin can be used for several functions. An SoC pinctrl
+ driver allows the required function to be selected for each pin.
+ The driver is typically controlled by the device tree.
config PINCONF
bool "Support pin configuration controllers"
@@ -86,6 +89,12 @@ config SPL_PINMUX
help
This option is an SPL-variant of the PINMUX option.
See the help of PINMUX for details.
+ The pinctrl subsystem can add a substantial overhead to the SPL
+ image since it typically requires quite a few tables either in the
+ driver or in the device tree. If this is acceptable and you need
+ to adjust pin multiplexing in SPL in order to boot into U-Boot,
+ enable this option. You will need to enable device tree in SPL
+ for this to work.
config SPL_PINCONF
bool "Support pin configuration controllers in SPL"
@@ -96,6 +105,15 @@ config SPL_PINCONF
if PINCTRL || SPL_PINCTRL
+config ROCKCHIP_PINCTRL
+ bool "Rockchip pin control driver"
+ depends on DM
+ help
+ Support pin multiplexing control on Rockchip SoCs. The driver is
+ controlled by a device tree node which contains both the GPIO
+ definitions and pin control functions for each available multiplex
+ function.
+
config PINCTRL_SANDBOX
bool "Sandbox pinctrl driver"
depends on SANDBOX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 35decf49c3..f537df4e88 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -1,4 +1,5 @@
obj-y += pinctrl-uclass.o
obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o
+obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
diff --git a/drivers/pinctrl/pinctrl-uclass.c b/drivers/pinctrl/pinctrl-uclass.c
index d96c201e83..58001ef572 100644
--- a/drivers/pinctrl/pinctrl-uclass.c
+++ b/drivers/pinctrl/pinctrl-uclass.c
@@ -11,6 +11,7 @@
#include <dm/device.h>
#include <dm/lists.h>
#include <dm/pinctrl.h>
+#include <dm/root.h>
#include <dm/uclass.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -159,7 +160,8 @@ static int pinctrl_select_state_full(struct udevice *dev, const char *statename)
static int pinconfig_post_bind(struct udevice *dev)
{
- return 0;
+ /* Scan the bus for devices */
+ return dm_scan_fdt_node(dev, gd->fdt_blob, dev->of_offset, false);
}
#endif
@@ -205,6 +207,31 @@ int pinctrl_select_state(struct udevice *dev, const char *statename)
return 0;
}
+int pinctrl_request(struct udevice *dev, int func, int flags)
+{
+ struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+
+ if (!ops->request)
+ return -ENOSYS;
+
+ return ops->request(dev, func, flags);
+}
+
+int pinctrl_request_noflags(struct udevice *dev, int func)
+{
+ return pinctrl_request(dev, func, 0);
+}
+
+int pinctrl_get_periph_id(struct udevice *dev, struct udevice *periph)
+{
+ struct pinctrl_ops *ops = pinctrl_get_ops(dev);
+
+ if (!ops->get_periph_id)
+ return -ENOSYS;
+
+ return ops->get_periph_id(dev, periph);
+}
+
/**
* pinconfig_post-bind() - post binding for PINCTRL uclass
* Recursively bind child nodes as pinconfig devices in case of full pinctrl.
@@ -222,15 +249,10 @@ static int pinctrl_post_bind(struct udevice *dev)
}
/*
- * If set_state callback is set, we assume this pinctrl driver is the
- * full implementation. In this case, its child nodes should be bound
- * so that peripheral devices can easily search in parent devices
- * during later DT-parsing.
+ * The pinctrl driver child nodes should be bound so that peripheral
+ * devices can easily search in parent devices during later DT-parsing.
*/
- if (ops->set_state)
- return pinconfig_post_bind(dev);
-
- return 0;
+ return pinconfig_post_bind(dev);
}
UCLASS_DRIVER(pinctrl) = {
diff --git a/drivers/pinctrl/rockchip/Makefile b/drivers/pinctrl/rockchip/Makefile
new file mode 100644
index 0000000000..251bace797
--- /dev/null
+++ b/drivers/pinctrl/rockchip/Makefile
@@ -0,0 +1,8 @@
+#
+# Copyright (c) 2015 Google, Inc
+# Written by Simon Glass <sjg@chromium.org>
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_ROCKCHIP_PINCTRL) += pinctrl_rk3288.o
diff --git a/drivers/pinctrl/rockchip/pinctrl_rk3288.c b/drivers/pinctrl/rockchip/pinctrl_rk3288.c
new file mode 100644
index 0000000000..5205498b36
--- /dev/null
+++ b/drivers/pinctrl/rockchip/pinctrl_rk3288.c
@@ -0,0 +1,441 @@
+/*
+ * Pinctrl driver for Rockchip SoCs
+ * Copyright (c) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/grf_rk3288.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/periph.h>
+#include <asm/arch/pmu_rk3288.h>
+#include <dm/pinctrl.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct rk3288_pinctrl_priv {
+ struct rk3288_grf *grf;
+ struct rk3288_pmu *pmu;
+};
+
+static void pinctrl_rk3288_pwm_config(struct rk3288_grf *grf, int pwm_id)
+{
+ switch (pwm_id) {
+ case PERIPH_ID_PWM0:
+ rk_clrsetreg(&grf->gpio7a_iomux, GPIO7A0_MASK << GPIO7A0_SHIFT,
+ GPIO7A0_PWM_0 << GPIO7A0_SHIFT);
+ break;
+ case PERIPH_ID_PWM1:
+ rk_clrsetreg(&grf->gpio7a_iomux, GPIO7A1_MASK << GPIO7A1_SHIFT,
+ GPIO7A1_PWM_1 << GPIO7A1_SHIFT);
+ break;
+ case PERIPH_ID_PWM2:
+ rk_clrsetreg(&grf->gpio7a_iomux, GPIO7C6_MASK << GPIO7C6_SHIFT,
+ GPIO7C6_PWM_2 << GPIO7C6_SHIFT);
+ break;
+ case PERIPH_ID_PWM3:
+ rk_clrsetreg(&grf->gpio7a_iomux, GPIO7C7_MASK << GPIO7C6_SHIFT,
+ GPIO7C7_PWM_3 << GPIO7C7_SHIFT);
+ break;
+ default:
+ debug("pwm id = %d iomux error!\n", pwm_id);
+ break;
+ }
+}
+
+static void pinctrl_rk3288_i2c_config(struct rk3288_grf *grf,
+ struct rk3288_pmu *pmu, int i2c_id)
+{
+ switch (i2c_id) {
+ case PERIPH_ID_I2C0:
+ clrsetbits_le32(&pmu->gpio0b_iomux,
+ GPIO0_B7_MASK << GPIO0_B7_SHIFT,
+ GPIO0_B7_I2C0PMU_SDA << GPIO0_B7_SHIFT);
+ clrsetbits_le32(&pmu->gpio0b_iomux,
+ GPIO0_C0_MASK << GPIO0_C0_SHIFT,
+ GPIO0_C0_I2C0PMU_SCL << GPIO0_C0_SHIFT);
+ break;
+ case PERIPH_ID_I2C1:
+ rk_clrsetreg(&grf->gpio8a_iomux,
+ GPIO8A4_MASK << GPIO8A4_SHIFT |
+ GPIO8A5_MASK << GPIO8A5_SHIFT,
+ GPIO8A4_I2C2SENSOR_SDA << GPIO8A4_SHIFT |
+ GPIO8A5_I2C2SENSOR_SCL << GPIO8A5_SHIFT);
+ break;
+ case PERIPH_ID_I2C2:
+ rk_clrsetreg(&grf->gpio6b_iomux,
+ GPIO6B1_MASK << GPIO6B1_SHIFT |
+ GPIO6B2_MASK << GPIO6B2_SHIFT,
+ GPIO6B1_I2C1AUDIO_SDA << GPIO6B1_SHIFT |
+ GPIO6B2_I2C1AUDIO_SCL << GPIO6B2_SHIFT);
+ break;
+ case PERIPH_ID_I2C3:
+ rk_clrsetreg(&grf->gpio2c_iomux,
+ GPIO2C1_MASK << GPIO2C1_SHIFT |
+ GPIO2C0_MASK << GPIO2C0_SHIFT,
+ GPIO2C1_I2C3CAM_SDA << GPIO2C1_SHIFT |
+ GPIO2C0_I2C3CAM_SCL << GPIO2C0_SHIFT);
+ break;
+ case PERIPH_ID_I2C4:
+ rk_clrsetreg(&grf->gpio7cl_iomux,
+ GPIO7C1_MASK << GPIO7C1_SHIFT |
+ GPIO7C2_MASK << GPIO7C2_SHIFT,
+ GPIO7C1_I2C4TP_SDA << GPIO7C1_SHIFT |
+ GPIO7C2_I2C4TP_SCL << GPIO7C2_SHIFT);
+ break;
+ case PERIPH_ID_I2C5:
+ rk_clrsetreg(&grf->gpio7cl_iomux,
+ GPIO7C3_MASK << GPIO7C3_SHIFT,
+ GPIO7C3_I2C5HDMI_SDA << GPIO7C3_SHIFT);
+ rk_clrsetreg(&grf->gpio7ch_iomux,
+ GPIO7C4_MASK << GPIO7C4_SHIFT,
+ GPIO7C4_I2C5HDMI_SCL << GPIO7C4_SHIFT);
+ break;
+ default:
+ debug("i2c id = %d iomux error!\n", i2c_id);
+ break;
+ }
+}
+
+static void pinctrl_rk3288_lcdc_config(struct rk3288_grf *grf, int lcd_id)
+{
+ switch (lcd_id) {
+ case PERIPH_ID_LCDC0:
+ rk_clrsetreg(&grf->gpio1d_iomux,
+ GPIO1D3_MASK << GPIO1D0_SHIFT |
+ GPIO1D2_MASK << GPIO1D2_SHIFT |
+ GPIO1D1_MASK << GPIO1D1_SHIFT |
+ GPIO1D0_MASK << GPIO1D0_SHIFT,
+ GPIO1D3_LCDC0_DCLK << GPIO1D3_SHIFT |
+ GPIO1D2_LCDC0_DEN << GPIO1D2_SHIFT |
+ GPIO1D1_LCDC0_VSYNC << GPIO1D1_SHIFT |
+ GPIO1D0_LCDC0_HSYNC << GPIO1D0_SHIFT);
+ break;
+ default:
+ debug("lcdc id = %d iomux error!\n", lcd_id);
+ break;
+ }
+}
+
+static int pinctrl_rk3288_spi_config(struct rk3288_grf *grf,
+ enum periph_id spi_id, int cs)
+{
+ switch (spi_id) {
+ case PERIPH_ID_SPI0:
+ switch (cs) {
+ case 0:
+ rk_clrsetreg(&grf->gpio5b_iomux,
+ GPIO5B5_MASK << GPIO5B5_SHIFT,
+ GPIO5B5_SPI0_CSN0 << GPIO5B5_SHIFT);
+ break;
+ case 1:
+ rk_clrsetreg(&grf->gpio5c_iomux,
+ GPIO5C0_MASK << GPIO5C0_SHIFT,
+ GPIO5C0_SPI0_CSN1 << GPIO5C0_SHIFT);
+ break;
+ default:
+ goto err;
+ }
+ rk_clrsetreg(&grf->gpio5b_iomux,
+ GPIO5B7_MASK << GPIO5B7_SHIFT |
+ GPIO5B6_MASK << GPIO5B6_SHIFT |
+ GPIO5B4_MASK << GPIO5B4_SHIFT,
+ GPIO5B7_SPI0_RXD << GPIO5B7_SHIFT |
+ GPIO5B6_SPI0_TXD << GPIO5B6_SHIFT |
+ GPIO5B4_SPI0_CLK << GPIO5B4_SHIFT);
+ break;
+ case PERIPH_ID_SPI1:
+ if (cs != 0)
+ goto err;
+ rk_clrsetreg(&grf->gpio7b_iomux,
+ GPIO7B6_MASK << GPIO7B6_SHIFT |
+ GPIO7B7_MASK << GPIO7B7_SHIFT |
+ GPIO7B5_MASK << GPIO7B5_SHIFT |
+ GPIO7B4_MASK << GPIO7B4_SHIFT,
+ GPIO7B6_SPI1_RXD << GPIO7B6_SHIFT |
+ GPIO7B7_SPI1_TXD << GPIO7B7_SHIFT |
+ GPIO7B5_SPI1_CSN0 << GPIO7B5_SHIFT |
+ GPIO7B4_SPI1_CLK << GPIO7B4_SHIFT);
+ break;
+ case PERIPH_ID_SPI2:
+ switch (cs) {
+ case 0:
+ rk_clrsetreg(&grf->gpio8a_iomux,
+ GPIO8A7_MASK << GPIO8A7_SHIFT,
+ GPIO8A7_SPI2_CSN0 << GPIO8A7_SHIFT);
+ break;
+ case 1:
+ rk_clrsetreg(&grf->gpio8a_iomux,
+ GPIO8A3_MASK << GPIO8A3_SHIFT,
+ GPIO8A3_SPI2_CSN1 << GPIO8A3_SHIFT);
+ break;
+ default:
+ goto err;
+ }
+ rk_clrsetreg(&grf->gpio8b_iomux,
+ GPIO8B1_MASK << GPIO8B1_SHIFT |
+ GPIO8B0_MASK << GPIO8B0_SHIFT,
+ GPIO8B1_SPI2_TXD << GPIO8B1_SHIFT |
+ GPIO8B0_SPI2_RXD << GPIO8B0_SHIFT);
+ rk_clrsetreg(&grf->gpio8a_iomux,
+ GPIO8A6_MASK << GPIO8A6_SHIFT,
+ GPIO8A6_SPI2_CLK << GPIO8A6_SHIFT);
+ break;
+ default:
+ goto err;
+ }
+
+ return 0;
+err:
+ debug("rkspi: periph%d cs=%d not supported", spi_id, cs);
+ return -ENOENT;
+}
+
+static void pinctrl_rk3288_uart_config(struct rk3288_grf *grf, int uart_id)
+{
+ switch (uart_id) {
+ case PERIPH_ID_UART_BT:
+ rk_clrsetreg(&grf->gpio4c_iomux,
+ GPIO4C3_MASK << GPIO4C3_SHIFT |
+ GPIO4C2_MASK << GPIO4C2_SHIFT |
+ GPIO4C1_MASK << GPIO4C1_SHIFT |
+ GPIO4C0_MASK << GPIO4C0_SHIFT,
+ GPIO4C3_UART0BT_RTSN << GPIO4C3_SHIFT |
+ GPIO4C2_UART0BT_CTSN << GPIO4C2_SHIFT |
+ GPIO4C1_UART0BT_SOUT << GPIO4C1_SHIFT |
+ GPIO4C0_UART0BT_SIN << GPIO4C0_SHIFT);
+ break;
+ case PERIPH_ID_UART_BB:
+ rk_clrsetreg(&grf->gpio5b_iomux,
+ GPIO5B3_MASK << GPIO5B3_SHIFT |
+ GPIO5B2_MASK << GPIO5B2_SHIFT |
+ GPIO5B1_MASK << GPIO5B1_SHIFT |
+ GPIO5B0_MASK << GPIO5B0_SHIFT,
+ GPIO5B3_UART1BB_RTSN << GPIO5B3_SHIFT |
+ GPIO5B2_UART1BB_CTSN << GPIO5B2_SHIFT |
+ GPIO5B1_UART1BB_SOUT << GPIO5B1_SHIFT |
+ GPIO5B0_UART1BB_SIN << GPIO5B0_SHIFT);
+ break;
+ case PERIPH_ID_UART_DBG:
+ rk_clrsetreg(&grf->gpio7ch_iomux,
+ GPIO7C7_MASK << GPIO7C7_SHIFT |
+ GPIO7C6_MASK << GPIO7C6_SHIFT,
+ GPIO7C7_UART2DBG_SOUT << GPIO7C7_SHIFT |
+ GPIO7C6_UART2DBG_SIN << GPIO7C6_SHIFT);
+ break;
+ case PERIPH_ID_UART_GPS:
+ rk_clrsetreg(&grf->gpio7b_iomux,
+ GPIO7B2_MASK << GPIO7B2_SHIFT |
+ GPIO7B1_MASK << GPIO7B1_SHIFT |
+ GPIO7B0_MASK << GPIO7B0_SHIFT,
+ GPIO7B2_UART3GPS_RTSN << GPIO7B2_SHIFT |
+ GPIO7B1_UART3GPS_CTSN << GPIO7B1_SHIFT |
+ GPIO7B0_UART3GPS_SOUT << GPIO7B0_SHIFT);
+ rk_clrsetreg(&grf->gpio7a_iomux,
+ GPIO7A7_MASK << GPIO7A7_SHIFT,
+ GPIO7A7_UART3GPS_SIN << GPIO7A7_SHIFT);
+ break;
+ case PERIPH_ID_UART_EXP:
+ rk_clrsetreg(&grf->gpio5b_iomux,
+ GPIO5B5_MASK << GPIO5B5_SHIFT |
+ GPIO5B4_MASK << GPIO5B4_SHIFT |
+ GPIO5B6_MASK << GPIO5B6_SHIFT |
+ GPIO5B7_MASK << GPIO5B7_SHIFT,
+ GPIO5B5_UART4EXP_RTSN << GPIO5B5_SHIFT |
+ GPIO5B4_UART4EXP_CTSN << GPIO5B4_SHIFT |
+ GPIO5B6_UART4EXP_SOUT << GPIO5B6_SHIFT |
+ GPIO5B7_UART4EXP_SIN << GPIO5B7_SHIFT);
+ break;
+ default:
+ debug("uart id = %d iomux error!\n", uart_id);
+ break;
+ }
+}
+
+static void pinctrl_rk3288_sdmmc_config(struct rk3288_grf *grf, int mmc_id)
+{
+ switch (mmc_id) {
+ case PERIPH_ID_EMMC:
+ rk_clrsetreg(&grf->gpio3a_iomux, 0xffff,
+ GPIO3A7_EMMC_DATA7 << GPIO3A7_SHIFT |
+ GPIO3A6_EMMC_DATA6 << GPIO3A6_SHIFT |
+ GPIO3A5_EMMC_DATA5 << GPIO3A5_SHIFT |
+ GPIO3A4_EMMC_DATA4 << GPIO3A4_SHIFT |
+ GPIO3A3_EMMC_DATA3 << GPIO3A3_SHIFT |
+ GPIO3A2_EMMC_DATA2 << GPIO3A2_SHIFT |
+ GPIO3A1_EMMC_DATA1 << GPIO3A1_SHIFT |
+ GPIO3A0_EMMC_DATA0 << GPIO3A0_SHIFT);
+ rk_clrsetreg(&grf->gpio3b_iomux, GPIO3B1_MASK << GPIO3B1_SHIFT,
+ GPIO3B1_EMMC_PWREN << GPIO3B1_SHIFT);
+ rk_clrsetreg(&grf->gpio3c_iomux,
+ GPIO3C0_MASK << GPIO3C0_SHIFT,
+ GPIO3C0_EMMC_CMD << GPIO3C0_SHIFT);
+ break;
+ case PERIPH_ID_SDCARD:
+ rk_clrsetreg(&grf->gpio6c_iomux, 0xffff,
+ GPIO6C6_SDMMC0_DECTN << GPIO6C6_SHIFT |
+ GPIO6C5_SDMMC0_CMD << GPIO6C5_SHIFT |
+ GPIO6C4_SDMMC0_CLKOUT << GPIO6C4_SHIFT |
+ GPIO6C3_SDMMC0_DATA3 << GPIO6C3_SHIFT |
+ GPIO6C2_SDMMC0_DATA2 << GPIO6C2_SHIFT |
+ GPIO6C1_SDMMC0_DATA1 << GPIO6C1_SHIFT |
+ GPIO6C0_SDMMC0_DATA0 << GPIO6C0_SHIFT);
+
+ /* use sdmmc0 io, disable JTAG function */
+ rk_clrsetreg(&grf->soc_con0, 1 << GRF_FORCE_JTAG_SHIFT, 0);
+ break;
+ default:
+ debug("mmc id = %d iomux error!\n", mmc_id);
+ break;
+ }
+}
+
+static void pinctrl_rk3288_hdmi_config(struct rk3288_grf *grf, int hdmi_id)
+{
+ switch (hdmi_id) {
+ case PERIPH_ID_HDMI:
+ rk_clrsetreg(&grf->gpio7cl_iomux, GPIO7C3_MASK << GPIO7C3_SHIFT,
+ GPIO7C3_EDPHDMII2C_SDA << GPIO7C3_SHIFT);
+ rk_clrsetreg(&grf->gpio7ch_iomux, GPIO7C4_MASK << GPIO7C4_SHIFT,
+ GPIO7C4_EDPHDMII2C_SCL << GPIO7C4_SHIFT);
+ break;
+ default:
+ debug("hdmi id = %d iomux error!\n", hdmi_id);
+ break;
+ }
+}
+
+static int rk3288_pinctrl_request(struct udevice *dev, int func, int flags)
+{
+ struct rk3288_pinctrl_priv *priv = dev_get_priv(dev);
+
+ debug("%s: func=%x, flags=%x\n", __func__, func, flags);
+ switch (func) {
+ case PERIPH_ID_PWM0:
+ case PERIPH_ID_PWM1:
+ case PERIPH_ID_PWM2:
+ case PERIPH_ID_PWM3:
+ case PERIPH_ID_PWM4:
+ pinctrl_rk3288_pwm_config(priv->grf, func);
+ break;
+ case PERIPH_ID_I2C0:
+ case PERIPH_ID_I2C1:
+ case PERIPH_ID_I2C2:
+ case PERIPH_ID_I2C3:
+ case PERIPH_ID_I2C4:
+ case PERIPH_ID_I2C5:
+ pinctrl_rk3288_i2c_config(priv->grf, priv->pmu, func);
+ break;
+ case PERIPH_ID_SPI0:
+ case PERIPH_ID_SPI1:
+ case PERIPH_ID_SPI2:
+ pinctrl_rk3288_spi_config(priv->grf, func, flags);
+ break;
+ case PERIPH_ID_UART0:
+ case PERIPH_ID_UART1:
+ case PERIPH_ID_UART2:
+ case PERIPH_ID_UART3:
+ case PERIPH_ID_UART4:
+ pinctrl_rk3288_uart_config(priv->grf, func);
+ break;
+ case PERIPH_ID_LCDC0:
+ case PERIPH_ID_LCDC1:
+ pinctrl_rk3288_lcdc_config(priv->grf, func);
+ break;
+ case PERIPH_ID_SDMMC0:
+ case PERIPH_ID_SDMMC1:
+ pinctrl_rk3288_sdmmc_config(priv->grf, func);
+ break;
+ case PERIPH_ID_HDMI:
+ pinctrl_rk3288_hdmi_config(priv->grf, func);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rk3288_pinctrl_get_periph_id(struct udevice *dev,
+ struct udevice *periph)
+{
+ u32 cell[3];
+ int ret;
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, periph->of_offset,
+ "interrupts", cell, ARRAY_SIZE(cell));
+ if (ret < 0)
+ return -EINVAL;
+
+ switch (cell[1]) {
+ case 44:
+ return PERIPH_ID_SPI0;
+ case 45:
+ return PERIPH_ID_SPI1;
+ case 46:
+ return PERIPH_ID_SPI2;
+ case 60:
+ return PERIPH_ID_I2C0;
+ case 62: /* Note strange order */
+ return PERIPH_ID_I2C1;
+ case 61:
+ return PERIPH_ID_I2C2;
+ case 63:
+ return PERIPH_ID_I2C3;
+ case 64:
+ return PERIPH_ID_I2C4;
+ case 65:
+ return PERIPH_ID_I2C5;
+ }
+
+ return -ENOENT;
+}
+
+static int rk3288_pinctrl_set_state_simple(struct udevice *dev,
+ struct udevice *periph)
+{
+ int func;
+
+ func = rk3288_pinctrl_get_periph_id(dev, periph);
+ if (func < 0)
+ return func;
+ return rk3288_pinctrl_request(dev, func, 0);
+}
+
+static struct pinctrl_ops rk3288_pinctrl_ops = {
+ .set_state_simple = rk3288_pinctrl_set_state_simple,
+ .request = rk3288_pinctrl_request,
+ .get_periph_id = rk3288_pinctrl_get_periph_id,
+};
+
+static int rk3288_pinctrl_probe(struct udevice *dev)
+{
+ struct rk3288_pinctrl_priv *priv = dev_get_priv(dev);
+
+ priv->grf = syscon_get_first_range(ROCKCHIP_SYSCON_GRF);
+ priv->pmu = syscon_get_first_range(ROCKCHIP_SYSCON_PMU);
+ debug("%s: grf=%p, pmu=%p\n", __func__, priv->grf, priv->pmu);
+
+ return 0;
+}
+
+static const struct udevice_id rk3288_pinctrl_ids[] = {
+ { .compatible = "rockchip,rk3288-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_rk3288) = {
+ .name = "pinctrl_rk3288",
+ .id = UCLASS_PINCTRL,
+ .of_match = rk3288_pinctrl_ids,
+ .priv_auto_alloc_size = sizeof(struct rk3288_pinctrl_priv),
+ .ops = &rk3288_pinctrl_ops,
+ .probe = rk3288_pinctrl_probe,
+};
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index fc6a3743dc..547fd1aaa6 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -10,6 +10,15 @@ config DM_PMIC
- 'drivers/power/pmic/pmic-uclass.c'
- 'include/power/pmic.h'
+config PMIC_ACT8846
+ bool "Enable support for the active-semi 8846 PMIC"
+ depends on DM_PMIC && DM_I2C
+ ---help---
+ This PMIC includes 4 DC/DC step-down buck regulators and 8 low-dropout
+ regulators (LDOs). It also provides some GPIO, reset and battery
+ functions. It uses an I2C interface and is designed for use with
+ tablets and smartphones.
+
config DM_PMIC_PFUZE100
bool "Enable Driver Model for PMIC PFUZE100"
depends on DM_PMIC
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index e7a761703e..00fde71b2c 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_DM_PMIC) += pmic-uclass.o
obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
obj-$(CONFIG_DM_PMIC_PFUZE100) += pfuze100.o
obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
+obj-$(CONFIG_PMIC_ACT8846) += act8846.o
obj-$(CONFIG_PMIC_TPS65090) += tps65090.o
obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o
diff --git a/drivers/power/pmic/act8846.c b/drivers/power/pmic/act8846.c
new file mode 100644
index 0000000000..ff096b3a9c
--- /dev/null
+++ b/drivers/power/pmic/act8846.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <fdtdec.h>
+#include <libfdt.h>
+#include <power/act8846_pmic.h>
+#include <power/pmic.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const struct pmic_child_info pmic_children_info[] = {
+ { .prefix = "REG", .driver = "act8846_reg"},
+ { },
+};
+
+static int act8846_reg_count(struct udevice *dev)
+{
+ return ACT8846_NUM_OF_REGS;
+}
+
+static int act8846_write(struct udevice *dev, uint reg, const uint8_t *buff,
+ int len)
+{
+ if (dm_i2c_write(dev, reg, buff, len)) {
+ debug("write error to device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int act8846_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ if (dm_i2c_read(dev, reg, buff, len)) {
+ debug("read error from device: %p register: %#x!", dev, reg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int act8846_bind(struct udevice *dev)
+{
+ const void *blob = gd->fdt_blob;
+ int regulators_node;
+ int children;
+
+ regulators_node = fdt_subnode_offset(blob, dev->of_offset,
+ "regulators");
+ if (regulators_node <= 0) {
+ debug("%s: %s regulators subnode not found!", __func__,
+ dev->name);
+ return -ENXIO;
+ }
+
+ debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+ children = pmic_bind_children(dev, regulators_node, pmic_children_info);
+ if (!children)
+ debug("%s: %s - no child found\n", __func__, dev->name);
+
+ /* Always return success for this device */
+ return 0;
+}
+
+static struct dm_pmic_ops act8846_ops = {
+ .reg_count = act8846_reg_count,
+ .read = act8846_read,
+ .write = act8846_write,
+};
+
+static const struct udevice_id act8846_ids[] = {
+ { .compatible = "active-semi,act8846" },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_act8846) = {
+ .name = "act8846 pmic",
+ .id = UCLASS_PMIC,
+ .of_match = act8846_ids,
+ .bind = act8846_bind,
+ .ops = &act8846_ops,
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index 77d64e43c6..434dd029b5 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -16,6 +16,15 @@ config DM_REGULATOR
for this purpose if PMIC I/O driver is implemented or dm_scan_fdt_node()
otherwise. Detailed information can be found in the header file.
+config REGULATOR_ACT8846
+ bool "Enable driver for ACT8846 regulator"
+ depends on DM_REGULATOR && PMIC_ACT8846
+ ---help---
+ Enable support for the regulator functions of the ACT8846 PMIC. The
+ driver implements get/set api for the various BUCKS and LDOS supported
+ by the PMIC device. This driver is controlled by a device tree node
+ which includes voltage limits.
+
config DM_REGULATOR_PFUZE100
bool "Enable Driver Model for REGULATOR PFUZE100"
depends on DM_REGULATOR && DM_PMIC_PFUZE100
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 7035936a35..c85978e024 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -6,6 +6,7 @@
#
obj-$(CONFIG_DM_REGULATOR) += regulator-uclass.o
+obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o
obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_DM_REGULATOR_PFUZE100) += pfuze100.o
obj-$(CONFIG_DM_REGULATOR_FIXED) += fixed.o
diff --git a/drivers/power/regulator/act8846.c b/drivers/power/regulator/act8846.c
new file mode 100644
index 0000000000..255f8b096e
--- /dev/null
+++ b/drivers/power/regulator/act8846.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * Based on Rockchip's drivers/power/pmic/pmic_act8846.c:
+ * Copyright (C) 2012 rockchips
+ * zyw <zyw@rock-chips.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <power/act8846_pmic.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+static const u16 voltage_map[] = {
+ 600, 625, 650, 675, 700, 725, 750, 775,
+ 800, 825, 850, 875, 900, 925, 950, 975,
+ 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175,
+ 1200, 1250, 1300, 1350, 1400, 1450, 1500, 1550,
+ 1600, 1650, 1700, 1750, 1800, 1850, 1900, 1950,
+ 2000, 2050, 2100, 2150, 2200, 2250, 2300, 2350,
+ 2400, 2500, 2600, 2700, 2800, 2900, 3000, 3100,
+ 3200, 3300, 3400, 3500, 3600, 3700, 3800, 3900,
+};
+
+enum {
+ REG_SYS0,
+ REG_SYS1,
+ REG1_VOL = 0x10,
+ REG1_CTL = 0X11,
+ REG2_VOL0 = 0x20,
+ REG2_VOL1,
+ REG2_CTL,
+ REG3_VOL0 = 0x30,
+ REG3_VOL1,
+ REG3_CTL,
+ REG4_VOL0 = 0x40,
+ REG4_VOL1,
+ REG4_CTL,
+ REG5_VOL = 0x50,
+ REG5_CTL,
+ REG6_VOL = 0X58,
+ REG6_CTL,
+ REG7_VOL = 0x60,
+ REG7_CTL,
+ REG8_VOL = 0x68,
+ REG8_CTL,
+ REG9_VOL = 0x70,
+ REG9_CTL,
+ REG10_VOL = 0x80,
+ REG10_CTL,
+ REG11_VOL = 0x90,
+ REG11_CTL,
+ REG12_VOL = 0xa0,
+ REG12_CTL,
+ REG13 = 0xb1,
+};
+
+static const u8 addr_vol[] = {
+ 0, REG1_VOL, REG2_VOL0, REG3_VOL0, REG4_VOL0,
+ REG5_VOL, REG6_VOL, REG7_VOL, REG8_VOL, REG9_VOL,
+ REG10_VOL, REG11_VOL, REG12_VOL,
+};
+
+static const u8 addr_ctl[] = {
+ 0, REG1_CTL, REG2_CTL, REG3_CTL, REG4_CTL,
+ REG5_CTL, REG6_CTL, REG7_CTL, REG8_CTL, REG9_CTL,
+ REG10_CTL, REG11_CTL, REG12_CTL,
+};
+
+static int check_volt_table(const u16 *volt_table, int uvolt)
+{
+ int i;
+
+ for (i = VOL_MIN_IDX; i < VOL_MAX_IDX; i++) {
+ if (uvolt <= (volt_table[i] * 1000))
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int reg_get_value(struct udevice *dev)
+{
+ int reg = dev->driver_data;
+ int ret;
+
+ ret = pmic_reg_read(dev->parent, reg);
+ if (ret < 0)
+ return ret;
+
+ return voltage_map[ret & LDO_VOL_MASK] * 1000;
+}
+
+static int reg_set_value(struct udevice *dev, int uvolt)
+{
+ int reg = dev->driver_data;
+ int val;
+
+ val = check_volt_table(voltage_map, uvolt);
+ if (val < 0)
+ return val;
+
+ return pmic_clrsetbits(dev->parent, addr_vol[reg], LDO_VOL_MASK, val);
+}
+
+static int reg_set_enable(struct udevice *dev, bool enable)
+{
+ int reg = dev->driver_data;
+
+ return pmic_clrsetbits(dev->parent, addr_ctl[reg], LDO_EN_MASK,
+ enable ? LDO_EN_MASK : 0);
+}
+
+static bool reg_get_enable(struct udevice *dev)
+{
+ int reg = dev->driver_data;
+ int ret;
+
+ ret = pmic_reg_read(dev->parent, reg);
+ if (ret < 0)
+ return ret;
+
+ return ret & LDO_EN_MASK ? true : false;
+}
+
+static int act8846_reg_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_platdata *uc_pdata;
+ int reg = dev->driver_data;
+
+ uc_pdata = dev_get_uclass_platdata(dev);
+
+ uc_pdata->type = reg <= 4 ? REGULATOR_TYPE_BUCK : REGULATOR_TYPE_LDO;
+ uc_pdata->mode_count = 0;
+
+ return 0;
+}
+
+static const struct dm_regulator_ops act8846_reg_ops = {
+ .get_value = reg_get_value,
+ .set_value = reg_set_value,
+ .get_enable = reg_get_enable,
+ .set_enable = reg_set_enable,
+};
+
+U_BOOT_DRIVER(act8846_buck) = {
+ .name = "act8846_reg",
+ .id = UCLASS_REGULATOR,
+ .ops = &act8846_reg_ops,
+ .probe = act8846_reg_probe,
+};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 002f1dafe1..ccb80d2d1d 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -109,6 +109,15 @@ config DEBUG_UART_SHIFT
value. Use this value to specify the shift to use, where 0=byte
registers, 2=32-bit word registers, etc.
+config ROCKCHIP_SERIAL
+ bool "Rockchip on-chip UART support"
+ depends on ARCH_UNIPHIER && DM_SERIAL
+ help
+ Select this to enable a debug UART for Rockchip devices. This uses
+ the ns16550 driver. You will need to #define CONFIG_SYS_NS16550 in
+ your board config header. The clock input is automatically set to
+ use the oscillator (24MHz).
+
config SANDBOX_SERIAL
bool "Sandbox UART support"
depends on SANDBOX
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index 1d1f0361cd..869ea7b8fb 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -40,6 +40,7 @@ obj-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o
obj-$(CONFIG_BFIN_SERIAL) += serial_bfin.o
obj-$(CONFIG_FSL_LPUART) += serial_lpuart.o
obj-$(CONFIG_MXS_AUART) += mxs_auart.o
+obj-$(CONFIG_ROCKCHIP_SERIAL) += serial_rockchip.o
obj-$(CONFIG_ARC_SERIAL) += serial_arc.o
obj-$(CONFIG_TEGRA_SERIAL) += serial_tegra.o
obj-$(CONFIG_UNIPHIER_SERIAL) += serial_uniphier.o
diff --git a/drivers/serial/serial_rockchip.c b/drivers/serial/serial_rockchip.c
new file mode 100644
index 0000000000..0e7bbfc690
--- /dev/null
+++ b/drivers/serial/serial_rockchip.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2015 Google, Inc
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <ns16550.h>
+#include <serial.h>
+#include <asm/arch/clock.h>
+
+static const struct udevice_id rockchip_serial_ids[] = {
+ { .compatible = "rockchip,rk3288-uart" },
+ { }
+};
+
+static int rockchip_serial_ofdata_to_platdata(struct udevice *dev)
+{
+ struct ns16550_platdata *plat = dev_get_platdata(dev);
+ int ret;
+
+ ret = ns16550_serial_ofdata_to_platdata(dev);
+ if (ret)
+ return ret;
+
+ /* Do all Rockchip parts use 24MHz? */
+ plat->clock = 24 * 1000000;
+
+ return 0;
+}
+
+U_BOOT_DRIVER(serial_ns16550) = {
+ .name = "serial_rockchip",
+ .id = UCLASS_SERIAL,
+ .of_match = rockchip_serial_ids,
+ .ofdata_to_platdata = rockchip_serial_ofdata_to_platdata,
+ .platdata_auto_alloc_size = sizeof(struct ns16550_platdata),
+ .priv_auto_alloc_size = sizeof(struct NS16550),
+ .probe = ns16550_serial_probe,
+ .ops = &ns16550_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index c84a7b74d6..8e04fce6f2 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 ee88aa162b..de241be15a 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 0000000000..5e0c6ad278
--- /dev/null
+++ b/drivers/spi/rk_spi.c
@@ -0,0 +1,374 @@
+/*
+ * 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 {
+ 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(&regs->ctrlr0));
+ debug("ctrl1: \t\t0x%08x\n", readl(&regs->ctrlr1));
+ debug("ssienr: \t\t0x%08x\n", readl(&regs->enr));
+ debug("ser: \t\t0x%08x\n", readl(&regs->ser));
+ debug("baudr: \t\t0x%08x\n", readl(&regs->baudr));
+ debug("txftlr: \t\t0x%08x\n", readl(&regs->txftlr));
+ debug("rxftlr: \t\t0x%08x\n", readl(&regs->rxftlr));
+ debug("txflr: \t\t0x%08x\n", readl(&regs->txflr));
+ debug("rxflr: \t\t0x%08x\n", readl(&regs->rxflr));
+ debug("sr: \t\t0x%08x\n", readl(&regs->sr));
+ debug("imr: \t\t0x%08x\n", readl(&regs->imr));
+ debug("isr: \t\t0x%08x\n", readl(&regs->isr));
+ debug("dmacr: \t\t0x%08x\n", readl(&regs->dmacr));
+ debug("dmatdlr: \t0x%08x\n", readl(&regs->dmatdlr));
+ debug("dmardlr: \t0x%08x\n", readl(&regs->dmardlr));
+}
+
+static void rkspi_enable_chip(struct rockchip_spi *regs, bool enable)
+{
+ writel(enable ? 1 : 0, &regs->enr);
+}
+
+static void rkspi_set_clk(struct rockchip_spi_priv *priv, uint speed)
+{
+ uint clk_div;
+
+ clk_div = clk_get_divisor(priv->input_rate, speed);
+ debug("spi speed %u, div %u\n", speed, clk_div);
+
+ writel(clk_div, &priv->regs->baudr);
+}
+
+static int rkspi_wait_till_not_busy(struct rockchip_spi *regs)
+{
+ unsigned long start;
+
+ start = get_timer(0);
+ while (readl(&regs->sr) & SR_BUSY) {
+ if (get_timer(start) > ROCKCHIP_SPI_TIMEOUT_MS) {
+ debug("RK SPI: Status keeps busy for 1000us after a read/write!\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+static void spi_cs_activate(struct rockchip_spi *regs, uint cs)
+{
+ debug("activate cs%u\n", cs);
+ writel(1 << cs, &regs->ser);
+}
+
+static void spi_cs_deactivate(struct rockchip_spi *regs, uint cs)
+{
+ debug("deactivate cs%u\n", cs);
+ writel(0, &regs->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, &regs->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 (DEBUG_RK_SPI)
+ rkspi_dump_regs(regs);
+
+ /* 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, &regs->ctrlr1);
+ rkspi_enable_chip(regs, true);
+
+ toread = todo;
+ towrite = todo;
+ while (toread || towrite) {
+ u32 status = readl(&regs->sr);
+
+ if (towrite && !(status & SR_TF_FULL)) {
+ writel(out ? *out++ : 0, regs->txdr);
+ towrite--;
+ }
+ if (toread && !(status & SR_RF_EMPT)) {
+ u32 byte = readl(regs->rxdr);
+
+ if (in)
+ *in++ = byte;
+ toread--;
+ }
+ }
+ ret = rkspi_wait_till_not_busy(regs);
+ if (ret)
+ break;
+ len -= todo;
+ }
+
+ /* Deassert CS after transfer */
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(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 > 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,
+};
diff --git a/drivers/spi/rk_spi.h b/drivers/spi/rk_spi.h
new file mode 100644
index 0000000000..f1ac81203f
--- /dev/null
+++ b/drivers/spi/rk_spi.h
@@ -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 */
OpenPOWER on IntegriCloud