summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2014-05-05 13:33:09 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-05-05 13:33:09 +0200
commitcdfca5dccb21258eccd549604a8481020d5f3e9f (patch)
tree504faa1faa36ba0bc189cb5d9e1beda924dead56 /drivers
parent845f765b6e7f5830b14c7910203d271c43e6af6a (diff)
parent903b1d430904588ef9f6b2f897b46e3e3cc4516a (diff)
downloadbarebox-cdfca5dccb21258eccd549604a8481020d5f3e9f.tar.gz
barebox-cdfca5dccb21258eccd549604a8481020d5f3e9f.tar.xz
Merge branch 'for-next/rockchip'
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-fixed-factor.c36
-rw-r--r--drivers/clk/clk-gate.c54
-rw-r--r--drivers/clk/clk.c20
-rw-r--r--drivers/clk/mvebu/common.c2
-rw-r--r--drivers/clk/mxs/clk-imx28.c2
-rw-r--r--drivers/clk/rockchip/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-rockchip.c86
-rw-r--r--drivers/clk/socfpga.c19
-rw-r--r--drivers/clk/tegra/clk-periph.c2
-rw-r--r--drivers/mfd/Kconfig4
-rw-r--r--drivers/mfd/Makefile1
-rw-r--r--drivers/mfd/act8846.c154
-rw-r--r--drivers/net/Kconfig7
-rw-r--r--drivers/net/Makefile1
-rw-r--r--drivers/net/arc_emac.c469
-rw-r--r--drivers/pinctrl/Kconfig7
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c560
19 files changed, 1379 insertions, 48 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 0687b3cddd..fa707dd938 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o
obj-$(CONFIG_ARCH_MVEBU) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_ARCH_TEGRA) += tegra/
obj-$(CONFIG_CLK_SOCFPGA) += socfpga.o
obj-$(CONFIG_MACH_MIPS_ATH79) += clk-ar933x.o
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
index cb531b146b..40b63d6d50 100644
--- a/drivers/clk/clk-fixed-factor.c
+++ b/drivers/clk/clk-fixed-factor.c
@@ -92,3 +92,39 @@ struct clk *clk_fixed_factor(const char *name,
return &f->clk;
}
+
+#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER)
+/**
+ * of_fixed_factor_clk_setup() - Setup function for simple fixed factor clock
+ */
+static int of_fixed_factor_clk_setup(struct device_node *node)
+{
+ struct clk *clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ u32 div, mult;
+
+ if (of_property_read_u32(node, "clock-div", &div)) {
+ pr_err("%s Fixed factor clock <%s> must have a clock-div property\n",
+ __func__, node->name);
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(node, "clock-mult", &mult)) {
+ pr_err("%s Fixed factor clock <%s> must have a clock-mult property\n",
+ __func__, node->name);
+ return -EINVAL;
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ clk = clk_fixed_factor(clk_name, parent_name, mult, div, 0);
+ if (IS_ERR(clk))
+ return IS_ERR(clk);
+
+ return of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(fixed_factor_clk, "fixed-factor-clock",
+ of_fixed_factor_clk_setup);
+#endif
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index b298b193dc..85eba3d23a 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -25,42 +25,45 @@ struct clk_gate {
void __iomem *reg;
int shift;
const char *parent;
-#define CLK_GATE_INVERTED (1 << 0)
unsigned flags;
};
#define to_clk_gate(_clk) container_of(_clk, struct clk_gate, clk)
-static int clk_gate_enable(struct clk *clk)
+static void clk_gate_endisable(struct clk *clk, int enable)
{
- struct clk_gate *g = container_of(clk, struct clk_gate, clk);
+ struct clk_gate *gate = container_of(clk, struct clk_gate, clk);
+ int set = gate->flags & CLK_GATE_INVERTED ? 1 : 0;
u32 val;
- val = readl(g->reg);
+ set ^= enable;
- if (g->flags & CLK_GATE_INVERTED)
- val &= ~(1 << g->shift);
- else
- val |= 1 << g->shift;
+ if (gate->flags & CLK_GATE_HIWORD_MASK) {
+ val = BIT(gate->shift + 16);
+ if (set)
+ val |= BIT(gate->shift);
+ } else {
+ val = readl(gate->reg);
- writel(val, g->reg);
+ if (set)
+ val |= BIT(gate->shift);
+ else
+ val &= ~BIT(gate->shift);
+ }
- return 0;
+ writel(val, gate->reg);
}
-static void clk_gate_disable(struct clk *clk)
+static int clk_gate_enable(struct clk *clk)
{
- struct clk_gate *g = container_of(clk, struct clk_gate, clk);
- u32 val;
+ clk_gate_endisable(clk, 1);
- val = readl(g->reg);
-
- if (g->flags & CLK_GATE_INVERTED)
- val |= 1 << g->shift;
- else
- val &= ~(1 << g->shift);
+ return 0;
+}
- writel(val, g->reg);
+static void clk_gate_disable(struct clk *clk)
+{
+ clk_gate_endisable(clk, 0);
}
static int clk_gate_is_enabled(struct clk *clk)
@@ -85,7 +88,7 @@ static struct clk_ops clk_gate_ops = {
};
struct clk *clk_gate_alloc(const char *name, const char *parent,
- void __iomem *reg, u8 shift, unsigned flags)
+ void __iomem *reg, u8 shift, unsigned flags, u8 clk_gate_flags)
{
struct clk_gate *g = xzalloc(sizeof(*g));
@@ -97,6 +100,7 @@ struct clk *clk_gate_alloc(const char *name, const char *parent,
g->clk.flags = flags;
g->clk.parent_names = &g->parent;
g->clk.num_parents = 1;
+ g->flags = clk_gate_flags;
return &g->clk;
}
@@ -109,12 +113,12 @@ void clk_gate_free(struct clk *clk_gate)
}
struct clk *clk_gate(const char *name, const char *parent, void __iomem *reg,
- u8 shift, unsigned flags)
+ u8 shift, unsigned flags, u8 clk_gate_flags)
{
struct clk *g;
int ret;
- g = clk_gate_alloc(name , parent, reg, shift, flags);
+ g = clk_gate_alloc(name , parent, reg, shift, flags, clk_gate_flags);
ret = clk_register(g);
if (ret) {
@@ -131,13 +135,11 @@ struct clk *clk_gate_inverted(const char *name, const char *parent,
struct clk *clk;
struct clk_gate *g;
- clk = clk_gate(name, parent, reg, shift, flags);
+ clk = clk_gate(name, parent, reg, shift, flags, CLK_GATE_INVERTED);
if (IS_ERR(clk))
return clk;
g = container_of(clk, struct clk_gate, clk);
- g->flags = CLK_GATE_INVERTED;
-
return clk;
}
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 23b1a7a7ea..584e2f3242 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -390,6 +390,26 @@ struct clk *of_clk_get_from_provider(struct of_phandle_args *clkspec)
return clk;
}
+char *of_clk_get_parent_name(struct device_node *np, unsigned int index)
+{
+ struct of_phandle_args clkspec;
+ const char *clk_name;
+ int rc;
+
+ rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
+ &clkspec);
+ if (rc)
+ return NULL;
+
+ if (of_property_read_string_index(clkspec.np, "clock-output-names",
+ clkspec.args_count ? clkspec.args[0] : 0,
+ &clk_name) < 0)
+ clk_name = clkspec.np->name;
+
+ return xstrdup(clk_name);
+}
+EXPORT_SYMBOL_GPL(of_clk_get_parent_name);
+
/**
* of_clk_init() - Scan and init clock providers from the DT
* @root: parent of the first level to probe or NULL for the root of the tree
diff --git a/drivers/clk/mvebu/common.c b/drivers/clk/mvebu/common.c
index 658ce3e81a..f3be5f2974 100644
--- a/drivers/clk/mvebu/common.c
+++ b/drivers/clk/mvebu/common.c
@@ -188,7 +188,7 @@ int mvebu_clk_gating_probe(struct device_d *dev)
(desc[n].parent) ? desc[n].parent : default_parent;
gate->bit_idx = desc[n].bit_idx;
gate->clk = clk_gate(desc[n].name, parent,
- base, desc[n].bit_idx, 0);
+ base, desc[n].bit_idx, 0, 0);
WARN_ON(IS_ERR(gate->clk));
}
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
index 934a1940fb..36b71f6870 100644
--- a/drivers/clk/mxs/clk-imx28.c
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -128,7 +128,7 @@ int __init mx28_clocks_init(void __iomem *regs)
clks[fec] = mxs_clk_gate("fec", "fec_sleep", ENET, 30);
clks[usb0_phy] = mxs_clk_gate("usb0_phy", "pll0", PLL0CTRL0, 18);
clks[usb1_phy] = mxs_clk_gate("usb1_phy", "pll1", PLL1CTRL0, 18);
- clks[enet_out] = clk_gate("enet_out", "pll2", ENET, 18, 0);
+ clks[enet_out] = clk_gate("enet_out", "pll2", ENET, 18, 0, 0);
clks[lcdif_comp] = mxs_clk_lcdif("lcdif_comp", clks[ref_pix],
clks[lcdif_div], clks[lcdif]);
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
new file mode 100644
index 0000000000..1c5271fc1c
--- /dev/null
+++ b/drivers/clk/rockchip/Makefile
@@ -0,0 +1 @@
+obj-y += clk-rockchip.o \ No newline at end of file
diff --git a/drivers/clk/rockchip/clk-rockchip.c b/drivers/clk/rockchip/clk-rockchip.c
new file mode 100644
index 0000000000..99f836cb9f
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rockchip.c
@@ -0,0 +1,86 @@
+/*
+ * Clock gate driver for Rockchip SoCs
+ *
+ * Based on Linux driver:
+ * Copyright (c) 2013 MundoReader S.L.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <of_address.h>
+#include <malloc.h>
+
+static void __init rk2928_gate_clk_init(struct device_node *node)
+{
+ struct clk_onecell_data *clk_data;
+ const char *clk_parent;
+ const char *clk_name;
+ void __iomem *reg;
+ void __iomem *reg_idx;
+ int flags;
+ int qty;
+ int reg_bit;
+ int clkflags = CLK_SET_RATE_PARENT;
+ int i;
+
+ qty = of_property_count_strings(node, "clock-output-names");
+ if (qty < 0) {
+ pr_err("%s: error in clock-output-names %d\n", __func__, qty);
+ return;
+ }
+
+ if (qty == 0) {
+ pr_info("%s: nothing to do\n", __func__);
+ return;
+ }
+
+ reg = of_iomap(node, 0);
+
+ clk_data = kzalloc(sizeof(struct clk_onecell_data), GFP_KERNEL);
+ if (!clk_data)
+ return;
+
+ clk_data->clks = kzalloc(qty * sizeof(struct clk *), GFP_KERNEL);
+ if (!clk_data->clks) {
+ kfree(clk_data);
+ return;
+ }
+
+ flags = CLK_GATE_HIWORD_MASK | CLK_GATE_INVERTED;
+
+ for (i = 0; i < qty; i++) {
+ of_property_read_string_index(node, "clock-output-names",
+ i, &clk_name);
+
+ /* ignore empty slots */
+ if (!strcmp("reserved", clk_name))
+ continue;
+
+ clk_parent = of_clk_get_parent_name(node, i);
+
+ reg_idx = reg + 4 * (i / 16);
+ reg_bit = i % 16;
+
+ clk_data->clks[i] = clk_gate(clk_name, clk_parent, reg_idx,
+ reg_bit, clkflags, flags);
+ WARN_ON(IS_ERR(clk_data->clks[i]));
+ }
+
+ clk_data->clk_num = qty;
+
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+CLK_OF_DECLARE(rk2928_gate, "rockchip,rk2928-gate-clk", rk2928_gate_clk_init);
diff --git a/drivers/clk/socfpga.c b/drivers/clk/socfpga.c
index 33a55cbeeb..f4257fdb5c 100644
--- a/drivers/clk/socfpga.c
+++ b/drivers/clk/socfpga.c
@@ -49,25 +49,6 @@
static void __iomem *clk_mgr_base_addr;
-char *of_clk_get_parent_name(struct device_node *np, unsigned int index)
-{
- struct of_phandle_args clkspec;
- const char *clk_name;
- int rc;
-
- rc = of_parse_phandle_with_args(np, "clocks", "#clock-cells", index,
- &clkspec);
- if (rc)
- return NULL;
-
- if (of_property_read_string_index(clkspec.np, "clock-output-names",
- clkspec.args_count ? clkspec.args[0] : 0,
- &clk_name) < 0)
- clk_name = clkspec.np->name;
-
- return xstrdup(clk_name);
-}
-
static struct clk *socfpga_fixed_clk(struct device_node *node)
{
uint32_t f = 0;
diff --git a/drivers/clk/tegra/clk-periph.c b/drivers/clk/tegra/clk-periph.c
index c970f63afa..b7414dec0b 100644
--- a/drivers/clk/tegra/clk-periph.c
+++ b/drivers/clk/tegra/clk-periph.c
@@ -145,7 +145,7 @@ struct clk *_tegra_clk_register_periph(const char *name,
goto out_mux;
periph->gate = clk_gate_alloc(NULL, NULL, clk_base + 0x10 +
- ((id >> 3) & 0xc), id & 0x1f, 0);
+ ((id >> 3) & 0xc), id & 0x1f, 0, 0);
if (!periph->gate)
goto out_gate;
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 887376427c..3af904dd6d 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1,5 +1,9 @@
menu MFD
+config MFD_ACT8846
+ depends on I2C
+ bool "ACT8846 driver"
+
config MFD_LP3972
depends on I2C
bool "LP3972 driver"
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 2ad766da1b..49b9e35663 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -1,3 +1,4 @@
+obj-$(CONFIG_MFD_ACT8846) += act8846.o
obj-$(CONFIG_MFD_LP3972) += lp3972.o
obj-$(CONFIG_MFD_MC13XXX) += mc13xxx.o
obj-$(CONFIG_MFD_MC34704) += mc34704.o
diff --git a/drivers/mfd/act8846.c b/drivers/mfd/act8846.c
new file mode 100644
index 0000000000..60029acf76
--- /dev/null
+++ b/drivers/mfd/act8846.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2007 Sascha Hauer, Pengutronix
+ * 2009 Marc Kleine-Budde <mkl@pengutronix.de>
+ *
+ * Copied from drivers/mfd/mc9sdz60.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ *
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <xfuncs.h>
+#include <errno.h>
+
+#include <i2c/i2c.h>
+#include <mfd/act8846.h>
+
+#define DRIVERNAME "act8846"
+
+#define to_act8846(a) container_of(a, struct act8846, cdev)
+
+static struct act8846 *act8846_dev;
+
+struct act8846 *act8846_get(void)
+{
+ if (!act8846_dev)
+ return NULL;
+
+ return act8846_dev;
+}
+EXPORT_SYMBOL(act8846_get);
+
+int act8846_reg_read(struct act8846 *act8846, enum act8846_reg reg, u8 *val)
+{
+ int ret;
+
+ ret = i2c_read_reg(act8846->client, reg, val, 1);
+
+ return ret == 1 ? 0 : ret;
+}
+EXPORT_SYMBOL(act8846_reg_read);
+
+int act8846_reg_write(struct act8846 *act8846, enum act8846_reg reg, u8 val)
+{
+ int ret;
+
+ ret = i2c_write_reg(act8846->client, reg, &val, 1);
+
+ return ret == 1 ? 0 : ret;
+}
+EXPORT_SYMBOL(act8846_reg_write);
+
+int act8846_set_bits(struct act8846 *act8846, enum act8846_reg reg,
+ u8 mask, u8 val)
+{
+ u8 tmp;
+ int err;
+
+ err = act8846_reg_read(act8846, reg, &tmp);
+ tmp = (tmp & ~mask) | val;
+
+ if (!err)
+ err = act8846_reg_write(act8846, reg, tmp);
+
+ return err;
+}
+EXPORT_SYMBOL(act8846_set_bits);
+
+static ssize_t act8846_read(struct cdev *cdev, void *_buf, size_t count,
+ loff_t offset, ulong flags)
+{
+ struct act8846 *act8846 = to_act8846(cdev);
+ u8 *buf = _buf;
+ size_t i = count;
+ int err;
+
+ while (i) {
+ err = act8846_reg_read(act8846, offset, buf);
+ if (err)
+ return (ssize_t)err;
+ buf++;
+ i--;
+ offset++;
+ }
+
+ return count;
+}
+
+static ssize_t act8846_write(struct cdev *cdev, const void *_buf, size_t count,
+ loff_t offset, ulong flags)
+{
+ struct act8846 *act8846 = to_act8846(cdev);
+ const u8 *buf = _buf;
+ size_t i = count;
+ int err;
+
+ while (i) {
+ err = act8846_reg_write(act8846, offset, *buf);
+ if (err)
+ return (ssize_t)err;
+ buf++;
+ i--;
+ offset++;
+ }
+
+ return count;
+}
+
+static struct file_operations act8846_fops = {
+ .lseek = dev_lseek_default,
+ .read = act8846_read,
+ .write = act8846_write,
+};
+
+static int act8846_probe(struct device_d *dev)
+{
+ if (act8846_dev)
+ return -EBUSY;
+
+ act8846_dev = xzalloc(sizeof(struct act8846));
+ act8846_dev->cdev.name = DRIVERNAME;
+ act8846_dev->client = to_i2c_client(dev);
+ act8846_dev->cdev.size = 64;
+ act8846_dev->cdev.dev = dev;
+ act8846_dev->cdev.ops = &act8846_fops;
+
+ devfs_create(&act8846_dev->cdev);
+
+ return 0;
+}
+
+static struct driver_d act8846_driver = {
+ .name = DRIVERNAME,
+ .probe = act8846_probe,
+};
+
+static int act8846_init(void)
+{
+ i2c_driver_register(&act8846_driver);
+ return 0;
+}
+
+device_initcall(act8846_init);
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index fd9685988c..057abd2bca 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -32,6 +32,13 @@ config DRIVER_NET_AR231X
help
Support for the AR231x/531x ethernet controller
+config DRIVER_NET_ARC_EMAC
+ bool "ARC Ethernet MAC driver"
+ select PHYLIB
+ help
+ This option enables support for the ARC EMAC ethernet
+ controller.
+
config DRIVER_NET_AT91_ETHER
bool "at91 ethernet driver"
depends on HAS_AT91_ETHER
diff --git a/drivers/net/Makefile b/drivers/net/Makefile
index c1c4559937..65f0d8b387 100644
--- a/drivers/net/Makefile
+++ b/drivers/net/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_PHYLIB) += phy/
obj-$(CONFIG_NET_USB) += usb/
obj-$(CONFIG_DRIVER_NET_AR231X) += ar231x.o
+obj-$(CONFIG_DRIVER_NET_ARC_EMAC) += arc_emac.o
obj-$(CONFIG_DRIVER_NET_AT91_ETHER) += at91_ether.o
obj-$(CONFIG_DRIVER_NET_CALXEDA_XGMAC) += xgmac.o
obj-$(CONFIG_DRIVER_NET_CS8900) += cs8900.o
diff --git a/drivers/net/arc_emac.c b/drivers/net/arc_emac.c
new file mode 100644
index 0000000000..1f1e889199
--- /dev/null
+++ b/drivers/net/arc_emac.c
@@ -0,0 +1,469 @@
+/*
+ * Driver for ARC EMAC Ethernet controller
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * Based on Linux kernel driver, which is:
+ * Copyright (C) 2004-2013 Synopsys, Inc. (www.synopsys.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/mmu.h>
+#include <common.h>
+#include <net.h>
+#include <io.h>
+#include <init.h>
+
+/* ARC EMAC register set combines entries for MAC and MDIO */
+enum {
+ R_ID = 0,
+ R_STATUS,
+ R_ENABLE,
+ R_CTRL,
+ R_POLLRATE,
+ R_RXERR,
+ R_MISS,
+ R_TX_RING,
+ R_RX_RING,
+ R_ADDRL,
+ R_ADDRH,
+ R_LAFL,
+ R_LAFH,
+ R_MDIO,
+};
+
+/* STATUS and ENABLE Register bit masks */
+#define TXINT_MASK (1<<0) /* Transmit interrupt */
+#define RXINT_MASK (1<<1) /* Receive interrupt */
+#define ERR_MASK (1<<2) /* Error interrupt */
+#define TXCH_MASK (1<<3) /* Transmit chaining error interrupt */
+#define MSER_MASK (1<<4) /* Missed packet counter error */
+#define RXCR_MASK (1<<8) /* RXCRCERR counter rolled over */
+#define RXFR_MASK (1<<9) /* RXFRAMEERR counter rolled over */
+#define RXFL_MASK (1<<10) /* RXOFLOWERR counter rolled over */
+#define MDIO_MASK (1<<12) /* MDIO complete interrupt */
+#define TXPL_MASK (1<<31) /* Force polling of BD by EMAC */
+
+/* CONTROL Register bit masks */
+#define EN_MASK (1<<0) /* VMAC enable */
+#define TXRN_MASK (1<<3) /* TX enable */
+#define RXRN_MASK (1<<4) /* RX enable */
+#define DSBC_MASK (1<<8) /* Disable receive broadcast */
+#define ENFL_MASK (1<<10) /* Enable Full-duplex */
+#define PROM_MASK (1<<11) /* Promiscuous mode */
+
+/* Buffer descriptor INFO bit masks */
+#define OWN_MASK (1<<31) /* 0-CPU owns buffer, 1-EMAC owns buffer */
+#define FIRST_MASK (1<<16) /* First buffer in chain */
+#define LAST_MASK (1<<17) /* Last buffer in chain */
+#define LEN_MASK 0x000007FF /* last 11 bits */
+#define CRLS (1<<21)
+#define DEFR (1<<22)
+#define DROP (1<<23)
+#define RTRY (1<<24)
+#define LTCL (1<<28)
+#define UFLO (1<<29)
+
+#define FIRST_OR_LAST_MASK (FIRST_MASK | LAST_MASK)
+
+#define FOR_EMAC OWN_MASK
+#define FOR_CPU 0
+
+#define EMAC_ZLEN 64
+
+/**
+ * struct arc_emac_priv - Storage of EMAC's private information.
+ * @bus: Pointer to the current MII bus.
+ * @regs: Base address of EMAC memory-mapped control registers.
+ * @rxbd: Pointer to Rx BD ring.
+ * @txbd: Pointer to Tx BD ring.
+ * @rxbuf: Buffers for received packets.
+ * @txbd_curr: Index of Tx BD to use on the next "ndo_start_xmit".
+ * @last_rx_bd: Index of the last Rx BD we've got from EMAC.
+ */
+struct arc_emac_priv {
+ struct mii_bus *bus;
+ void __iomem *regs;
+ struct arc_emac_bd *rxbd;
+ struct arc_emac_bd *txbd;
+ u8 *rxbuf;
+ unsigned int txbd_curr;
+ unsigned int last_rx_bd;
+};
+
+/**
+ * struct arc_emac_bd - EMAC buffer descriptor (BD).
+ *
+ * @info: Contains status information on the buffer itself.
+ * @data: 32-bit byte addressable pointer to the packet data.
+ */
+struct arc_emac_bd {
+ __le32 info;
+ dma_addr_t data;
+};
+
+/* Number of Rx/Tx BD's */
+#define RX_BD_NUM 16
+#define TX_BD_NUM 1
+
+#define RX_RING_SZ (RX_BD_NUM * sizeof(struct arc_emac_bd))
+#define TX_RING_SZ (TX_BD_NUM * sizeof(struct arc_emac_bd))
+
+/**
+ * arc_reg_set - Sets EMAC register with provided value.
+ * @priv: Pointer to ARC EMAC private data structure.
+ * @reg: Register offset from base address.
+ * @value: Value to set in register.
+ */
+static inline void arc_reg_set(struct arc_emac_priv *priv, int reg, int value)
+{
+ writel(value, priv->regs + reg * sizeof(int));
+}
+
+/**
+ * arc_reg_get - Gets value of specified EMAC register.
+ * @priv: Pointer to ARC EMAC private data structure.
+ * @reg: Register offset from base address.
+ *
+ * returns: Value of requested register.
+ */
+static inline unsigned int arc_reg_get(struct arc_emac_priv *priv, int reg)
+{
+ return readl(priv->regs + reg * sizeof(int));
+}
+
+/**
+ * arc_reg_or - Applies mask to specified EMAC register - ("reg" | "mask").
+ * @priv: Pointer to ARC EMAC private data structure.
+ * @reg: Register offset from base address.
+ * @mask: Mask to apply to specified register.
+ *
+ * This function reads initial register value, then applies provided mask
+ * to it and then writes register back.
+ */
+static inline void arc_reg_or(struct arc_emac_priv *priv, int reg, int mask)
+{
+ unsigned int value = arc_reg_get(priv, reg);
+ arc_reg_set(priv, reg, value | mask);
+}
+
+/**
+ * arc_reg_clr - Applies mask to specified EMAC register - ("reg" & ~"mask").
+ * @priv: Pointer to ARC EMAC private data structure.
+ * @reg: Register offset from base address.
+ * @mask: Mask to apply to specified register.
+ *
+ * This function reads initial register value, then applies provided mask
+ * to it and then writes register back.
+ */
+static inline void arc_reg_clr(struct arc_emac_priv *priv, int reg, int mask)
+{
+ unsigned int value = arc_reg_get(priv, reg);
+ arc_reg_set(priv, reg, value & ~mask);
+}
+
+static int arc_emac_init(struct eth_device *edev)
+{
+ return 0;
+}
+
+static int arc_emac_open(struct eth_device *edev)
+{
+ struct arc_emac_priv *priv = edev->priv;
+ void *rxbuf;
+ int ret, i;
+
+ priv->last_rx_bd = 0;
+ rxbuf = priv->rxbuf;
+
+ /* Allocate and set buffers for Rx BD's */
+ for (i = 0; i < RX_BD_NUM; i++) {
+ unsigned int *last_rx_bd = &priv->last_rx_bd;
+ struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd];
+
+ rxbd->data = cpu_to_le32(rxbuf);
+
+ /* Return ownership to EMAC */
+ rxbd->info = cpu_to_le32(FOR_EMAC | PKTSIZE);
+
+ *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM;
+ rxbuf += PKTSIZE;
+ }
+
+ /* Clean Tx BD's */
+ memset(priv->txbd, 0, TX_RING_SZ);
+
+ /* Initialize logical address filter */
+ arc_reg_set(priv, R_LAFL, 0x0);
+ arc_reg_set(priv, R_LAFH, 0x0);
+
+ /* Set BD ring pointers for device side */
+ arc_reg_set(priv, R_RX_RING, (unsigned int)priv->rxbd);
+ arc_reg_set(priv, R_TX_RING, (unsigned int)priv->txbd);
+
+ /* Enable interrupts */
+ arc_reg_set(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
+
+ /* Set CONTROL */
+ arc_reg_set(priv, R_CTRL,
+ (RX_BD_NUM << 24) | /* RX BD table length */
+ (TX_BD_NUM << 16) | /* TX BD table length */
+ TXRN_MASK | RXRN_MASK);
+
+ /* Enable EMAC */
+ arc_reg_or(priv, R_CTRL, EN_MASK);
+
+ ret = phy_device_connect(edev, priv->bus, -1, NULL, 0,
+ PHY_INTERFACE_MODE_NA);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int arc_emac_send(struct eth_device *edev, void *data, int length)
+{
+ struct arc_emac_priv *priv = edev->priv;
+ struct arc_emac_bd *bd = &priv->txbd[priv->txbd_curr];
+ char txbuf[EMAC_ZLEN];
+ int ret;
+
+ /* Pad short frames to minimum length */
+ if (length < EMAC_ZLEN) {
+ memcpy(txbuf, data, length);
+ memset(txbuf + length, 0, EMAC_ZLEN - length);
+ data = txbuf;
+ length = EMAC_ZLEN;
+ }
+
+ dma_flush_range((unsigned long)data, (unsigned long)data + length);
+
+ bd->data = cpu_to_le32(data);
+ bd->info = cpu_to_le32(FOR_EMAC | FIRST_OR_LAST_MASK | length);
+ arc_reg_set(priv, R_STATUS, TXPL_MASK);
+
+ ret = wait_on_timeout(20 * MSECOND,
+ (arc_reg_get(priv, R_STATUS) & TXINT_MASK) != 0);
+
+ if (ret) {
+ dev_err(&edev->dev, "transmit timeout\n");
+ return ret;
+ }
+
+ arc_reg_set(priv, R_STATUS, TXINT_MASK);
+
+ priv->txbd_curr++;
+ priv->txbd_curr %= TX_BD_NUM;
+
+ return 0;
+}
+
+static int arc_emac_recv(struct eth_device *edev)
+{
+ struct arc_emac_priv *priv = edev->priv;
+ unsigned int work_done;
+
+ for (work_done = 0; work_done < RX_BD_NUM; work_done++) {
+ unsigned int *last_rx_bd = &priv->last_rx_bd;
+ struct arc_emac_bd *rxbd = &priv->rxbd[*last_rx_bd];
+ unsigned int pktlen, info = le32_to_cpu(rxbd->info);
+
+ if (unlikely((info & OWN_MASK) == FOR_EMAC))
+ break;
+
+ /*
+ * Make a note that we saw a packet at this BD.
+ * So next time, driver starts from this + 1
+ */
+ *last_rx_bd = (*last_rx_bd + 1) % RX_BD_NUM;
+
+ if (unlikely((info & FIRST_OR_LAST_MASK) !=
+ FIRST_OR_LAST_MASK)) {
+ /*
+ * We pre-allocate buffers of MTU size so incoming
+ * packets won't be split/chained.
+ */
+ printk(KERN_DEBUG "incomplete packet received\n");
+
+ /* Return ownership to EMAC */
+ rxbd->info = cpu_to_le32(FOR_EMAC | PKTSIZE);
+ continue;
+ }
+
+ pktlen = info & LEN_MASK;
+
+ /* invalidate current receive buffer */
+ dma_inv_range((unsigned long)rxbd->data,
+ (unsigned long)rxbd->data + pktlen);
+
+ net_receive((unsigned char *)rxbd->data, pktlen);
+
+ rxbd->info = cpu_to_le32(FOR_EMAC | PKTSIZE);
+ }
+
+ return work_done;
+}
+
+static void arc_emac_halt(struct eth_device *edev)
+{
+ struct arc_emac_priv *priv = edev->priv;
+
+ /* Disable interrupts */
+ arc_reg_clr(priv, R_ENABLE, RXINT_MASK | ERR_MASK);
+
+ /* Disable EMAC */
+ arc_reg_clr(priv, R_CTRL, EN_MASK);
+}
+
+static int arc_emac_get_ethaddr(struct eth_device *edev, unsigned char *mac)
+{
+ return -1;
+}
+
+static int arc_emac_set_ethaddr(struct eth_device *edev, unsigned char *mac)
+{
+ struct arc_emac_priv *priv = edev->priv;
+ unsigned int addr_low, addr_hi;
+
+ addr_low = le32_to_cpu(*(__le32 *) &mac[0]);
+ addr_hi = le16_to_cpu(*(__le16 *) &mac[4]);
+
+ arc_reg_set(priv, R_ADDRL, addr_low);
+ arc_reg_set(priv, R_ADDRH, addr_hi);
+
+ return 0;
+}
+
+/* Number of seconds we wait for "MDIO complete" flag to appear */
+#define ARC_MDIO_COMPLETE_POLL_COUNT 1
+
+static int arc_mdio_complete_wait(struct arc_emac_priv *priv)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARC_MDIO_COMPLETE_POLL_COUNT * 40; i++) {
+ unsigned int status = arc_reg_get(priv, R_STATUS);
+
+ status &= MDIO_MASK;
+
+ if (status) {
+ /* Reset "MDIO complete" flag */
+ arc_reg_set(priv, R_STATUS, status);
+ return 0;
+ }
+
+ mdelay(25);
+ }
+ return -ETIMEDOUT;
+}
+
+static int arc_emac_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num)
+{
+ struct arc_emac_priv *priv = bus->priv;
+ int error;
+
+ arc_reg_set(priv, R_MDIO,
+ 0x60020000 | (phy_addr << 23) | (reg_num << 18));
+
+ error = arc_mdio_complete_wait(priv);
+ if (error < 0)
+ return error;
+
+ return arc_reg_get(priv, R_MDIO) & 0xffff;
+}
+
+static int arc_emac_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num,
+ u16 value)
+{
+ struct arc_emac_priv *priv = bus->priv;
+
+ arc_reg_set(priv, R_MDIO,
+ 0x50020000 | (phy_addr << 23) | (reg_num << 18) | value);
+
+ return arc_mdio_complete_wait(priv);
+}
+
+static int arc_emac_probe(struct device_d *dev)
+{
+ struct eth_device *edev;
+ struct arc_emac_priv *priv;
+ unsigned int clock_frequency;
+ struct mii_bus *miibus;
+ u32 id;
+
+ /* Get CPU clock frequency from device tree */
+ if (of_property_read_u32(dev->device_node, "clock-frequency",
+ &clock_frequency)) {
+ dev_err(dev, "failed to retrieve <clock-frequency> from device tree\n");
+ return -EINVAL;
+ }
+
+ edev = xzalloc(sizeof(struct eth_device) +
+ sizeof(struct arc_emac_priv));
+ edev->priv = (struct arc_emac_priv *)(edev + 1);
+ miibus = xzalloc(sizeof(struct mii_bus));
+
+ priv = edev->priv;
+ priv->regs = dev_request_mem_region(dev, 0);
+ priv->bus = miibus;
+
+ id = arc_reg_get(priv, R_ID);
+ /* Check for EMAC revision 5 or 7, magic number */
+ if (!(id == 0x0005fd02 || id == 0x0007fd02)) {
+ dev_err(dev, "ARC EMAC not detected, id=0x%x\n", id);
+ free(edev);
+ free(miibus);
+ return -ENODEV;
+ }
+ dev_info(dev, "ARC EMAC detected with id: 0x%x\n", id);
+
+ edev->init = arc_emac_init;
+ edev->open = arc_emac_open;
+ edev->send = arc_emac_send;
+ edev->recv = arc_emac_recv;
+ edev->halt = arc_emac_halt;
+ edev->get_ethaddr = arc_emac_get_ethaddr;
+ edev->set_ethaddr = arc_emac_set_ethaddr;
+ edev->parent = dev;
+
+ miibus->read = arc_emac_mdio_read;
+ miibus->write = arc_emac_mdio_write;
+ miibus->priv = priv;
+ miibus->parent = dev;
+
+ /* allocate rx/tx descriptors */
+ priv->rxbd = dma_alloc_coherent(RX_BD_NUM * sizeof(struct arc_emac_bd));
+ priv->txbd = dma_alloc_coherent(TX_BD_NUM * sizeof(struct arc_emac_bd));
+ priv->rxbuf = dma_alloc(RX_BD_NUM * PKTSIZE);
+
+ /* Set poll rate so that it polls every 1 ms */
+ arc_reg_set(priv, R_POLLRATE, clock_frequency / 1000000);
+
+ mdiobus_register(miibus);
+ eth_register(edev);
+
+ return 0;
+}
+
+static __maybe_unused struct of_device_id arc_emac_dt_ids[] = {
+ {
+ .compatible = "snps,arc-emac",
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d arc_emac_driver = {
+ .name = "arc-emac",
+ .probe = arc_emac_probe,
+ .of_compatible = DRV_OF_COMPAT(arc_emac_dt_ids),
+};
+device_platform_driver(arc_emac_driver);
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 7390971ea3..906e33af8f 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -24,6 +24,13 @@ config PINCTRL_IMX_IOMUX_V3
help
This iomux controller is found on i.MX25,35,51,53,6.
+config PINCTRL_ROCKCHIP
+ select PINCTRL
+ select GPIO_GENERIC
+ bool
+ help
+ The pinmux controller found on Rockchip SoCs.
+
config PINCTRL_SINGLE
select PINCTRL
bool "pinctrl single"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index b3b0fa9c58..9a5d0ba97e 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -2,5 +2,6 @@ obj-$(CONFIG_PINCTRL) += pinctrl.o
obj-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o
obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o
obj-$(CONFIG_PINCTRL_IMX_IOMUX_V3) += imx-iomux-v3.o
+obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
new file mode 100644
index 0000000000..a71fed3259
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -0,0 +1,560 @@
+/*
+ * Rockchip pinctrl and gpio driver for Barebox
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
+ *
+ * Based on Linux pinctrl-rockchip:
+ * Copyright (C) 2013 MundoReader S.L.
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ * Copyright (C) 2012 Linaro Ltd
+ * Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <gpio.h>
+#include <init.h>
+#include <malloc.h>
+#include <of.h>
+#include <of_address.h>
+#include <pinctrl.h>
+
+#include <linux/basic_mmio_gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+enum rockchip_pinctrl_type {
+ RK2928,
+ RK3066B,
+ RK3188,
+};
+
+enum rockchip_pin_bank_type {
+ COMMON_BANK,
+ RK3188_BANK0,
+};
+
+struct rockchip_pin_bank {
+ void __iomem *reg_base;
+ void __iomem *reg_pull;
+ struct clk *clk;
+ u32 pin_base;
+ u8 nr_pins;
+ char *name;
+ u8 bank_num;
+ enum rockchip_pin_bank_type bank_type;
+ bool valid;
+ struct device_node *of_node;
+ struct rockchip_pinctrl *drvdata;
+ struct bgpio_chip bgpio_chip;
+};
+
+#define PIN_BANK(id, pins, label) \
+ { \
+ .bank_num = id, \
+ .nr_pins = pins, \
+ .name = label, \
+ }
+
+struct rockchip_pin_ctrl {
+ struct rockchip_pin_bank *pin_banks;
+ u32 nr_banks;
+ u32 nr_pins;
+ char *label;
+ enum rockchip_pinctrl_type type;
+ int mux_offset;
+ void (*pull_calc_reg)(struct rockchip_pin_bank *bank, int pin_num,
+ void __iomem **reg, u8 *bit);
+};
+
+struct rockchip_pinctrl {
+ void __iomem *reg_base;
+ void __iomem *reg_pull;
+ struct pinctrl_device pctl_dev;
+ struct rockchip_pin_ctrl *ctrl;
+};
+
+enum {
+ RK_BIAS_DISABLE = 0,
+ RK_BIAS_PULL_UP,
+ RK_BIAS_PULL_DOWN,
+ RK_BIAS_BUS_HOLD,
+};
+
+/* GPIO registers */
+enum {
+ RK_GPIO_SWPORT_DR = 0x00,
+ RK_GPIO_SWPORT_DDR = 0x04,
+ RK_GPIO_EXT_PORT = 0x50,
+};
+
+static int rockchip_gpiolib_register(struct device_d *dev,
+ struct rockchip_pinctrl *info)
+{
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ struct rockchip_pin_bank *bank = ctrl->pin_banks;
+ void __iomem *reg_base;
+ int ret;
+ int i;
+
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ if (!bank->valid) {
+ dev_warn(dev, "bank %s is not valid\n", bank->name);
+ continue;
+ }
+
+ reg_base = bank->reg_base;
+
+ ret = bgpio_init(&bank->bgpio_chip, dev, 4,
+ reg_base + RK_GPIO_EXT_PORT,
+ reg_base + RK_GPIO_SWPORT_DR, NULL,
+ reg_base + RK_GPIO_SWPORT_DDR, NULL, 0);
+ if (ret)
+ goto fail;
+
+ bank->bgpio_chip.gc.ngpio = bank->nr_pins;
+ ret = gpiochip_add(&bank->bgpio_chip.gc);
+ if (ret) {
+ dev_err(dev, "failed to register gpio_chip %s, error code: %d\n",
+ bank->name, ret);
+ goto fail;
+ }
+
+ }
+
+ return 0;
+fail:
+ for (--i, --bank; i >= 0; --i, --bank) {
+ if (!bank->valid)
+ continue;
+
+ gpiochip_remove(&bank->bgpio_chip.gc);
+ }
+ return ret;
+}
+
+static struct rockchip_pinctrl *to_rockchip_pinctrl(struct pinctrl_device *pdev)
+{
+ return container_of(pdev, struct rockchip_pinctrl, pctl_dev);
+}
+
+static struct rockchip_pin_bank *bank_num_to_bank(struct rockchip_pinctrl *info,
+ unsigned num)
+{
+ struct rockchip_pin_bank *b = info->ctrl->pin_banks;
+ int i;
+
+ for (i = 0; i < info->ctrl->nr_banks; i++, b++) {
+ if (b->bank_num == num)
+ return b;
+ }
+
+ return ERR_PTR(-EINVAL);
+}
+
+static int parse_bias_config(struct device_node *np)
+{
+ u32 val;
+
+ if (of_property_read_u32(np, "bias-pull-up", &val) != -EINVAL)
+ return RK_BIAS_PULL_UP;
+ else if (of_property_read_u32(np, "bias-pull-down", &val) != -EINVAL)
+ return RK_BIAS_PULL_DOWN;
+ else if (of_property_read_u32(np, "bias-bus-hold", &val) != -EINVAL)
+ return RK_BIAS_BUS_HOLD;
+ else
+ return RK_BIAS_DISABLE;
+}
+
+
+#define RK2928_PULL_OFFSET 0x118
+#define RK2928_PULL_PINS_PER_REG 16
+#define RK2928_PULL_BANK_STRIDE 8
+
+static void rk2928_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, void __iomem **reg,
+ u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ *reg = info->reg_base + RK2928_PULL_OFFSET;
+ *reg += bank->bank_num * RK2928_PULL_BANK_STRIDE;
+ *reg += (pin_num / RK2928_PULL_PINS_PER_REG) * 4;
+
+ *bit = pin_num % RK2928_PULL_PINS_PER_REG;
+};
+
+#define RK3188_PULL_BITS_PER_PIN 2
+#define RK3188_PULL_PINS_PER_REG 8
+#define RK3188_PULL_BANK_STRIDE 16
+
+static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+ int pin_num, void __iomem **reg,
+ u8 *bit)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+
+ /* The first 12 pins of the first bank are located elsewhere */
+ if (bank->bank_type == RK3188_BANK0 && pin_num < 12) {
+ *reg = bank->reg_pull +
+ ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+ *bit = pin_num % RK3188_PULL_PINS_PER_REG;
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ } else {
+ *reg = info->reg_pull - 4;
+ *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+ *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+ /*
+ * The bits in these registers have an inverse ordering
+ * with the lowest pin being in bits 15:14 and the highest
+ * pin in bits 1:0
+ */
+ *bit = 7 - (pin_num % RK3188_PULL_PINS_PER_REG);
+ *bit *= RK3188_PULL_BITS_PER_PIN;
+ }
+}
+
+static int rockchip_pinctrl_set_func(struct rockchip_pin_bank *bank, int pin,
+ int mux)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ void __iomem *reg = info->reg_base + info->ctrl->mux_offset;
+ u8 bit;
+ u32 data;
+
+ /* get basic quadruple of mux registers and the correct reg inside */
+ reg += bank->bank_num * 0x10;
+ reg += (pin / 8) * 4;
+ bit = (pin % 8) * 2;
+
+ data = 3 << (bit + 16);
+ data |= (mux & 3) << bit;
+ writel(data, reg);
+
+ return 0;
+}
+
+static int rockchip_pinctrl_set_pull(struct rockchip_pin_bank *bank,
+ int pin_num, int pull)
+{
+ struct rockchip_pinctrl *info = bank->drvdata;
+ struct rockchip_pin_ctrl *ctrl = info->ctrl;
+ void __iomem *reg;
+ u8 bit;
+ u32 data;
+
+ dev_dbg(info->pctl_dev.dev, "setting pull of GPIO%d-%d to %d\n",
+ bank->bank_num, pin_num, pull);
+
+ /* rk3066b doesn't support any pulls */
+ if (ctrl->type == RK3066B)
+ return pull ? -EINVAL : 0;
+
+ ctrl->pull_calc_reg(bank, pin_num, &reg, &bit);
+
+ switch (ctrl->type) {
+ case RK2928:
+ data = BIT(bit + 16);
+ if (pull == RK_BIAS_DISABLE)
+ data |= BIT(bit);
+ writel(data, reg);
+ break;
+ case RK3188:
+ data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16);
+ data |= pull << bit;
+ writel(data, reg);
+ break;
+ default:
+ dev_err(info->pctl_dev.dev, "unsupported pinctrl type\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rockchip_pinctrl_set_state(struct pinctrl_device *pdev,
+ struct device_node *np)
+{
+ struct rockchip_pinctrl *info = to_rockchip_pinctrl(pdev);
+ const __be32 *list;
+ int i, size;
+ int bank_num, pin_num, func;
+
+ /*
+ * the binding format is rockchip,pins = <bank pin mux CONFIG>,
+ * do sanity check and calculate pins number
+ */
+ list = of_get_property(np, "rockchip,pins", &size);
+ size /= sizeof(*list);
+
+ if (!size || size % 4) {
+ dev_err(pdev->dev, "wrong pins number or pins and configs should be by 4\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < size; i += 4) {
+ const __be32 *phandle;
+ struct device_node *np_config;
+ struct rockchip_pin_bank *bank;
+
+ bank_num = be32_to_cpu(*list++);
+ pin_num = be32_to_cpu(*list++);
+ func = be32_to_cpu(*list++);
+ phandle = list++;
+
+ if (!phandle)
+ return -EINVAL;
+
+ np_config = of_find_node_by_phandle(be32_to_cpup(phandle));
+ bank = bank_num_to_bank(info, bank_num);
+ rockchip_pinctrl_set_func(bank, pin_num, func);
+ rockchip_pinctrl_set_pull(bank, pin_num,
+ parse_bias_config(np_config));
+ }
+
+ return 0;
+}
+
+static struct pinctrl_ops rockchip_pinctrl_ops = {
+ .set_state = rockchip_pinctrl_set_state,
+};
+
+static int rockchip_get_bank_data(struct rockchip_pin_bank *bank,
+ struct device_d *dev)
+{
+ struct resource node_res, *res;
+
+ if (of_address_to_resource(bank->of_node, 0, &node_res)) {
+ dev_err(dev, "cannot find IO resource for bank\n");
+ return -ENOENT;
+ }
+
+ res = request_iomem_region(dev_name(dev), node_res.start, node_res.end);
+ if (!res) {
+ dev_err(dev, "cannot request iomem region %08x\n",
+ node_res.start);
+ return -ENOENT;
+ }
+
+ bank->reg_base = (void __iomem *)res->start;
+
+ /*
+ * special case, where parts of the pull setting-registers are
+ * part of the PMU register space
+ */
+ if (of_device_is_compatible(bank->of_node,
+ "rockchip,rk3188-gpio-bank0")) {
+ bank->bank_type = RK3188_BANK0;
+
+ if (of_address_to_resource(bank->of_node, 1, &node_res)) {
+ dev_err(dev, "cannot find IO resource for bank\n");
+ return -ENOENT;
+ }
+
+ res = request_iomem_region(dev_name(dev), node_res.start,
+ node_res.end);
+ if (!res) {
+ dev_err(dev, "cannot request iomem region %08x\n",
+ node_res.start);
+ return -ENOENT;
+ }
+
+ bank->reg_pull = (void __iomem *)res->start;
+ } else {
+ bank->bank_type = COMMON_BANK;
+ }
+
+ bank->clk = of_clk_get(bank->of_node, 0);
+ if (IS_ERR(bank->clk))
+ return PTR_ERR(bank->clk);
+
+ return clk_enable(bank->clk);
+}
+
+static struct of_device_id rockchip_pinctrl_dt_match[];
+
+static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
+ struct rockchip_pinctrl *d, struct device_d *dev)
+{
+ const struct of_device_id *match;
+ struct device_node *node = dev->device_node;
+ struct device_node *np;
+ struct rockchip_pin_ctrl *ctrl;
+ struct rockchip_pin_bank *bank;
+ char *name;
+ int i;
+
+ match = of_match_node(rockchip_pinctrl_dt_match, node);
+ ctrl = (struct rockchip_pin_ctrl *)match->data;
+
+ for_each_child_of_node(node, np) {
+ if (!of_find_property(np, "gpio-controller", NULL))
+ continue;
+
+ bank = ctrl->pin_banks;
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ name = bank->name;
+ if (!strncmp(name, np->name, strlen(name))) {
+ bank->of_node = np;
+ if (!rockchip_get_bank_data(bank, dev))
+ bank->valid = true;
+
+ break;
+ }
+ }
+ }
+
+ bank = ctrl->pin_banks;
+ for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+ bank->drvdata = d;
+ bank->pin_base = ctrl->nr_pins;
+ ctrl->nr_pins += bank->nr_pins;
+ }
+
+ return ctrl;
+}
+
+static int rockchip_pinctrl_probe(struct device_d *dev)
+{
+ struct rockchip_pinctrl *info;
+ struct rockchip_pin_ctrl *ctrl;
+ int ret;
+
+ info = xzalloc(sizeof(struct rockchip_pinctrl));
+ if (!info)
+ return -ENOMEM;
+
+ ctrl = rockchip_pinctrl_get_soc_data(info, dev);
+ if (!ctrl) {
+ dev_err(dev, "driver data not available\n");
+ return -EINVAL;
+ }
+ info->ctrl = ctrl;
+
+ info->reg_base = dev_request_mem_region(dev, 0);
+ if (!info->reg_base) {
+ dev_err(dev, "Could not get reg_base region\n");
+ return -ENODEV;
+ }
+
+ /* The RK3188 has its pull registers in a separate place */
+ if (ctrl->type == RK3188) {
+ info->reg_pull = dev_request_mem_region(dev, 1);
+ if (!info->reg_pull) {
+ dev_err(dev, "Could not get reg_pull region\n");
+ return -ENODEV;
+ }
+ }
+
+ info->pctl_dev.dev = dev;
+ info->pctl_dev.ops = &rockchip_pinctrl_ops;
+
+ ret = rockchip_gpiolib_register(dev, info);
+ if (ret)
+ return ret;
+
+ ret = pinctrl_register(&info->pctl_dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static struct rockchip_pin_bank rk2928_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk2928_pin_ctrl = {
+ .pin_banks = rk2928_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk2928_pin_banks),
+ .type = RK2928,
+ .mux_offset = 0xa8,
+ .pull_calc_reg = rk2928_calc_pull_reg_and_bit,
+};
+
+static struct rockchip_pin_bank rk3066a_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+ PIN_BANK(4, 32, "gpio4"),
+ PIN_BANK(6, 16, "gpio6"),
+};
+
+static struct rockchip_pin_ctrl rk3066a_pin_ctrl = {
+ .pin_banks = rk3066a_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3066a_pin_banks),
+ .type = RK2928,
+ .mux_offset = 0xa8,
+ .pull_calc_reg = rk2928_calc_pull_reg_and_bit,
+};
+
+static struct rockchip_pin_bank rk3066b_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk3066b_pin_ctrl = {
+ .pin_banks = rk3066b_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3066b_pin_banks),
+ .type = RK3066B,
+ .mux_offset = 0x60,
+};
+
+static struct rockchip_pin_bank rk3188_pin_banks[] = {
+ PIN_BANK(0, 32, "gpio0"),
+ PIN_BANK(1, 32, "gpio1"),
+ PIN_BANK(2, 32, "gpio2"),
+ PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk3188_pin_ctrl = {
+ .pin_banks = rk3188_pin_banks,
+ .nr_banks = ARRAY_SIZE(rk3188_pin_banks),
+ .type = RK3188,
+ .mux_offset = 0x60,
+ .pull_calc_reg = rk3188_calc_pull_reg_and_bit,
+};
+
+static struct of_device_id rockchip_pinctrl_dt_match[] = {
+ {
+ .compatible = "rockchip,rk2928-pinctrl",
+ .data = (long)&rk2928_pin_ctrl,
+ },
+ {
+ .compatible = "rockchip,rk3066a-pinctrl",
+ .data = (long)&rk3066a_pin_ctrl,
+ },
+ {
+ .compatible = "rockchip,rk3066b-pinctrl",
+ .data = (long)&rk3066b_pin_ctrl,
+ },
+ {
+ .compatible = "rockchip,rk3188-pinctrl",
+ .data = (long)&rk3188_pin_ctrl,
+ }, {
+ /* sentinel */
+ }
+};
+
+static struct driver_d rockchip_pinctrl_driver = {
+ .name = "rockchip-pinctrl",
+ .probe = rockchip_pinctrl_probe,
+ .of_compatible = DRV_OF_COMPAT(rockchip_pinctrl_dt_match),
+};
+
+console_platform_driver(rockchip_pinctrl_driver);