summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-vt8500.c510
-rw-r--r--drivers/clk/mxs/clk-imx23.c55
-rw-r--r--drivers/clk/mxs/clk-imx28.c113
-rw-r--r--drivers/gpio/Kconfig6
-rw-r--r--drivers/gpio/Makefile1
-rw-r--r--drivers/gpio/gpio-pxa.c77
-rw-r--r--drivers/gpio/gpio-samsung.c63
-rw-r--r--drivers/gpio/gpio-twl4030.c77
-rw-r--r--drivers/gpio/gpio-vt8500.c316
-rw-r--r--drivers/mtd/nand/pxa3xx_nand.c87
-rw-r--r--drivers/pinctrl/pinctrl-sirf.c58
-rw-r--r--drivers/rtc/rtc-ab8500.c6
-rw-r--r--drivers/rtc/rtc-pxa.c11
-rw-r--r--drivers/rtc/rtc-vt8500.c9
-rw-r--r--drivers/tty/serial/vt8500_serial.c58
-rw-r--r--drivers/video/Kconfig6
-rw-r--r--drivers/video/vt8500lcdfb.c79
-rw-r--r--drivers/video/wm8505fb.c97
-rw-r--r--drivers/video/wmt_ge_rops.c9
20 files changed, 1354 insertions, 285 deletions
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 2b861625bdae..71a25b91de00 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_ARCH_U8500) += ux500/
+obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
diff --git a/drivers/clk/clk-vt8500.c b/drivers/clk/clk-vt8500.c
new file mode 100644
index 000000000000..a885600f5270
--- /dev/null
+++ b/drivers/clk/clk-vt8500.c
@@ -0,0 +1,510 @@
+/*
+ * Clock implementation for VIA/Wondermedia SoC's
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/io.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/bitops.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+
+/* All clocks share the same lock as none can be changed concurrently */
+static DEFINE_SPINLOCK(_lock);
+
+struct clk_device {
+ struct clk_hw hw;
+ void __iomem *div_reg;
+ unsigned int div_mask;
+ void __iomem *en_reg;
+ int en_bit;
+ spinlock_t *lock;
+};
+
+/*
+ * Add new PLL_TYPE_x definitions here as required. Use the first known model
+ * to support the new type as the name.
+ * Add case statements to vtwm_pll_recalc_rate(), vtwm_pll_round_round() and
+ * vtwm_pll_set_rate() to handle the new PLL_TYPE_x
+ */
+
+#define PLL_TYPE_VT8500 0
+#define PLL_TYPE_WM8650 1
+
+struct clk_pll {
+ struct clk_hw hw;
+ void __iomem *reg;
+ spinlock_t *lock;
+ int type;
+};
+
+static void __iomem *pmc_base;
+
+#define to_clk_device(_hw) container_of(_hw, struct clk_device, hw)
+
+#define VT8500_PMC_BUSY_MASK 0x18
+
+static void vt8500_pmc_wait_busy(void)
+{
+ while (readl(pmc_base) & VT8500_PMC_BUSY_MASK)
+ cpu_relax();
+}
+
+static int vt8500_dclk_enable(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ en_val = readl(cdev->en_reg);
+ en_val |= BIT(cdev->en_bit);
+ writel(en_val, cdev->en_reg);
+
+ spin_unlock_irqrestore(cdev->lock, flags);
+ return 0;
+}
+
+static void vt8500_dclk_disable(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ en_val = readl(cdev->en_reg);
+ en_val &= ~BIT(cdev->en_bit);
+ writel(en_val, cdev->en_reg);
+
+ spin_unlock_irqrestore(cdev->lock, flags);
+}
+
+static int vt8500_dclk_is_enabled(struct clk_hw *hw)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 en_val = (readl(cdev->en_reg) & BIT(cdev->en_bit));
+
+ return en_val ? 1 : 0;
+}
+
+static unsigned long vt8500_dclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 div = readl(cdev->div_reg) & cdev->div_mask;
+
+ /* Special case for SDMMC devices */
+ if ((cdev->div_mask == 0x3F) && (div & BIT(5)))
+ div = 64 * (div & 0x1f);
+
+ /* div == 0 is actually the highest divisor */
+ if (div == 0)
+ div = (cdev->div_mask + 1);
+
+ return parent_rate / div;
+}
+
+static long vt8500_dclk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ u32 divisor = *prate / rate;
+
+ return *prate / divisor;
+}
+
+static int vt8500_dclk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_device *cdev = to_clk_device(hw);
+ u32 divisor = parent_rate / rate;
+ unsigned long flags = 0;
+
+ if (divisor == cdev->div_mask + 1)
+ divisor = 0;
+
+ if (divisor > cdev->div_mask) {
+ pr_err("%s: invalid divisor for clock\n", __func__);
+ return -EINVAL;
+ }
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ vt8500_pmc_wait_busy();
+ writel(divisor, cdev->div_reg);
+ vt8500_pmc_wait_busy();
+
+ spin_lock_irqsave(cdev->lock, flags);
+
+ return 0;
+}
+
+
+static const struct clk_ops vt8500_gated_clk_ops = {
+ .enable = vt8500_dclk_enable,
+ .disable = vt8500_dclk_disable,
+ .is_enabled = vt8500_dclk_is_enabled,
+};
+
+static const struct clk_ops vt8500_divisor_clk_ops = {
+ .round_rate = vt8500_dclk_round_rate,
+ .set_rate = vt8500_dclk_set_rate,
+ .recalc_rate = vt8500_dclk_recalc_rate,
+};
+
+static const struct clk_ops vt8500_gated_divisor_clk_ops = {
+ .enable = vt8500_dclk_enable,
+ .disable = vt8500_dclk_disable,
+ .is_enabled = vt8500_dclk_is_enabled,
+ .round_rate = vt8500_dclk_round_rate,
+ .set_rate = vt8500_dclk_set_rate,
+ .recalc_rate = vt8500_dclk_recalc_rate,
+};
+
+#define CLK_INIT_GATED BIT(0)
+#define CLK_INIT_DIVISOR BIT(1)
+#define CLK_INIT_GATED_DIVISOR (CLK_INIT_DIVISOR | CLK_INIT_GATED)
+
+static __init void vtwm_device_clk_init(struct device_node *node)
+{
+ u32 en_reg, div_reg;
+ struct clk *clk;
+ struct clk_device *dev_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+ int clk_init_flags = 0;
+
+ dev_clk = kzalloc(sizeof(*dev_clk), GFP_KERNEL);
+ if (WARN_ON(!dev_clk))
+ return;
+
+ dev_clk->lock = &_lock;
+
+ rc = of_property_read_u32(node, "enable-reg", &en_reg);
+ if (!rc) {
+ dev_clk->en_reg = pmc_base + en_reg;
+ rc = of_property_read_u32(node, "enable-bit", &dev_clk->en_bit);
+ if (rc) {
+ pr_err("%s: enable-bit property required for gated clock\n",
+ __func__);
+ return;
+ }
+ clk_init_flags |= CLK_INIT_GATED;
+ }
+
+ rc = of_property_read_u32(node, "divisor-reg", &div_reg);
+ if (!rc) {
+ dev_clk->div_reg = pmc_base + div_reg;
+ /*
+ * use 0x1f as the default mask since it covers
+ * almost all the clocks and reduces dts properties
+ */
+ dev_clk->div_mask = 0x1f;
+
+ of_property_read_u32(node, "divisor-mask", &dev_clk->div_mask);
+ clk_init_flags |= CLK_INIT_DIVISOR;
+ }
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ switch (clk_init_flags) {
+ case CLK_INIT_GATED:
+ init.ops = &vt8500_gated_clk_ops;
+ break;
+ case CLK_INIT_DIVISOR:
+ init.ops = &vt8500_divisor_clk_ops;
+ break;
+ case CLK_INIT_GATED_DIVISOR:
+ init.ops = &vt8500_gated_divisor_clk_ops;
+ break;
+ default:
+ pr_err("%s: Invalid clock description in device tree\n",
+ __func__);
+ kfree(dev_clk);
+ return;
+ }
+
+ init.name = clk_name;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ dev_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &dev_clk->hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(dev_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+}
+
+
+/* PLL clock related functions */
+
+#define to_clk_pll(_hw) container_of(_hw, struct clk_pll, hw)
+
+/* Helper macros for PLL_VT8500 */
+#define VT8500_PLL_MUL(x) ((x & 0x1F) << 1)
+#define VT8500_PLL_DIV(x) ((x & 0x100) ? 1 : 2)
+
+#define VT8500_BITS_TO_FREQ(r, m, d) \
+ ((r / d) * m)
+
+#define VT8500_BITS_TO_VAL(m, d) \
+ ((d == 2 ? 0 : 0x100) | ((m >> 1) & 0x1F))
+
+/* Helper macros for PLL_WM8650 */
+#define WM8650_PLL_MUL(x) (x & 0x3FF)
+#define WM8650_PLL_DIV(x) (((x >> 10) & 7) * (1 << ((x >> 13) & 3)))
+
+#define WM8650_BITS_TO_FREQ(r, m, d1, d2) \
+ (r * m / (d1 * (1 << d2)))
+
+#define WM8650_BITS_TO_VAL(m, d1, d2) \
+ ((d2 << 13) | (d1 << 10) | (m & 0x3FF))
+
+
+static void vt8500_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+ u32 *multiplier, u32 *prediv)
+{
+ unsigned long tclk;
+
+ /* sanity check */
+ if ((rate < parent_rate * 4) || (rate > parent_rate * 62)) {
+ pr_err("%s: requested rate out of range\n", __func__);
+ *multiplier = 0;
+ *prediv = 1;
+ return;
+ }
+ if (rate <= parent_rate * 31)
+ /* use the prediv to double the resolution */
+ *prediv = 2;
+ else
+ *prediv = 1;
+
+ *multiplier = rate / (parent_rate / *prediv);
+ tclk = (parent_rate / *prediv) * *multiplier;
+
+ if (tclk != rate)
+ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__,
+ rate, tclk);
+}
+
+static void wm8650_find_pll_bits(unsigned long rate, unsigned long parent_rate,
+ u32 *multiplier, u32 *divisor1, u32 *divisor2)
+{
+ u32 mul, div1, div2;
+ u32 best_mul, best_div1, best_div2;
+ unsigned long tclk, rate_err, best_err;
+
+ best_err = (unsigned long)-1;
+
+ /* Find the closest match (lower or equal to requested) */
+ for (div1 = 5; div1 >= 3; div1--)
+ for (div2 = 3; div2 >= 0; div2--)
+ for (mul = 3; mul <= 1023; mul++) {
+ tclk = parent_rate * mul / (div1 * (1 << div2));
+ if (tclk > rate)
+ continue;
+ /* error will always be +ve */
+ rate_err = rate - tclk;
+ if (rate_err == 0) {
+ *multiplier = mul;
+ *divisor1 = div1;
+ *divisor2 = div2;
+ return;
+ }
+
+ if (rate_err < best_err) {
+ best_err = rate_err;
+ best_mul = mul;
+ best_div1 = div1;
+ best_div2 = div2;
+ }
+ }
+
+ /* if we got here, it wasn't an exact match */
+ pr_warn("%s: requested rate %lu, found rate %lu\n", __func__, rate,
+ rate - best_err);
+ *multiplier = mul;
+ *divisor1 = div1;
+ *divisor2 = div2;
+}
+
+static int vtwm_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mul, div1, div2;
+ u32 pll_val;
+ unsigned long flags = 0;
+
+ /* sanity check */
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ vt8500_find_pll_bits(rate, parent_rate, &mul, &div1);
+ pll_val = VT8500_BITS_TO_VAL(mul, div1);
+ break;
+ case PLL_TYPE_WM8650:
+ wm8650_find_pll_bits(rate, parent_rate, &mul, &div1, &div2);
+ pll_val = WM8650_BITS_TO_VAL(mul, div1, div2);
+ break;
+ default:
+ pr_err("%s: invalid pll type\n", __func__);
+ return 0;
+ }
+
+ spin_lock_irqsave(pll->lock, flags);
+
+ vt8500_pmc_wait_busy();
+ writel(pll_val, pll->reg);
+ vt8500_pmc_wait_busy();
+
+ spin_unlock_irqrestore(pll->lock, flags);
+
+ return 0;
+}
+
+static long vtwm_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 mul, div1, div2;
+ long round_rate;
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ vt8500_find_pll_bits(rate, *prate, &mul, &div1);
+ round_rate = VT8500_BITS_TO_FREQ(*prate, mul, div1);
+ break;
+ case PLL_TYPE_WM8650:
+ wm8650_find_pll_bits(rate, *prate, &mul, &div1, &div2);
+ round_rate = WM8650_BITS_TO_FREQ(*prate, mul, div1, div2);
+ break;
+ default:
+ round_rate = 0;
+ }
+
+ return round_rate;
+}
+
+static unsigned long vtwm_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_pll *pll = to_clk_pll(hw);
+ u32 pll_val = readl(pll->reg);
+ unsigned long pll_freq;
+
+ switch (pll->type) {
+ case PLL_TYPE_VT8500:
+ pll_freq = parent_rate * VT8500_PLL_MUL(pll_val);
+ pll_freq /= VT8500_PLL_DIV(pll_val);
+ break;
+ case PLL_TYPE_WM8650:
+ pll_freq = parent_rate * WM8650_PLL_MUL(pll_val);
+ pll_freq /= WM8650_PLL_DIV(pll_val);
+ break;
+ default:
+ pll_freq = 0;
+ }
+
+ return pll_freq;
+}
+
+const struct clk_ops vtwm_pll_ops = {
+ .round_rate = vtwm_pll_round_rate,
+ .set_rate = vtwm_pll_set_rate,
+ .recalc_rate = vtwm_pll_recalc_rate,
+};
+
+static __init void vtwm_pll_clk_init(struct device_node *node, int pll_type)
+{
+ u32 reg;
+ struct clk *clk;
+ struct clk_pll *pll_clk;
+ const char *clk_name = node->name;
+ const char *parent_name;
+ struct clk_init_data init;
+ int rc;
+
+ rc = of_property_read_u32(node, "reg", &reg);
+ if (WARN_ON(rc))
+ return;
+
+ pll_clk = kzalloc(sizeof(*pll_clk), GFP_KERNEL);
+ if (WARN_ON(!pll_clk))
+ return;
+
+ pll_clk->reg = pmc_base + reg;
+ pll_clk->lock = &_lock;
+ pll_clk->type = pll_type;
+
+ of_property_read_string(node, "clock-output-names", &clk_name);
+
+ init.name = clk_name;
+ init.ops = &vtwm_pll_ops;
+ init.flags = 0;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ pll_clk->hw.init = &init;
+
+ clk = clk_register(NULL, &pll_clk->hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(pll_clk);
+ return;
+ }
+ rc = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ clk_register_clkdev(clk, clk_name, NULL);
+}
+
+
+/* Wrappers for initialization functions */
+
+static void __init vt8500_pll_init(struct device_node *node)
+{
+ vtwm_pll_clk_init(node, PLL_TYPE_VT8500);
+}
+
+static void __init wm8650_pll_init(struct device_node *node)
+{
+ vtwm_pll_clk_init(node, PLL_TYPE_WM8650);
+}
+
+static const __initconst struct of_device_id clk_match[] = {
+ { .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
+ { .compatible = "via,vt8500-pll-clock", .data = vt8500_pll_init, },
+ { .compatible = "wm,wm8650-pll-clock", .data = wm8650_pll_init, },
+ { .compatible = "via,vt8500-device-clock",
+ .data = vtwm_device_clk_init, },
+ { /* sentinel */ }
+};
+
+void __init vtwm_clk_init(void __iomem *base)
+{
+ if (!base)
+ return;
+
+ pmc_base = base;
+
+ of_clk_init(clk_match);
+}
diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c
index 844043ad0fe4..9f6d15546cbe 100644
--- a/drivers/clk/mxs/clk-imx23.c
+++ b/drivers/clk/mxs/clk-imx23.c
@@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <mach/common.h>
#include <mach/mx23.h>
#include "clk.h"
@@ -71,44 +72,6 @@ static void __init clk_misc_init(void)
__mxs_setl(30 << BP_FRAC_IOFRAC, FRAC);
}
-static struct clk_lookup uart_lookups[] = {
- { .dev_id = "duart", },
- { .dev_id = "mxs-auart.0", },
- { .dev_id = "mxs-auart.1", },
- { .dev_id = "8006c000.serial", },
- { .dev_id = "8006e000.serial", },
- { .dev_id = "80070000.serial", },
-};
-
-static struct clk_lookup hbus_lookups[] = {
- { .dev_id = "imx23-dma-apbh", },
- { .dev_id = "80004000.dma-apbh", },
-};
-
-static struct clk_lookup xbus_lookups[] = {
- { .dev_id = "duart", .con_id = "apb_pclk"},
- { .dev_id = "80070000.serial", .con_id = "apb_pclk"},
- { .dev_id = "imx23-dma-apbx", },
- { .dev_id = "80024000.dma-apbx", },
-};
-
-static struct clk_lookup ssp_lookups[] = {
- { .dev_id = "imx23-mmc.0", },
- { .dev_id = "imx23-mmc.1", },
- { .dev_id = "80010000.ssp", },
- { .dev_id = "80034000.ssp", },
-};
-
-static struct clk_lookup lcdif_lookups[] = {
- { .dev_id = "imx23-fb", },
- { .dev_id = "80030000.lcdif", },
-};
-
-static struct clk_lookup gpmi_lookups[] = {
- { .dev_id = "imx23-gpmi-nand", },
- { .dev_id = "8000c000.gpmi-nand", },
-};
-
static const char *sel_pll[] __initconst = { "pll", "ref_xtal", };
static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", };
static const char *sel_pix[] __initconst = { "ref_pix", "ref_xtal", };
@@ -127,6 +90,7 @@ enum imx23_clk {
};
static struct clk *clks[clk_max];
+static struct clk_onecell_data clk_data;
static enum imx23_clk clks_init_on[] __initdata = {
cpu, hbus, xbus, emi, uart,
@@ -134,6 +98,7 @@ static enum imx23_clk clks_init_on[] __initdata = {
int __init mx23_clocks_init(void)
{
+ struct device_node *np;
int i;
clk_misc_init();
@@ -188,14 +153,14 @@ int __init mx23_clocks_init(void)
return PTR_ERR(clks[i]);
}
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl");
+ if (np) {
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ }
+
clk_register_clkdev(clks[clk32k], NULL, "timrot");
- clk_register_clkdev(clks[pwm], NULL, "80064000.pwm");
- clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups));
- clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups));
- clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups));
- clk_register_clkdevs(clks[ssp], ssp_lookups, ARRAY_SIZE(ssp_lookups));
- clk_register_clkdevs(clks[gpmi], gpmi_lookups, ARRAY_SIZE(gpmi_lookups));
- clk_register_clkdevs(clks[lcdif], lcdif_lookups, ARRAY_SIZE(lcdif_lookups));
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c
index e3aab67b3eb7..613e76f3758e 100644
--- a/drivers/clk/mxs/clk-imx28.c
+++ b/drivers/clk/mxs/clk-imx28.c
@@ -14,6 +14,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/of.h>
#include <mach/common.h>
#include <mach/mx28.h>
#include "clk.h"
@@ -120,90 +121,6 @@ static void __init clk_misc_init(void)
writel_relaxed(val, FRAC0);
}
-static struct clk_lookup uart_lookups[] = {
- { .dev_id = "duart", },
- { .dev_id = "mxs-auart.0", },
- { .dev_id = "mxs-auart.1", },
- { .dev_id = "mxs-auart.2", },
- { .dev_id = "mxs-auart.3", },
- { .dev_id = "mxs-auart.4", },
- { .dev_id = "8006a000.serial", },
- { .dev_id = "8006c000.serial", },
- { .dev_id = "8006e000.serial", },
- { .dev_id = "80070000.serial", },
- { .dev_id = "80072000.serial", },
- { .dev_id = "80074000.serial", },
-};
-
-static struct clk_lookup hbus_lookups[] = {
- { .dev_id = "imx28-dma-apbh", },
- { .dev_id = "80004000.dma-apbh", },
-};
-
-static struct clk_lookup xbus_lookups[] = {
- { .dev_id = "duart", .con_id = "apb_pclk"},
- { .dev_id = "80074000.serial", .con_id = "apb_pclk"},
- { .dev_id = "imx28-dma-apbx", },
- { .dev_id = "80024000.dma-apbx", },
-};
-
-static struct clk_lookup ssp0_lookups[] = {
- { .dev_id = "imx28-mmc.0", },
- { .dev_id = "80010000.ssp", },
-};
-
-static struct clk_lookup ssp1_lookups[] = {
- { .dev_id = "imx28-mmc.1", },
- { .dev_id = "80012000.ssp", },
-};
-
-static struct clk_lookup ssp2_lookups[] = {
- { .dev_id = "imx28-mmc.2", },
- { .dev_id = "80014000.ssp", },
-};
-
-static struct clk_lookup ssp3_lookups[] = {
- { .dev_id = "imx28-mmc.3", },
- { .dev_id = "80016000.ssp", },
-};
-
-static struct clk_lookup lcdif_lookups[] = {
- { .dev_id = "imx28-fb", },
- { .dev_id = "80030000.lcdif", },
-};
-
-static struct clk_lookup gpmi_lookups[] = {
- { .dev_id = "imx28-gpmi-nand", },
- { .dev_id = "8000c000.gpmi-nand", },
-};
-
-static struct clk_lookup fec_lookups[] = {
- { .dev_id = "imx28-fec.0", },
- { .dev_id = "imx28-fec.1", },
- { .dev_id = "800f0000.ethernet", },
- { .dev_id = "800f4000.ethernet", },
-};
-
-static struct clk_lookup can0_lookups[] = {
- { .dev_id = "flexcan.0", },
- { .dev_id = "80032000.can", },
-};
-
-static struct clk_lookup can1_lookups[] = {
- { .dev_id = "flexcan.1", },
- { .dev_id = "80034000.can", },
-};
-
-static struct clk_lookup saif0_lookups[] = {
- { .dev_id = "mxs-saif.0", },
- { .dev_id = "80042000.saif", },
-};
-
-static struct clk_lookup saif1_lookups[] = {
- { .dev_id = "mxs-saif.1", },
- { .dev_id = "80046000.saif", },
-};
-
static const char *sel_cpu[] __initconst = { "ref_cpu", "ref_xtal", };
static const char *sel_io0[] __initconst = { "ref_io0", "ref_xtal", };
static const char *sel_io1[] __initconst = { "ref_io1", "ref_xtal", };
@@ -228,6 +145,7 @@ enum imx28_clk {
};
static struct clk *clks[clk_max];
+static struct clk_onecell_data clk_data;
static enum imx28_clk clks_init_on[] __initdata = {
cpu, hbus, xbus, emi, uart,
@@ -235,6 +153,7 @@ static enum imx28_clk clks_init_on[] __initdata = {
int __init mx28_clocks_init(void)
{
+ struct device_node *np;
int i;
clk_misc_init();
@@ -312,27 +231,15 @@ int __init mx28_clocks_init(void)
return PTR_ERR(clks[i]);
}
+ np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl");
+ if (np) {
+ clk_data.clks = clks;
+ clk_data.clk_num = ARRAY_SIZE(clks);
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+ }
+
clk_register_clkdev(clks[clk32k], NULL, "timrot");
clk_register_clkdev(clks[enet_out], NULL, "enet_out");
- clk_register_clkdev(clks[pwm], NULL, "80064000.pwm");
- clk_register_clkdevs(clks[hbus], hbus_lookups, ARRAY_SIZE(hbus_lookups));
- clk_register_clkdevs(clks[xbus], xbus_lookups, ARRAY_SIZE(xbus_lookups));
- clk_register_clkdevs(clks[uart], uart_lookups, ARRAY_SIZE(uart_lookups));
- clk_register_clkdevs(clks[ssp0], ssp0_lookups, ARRAY_SIZE(ssp0_lookups));
- clk_register_clkdevs(clks[ssp1], ssp1_lookups, ARRAY_SIZE(ssp1_lookups));
- clk_register_clkdevs(clks[ssp2], ssp2_lookups, ARRAY_SIZE(ssp2_lookups));
- clk_register_clkdevs(clks[ssp3], ssp3_lookups, ARRAY_SIZE(ssp3_lookups));
- clk_register_clkdevs(clks[gpmi], gpmi_lookups, ARRAY_SIZE(gpmi_lookups));
- clk_register_clkdevs(clks[saif0], saif0_lookups, ARRAY_SIZE(saif0_lookups));
- clk_register_clkdevs(clks[saif1], saif1_lookups, ARRAY_SIZE(saif1_lookups));
- clk_register_clkdevs(clks[lcdif], lcdif_lookups, ARRAY_SIZE(lcdif_lookups));
- clk_register_clkdevs(clks[fec], fec_lookups, ARRAY_SIZE(fec_lookups));
- clk_register_clkdevs(clks[can0], can0_lookups, ARRAY_SIZE(can0_lookups));
- clk_register_clkdevs(clks[can1], can1_lookups, ARRAY_SIZE(can1_lookups));
- clk_register_clkdev(clks[usb0_pwr], NULL, "8007c000.usbphy");
- clk_register_clkdev(clks[usb1_pwr], NULL, "8007e000.usbphy");
- clk_register_clkdev(clks[usb0], NULL, "80080000.usb");
- clk_register_clkdev(clks[usb1], NULL, "80090000.usb");
for (i = 0; i < ARRAY_SIZE(clks_init_on); i++)
clk_prepare_enable(clks[clks_init_on[i]]);
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index ba7926f5c099..a00b828b1643 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -183,6 +183,12 @@ config GPIO_STA2X11
Say yes here to support the STA2x11/ConneXt GPIO device.
The GPIO module has 128 GPIO pins with alternate functions.
+config GPIO_VT8500
+ bool "VIA/Wondermedia SoC GPIO Support"
+ depends on ARCH_VT8500
+ help
+ Say yes here to support the VT8500/WM8505/WM8650 GPIO controller.
+
config GPIO_XILINX
bool "Xilinx GPIO support"
depends on PPC_OF || MICROBLAZE
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 153caceeb053..a288142ad998 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
+obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o
obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o
obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 9cac88a65f78..9528779ca463 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -26,6 +26,8 @@
#include <linux/syscore_ops.h>
#include <linux/slab.h>
+#include <asm/mach/irq.h>
+
#include <mach/irqs.h>
/*
@@ -59,6 +61,7 @@
#define BANK_OFF(n) (((n) < 3) ? (n) << 2 : 0x100 + (((n) - 3) << 2))
int pxa_last_gpio;
+static int irq_base;
#ifdef CONFIG_OF
static struct irq_domain *domain;
@@ -167,63 +170,14 @@ static inline int __gpio_is_occupied(unsigned gpio)
return ret;
}
-#ifdef CONFIG_ARCH_PXA
-static inline int __pxa_gpio_to_irq(int gpio)
-{
- if (gpio_is_pxa_type(gpio_type))
- return PXA_GPIO_TO_IRQ(gpio);
- return -1;
-}
-
-static inline int __pxa_irq_to_gpio(int irq)
-{
- if (gpio_is_pxa_type(gpio_type))
- return irq - PXA_GPIO_TO_IRQ(0);
- return -1;
-}
-#else
-static inline int __pxa_gpio_to_irq(int gpio) { return -1; }
-static inline int __pxa_irq_to_gpio(int irq) { return -1; }
-#endif
-
-#ifdef CONFIG_ARCH_MMP
-static inline int __mmp_gpio_to_irq(int gpio)
-{
- if (gpio_is_mmp_type(gpio_type))
- return MMP_GPIO_TO_IRQ(gpio);
- return -1;
-}
-
-static inline int __mmp_irq_to_gpio(int irq)
-{
- if (gpio_is_mmp_type(gpio_type))
- return irq - MMP_GPIO_TO_IRQ(0);
- return -1;
-}
-#else
-static inline int __mmp_gpio_to_irq(int gpio) { return -1; }
-static inline int __mmp_irq_to_gpio(int irq) { return -1; }
-#endif
-
static int pxa_gpio_to_irq(struct gpio_chip *chip, unsigned offset)
{
- int gpio, ret;
-
- gpio = chip->base + offset;
- ret = __pxa_gpio_to_irq(gpio);
- if (ret >= 0)
- return ret;
- return __mmp_gpio_to_irq(gpio);
+ return chip->base + offset + irq_base;
}
int pxa_irq_to_gpio(int irq)
{
- int ret;
-
- ret = __pxa_irq_to_gpio(irq);
- if (ret >= 0)
- return ret;
- return __mmp_irq_to_gpio(irq);
+ return irq - irq_base;
}
static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
@@ -403,6 +357,9 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc)
struct pxa_gpio_chip *c;
int loop, gpio, gpio_base, n;
unsigned long gedr;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
do {
loop = 0;
@@ -422,6 +379,8 @@ static void pxa_gpio_demux_handler(unsigned int irq, struct irq_desc *desc)
}
}
} while (loop);
+
+ chained_irq_exit(chip, desc);
}
static void pxa_ack_muxed_gpio(struct irq_data *d)
@@ -535,7 +494,7 @@ const struct irq_domain_ops pxa_irq_domain_ops = {
static int __devinit pxa_gpio_probe_dt(struct platform_device *pdev)
{
- int ret, nr_banks, nr_gpios, irq_base;
+ int ret, nr_banks, nr_gpios;
struct device_node *prev, *next, *np = pdev->dev.of_node;
const struct of_device_id *of_id =
of_match_device(pxa_gpio_dt_ids, &pdev->dev);
@@ -590,10 +549,20 @@ static int __devinit pxa_gpio_probe(struct platform_device *pdev)
int irq0 = 0, irq1 = 0, irq_mux, gpio_offset = 0;
ret = pxa_gpio_probe_dt(pdev);
- if (ret < 0)
+ if (ret < 0) {
pxa_last_gpio = pxa_gpio_nums();
- else
+#ifdef CONFIG_ARCH_PXA
+ if (gpio_is_pxa_type(gpio_type))
+ irq_base = PXA_GPIO_TO_IRQ(0);
+#endif
+#ifdef CONFIG_ARCH_MMP
+ if (gpio_is_mmp_type(gpio_type))
+ irq_base = MMP_GPIO_TO_IRQ(0);
+#endif
+ } else {
use_of = 1;
+ }
+
if (!pxa_last_gpio)
return -EINVAL;
diff --git a/drivers/gpio/gpio-samsung.c b/drivers/gpio/gpio-samsung.c
index 1c169324e357..8af4b06e80f7 100644
--- a/drivers/gpio/gpio-samsung.c
+++ b/drivers/gpio/gpio-samsung.c
@@ -938,6 +938,67 @@ static void __init samsung_gpiolib_add(struct samsung_gpio_chip *chip)
s3c_gpiolib_track(chip);
}
+#if defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF)
+static int s3c24xx_gpio_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ unsigned int pin;
+
+ if (WARN_ON(gc->of_gpio_n_cells < 3))
+ return -EINVAL;
+
+ if (WARN_ON(gpiospec->args_count < gc->of_gpio_n_cells))
+ return -EINVAL;
+
+ if (gpiospec->args[0] > gc->ngpio)
+ return -EINVAL;
+
+ pin = gc->base + gpiospec->args[0];
+
+ if (s3c_gpio_cfgpin(pin, S3C_GPIO_SFN(gpiospec->args[1])))
+ pr_warn("gpio_xlate: failed to set pin function\n");
+ if (s3c_gpio_setpull(pin, gpiospec->args[2] & 0xffff))
+ pr_warn("gpio_xlate: failed to set pin pull up/down\n");
+
+ if (flags)
+ *flags = gpiospec->args[2] >> 16;
+
+ return gpiospec->args[0];
+}
+
+static const struct of_device_id s3c24xx_gpio_dt_match[] __initdata = {
+ { .compatible = "samsung,s3c24xx-gpio", },
+ {}
+};
+
+static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
+ u64 base, u64 offset)
+{
+ struct gpio_chip *gc = &chip->chip;
+ u64 address;
+
+ if (!of_have_populated_dt())
+ return;
+
+ address = chip->base ? base + ((u32)chip->base & 0xfff) : base + offset;
+ gc->of_node = of_find_matching_node_by_address(NULL,
+ s3c24xx_gpio_dt_match, address);
+ if (!gc->of_node) {
+ pr_info("gpio: device tree node not found for gpio controller"
+ " with base address %08llx\n", address);
+ return;
+ }
+ gc->of_gpio_n_cells = 3;
+ gc->of_xlate = s3c24xx_gpio_xlate;
+}
+#else
+static __init void s3c24xx_gpiolib_attach_ofnode(struct samsung_gpio_chip *chip,
+ u64 base, u64 offset)
+{
+ return;
+}
+#endif /* defined(CONFIG_PLAT_S3C24XX) && defined(CONFIG_OF) */
+
static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip,
int nr_chips, void __iomem *base)
{
@@ -962,6 +1023,8 @@ static void __init s3c24xx_gpiolib_add_chips(struct samsung_gpio_chip *chip,
gc->direction_output = samsung_gpiolib_2bit_output;
samsung_gpiolib_add(chip);
+
+ s3c24xx_gpiolib_attach_ofnode(chip, S3C24XX_PA_GPIO, i * 0x10);
}
}
diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c
index f030880bc9bb..c5f8ca233e1f 100644
--- a/drivers/gpio/gpio-twl4030.c
+++ b/drivers/gpio/gpio-twl4030.c
@@ -396,6 +396,29 @@ static int __devinit gpio_twl4030_debounce(u32 debounce, u8 mmc_cd)
static int gpio_twl4030_remove(struct platform_device *pdev);
+static struct twl4030_gpio_platform_data *of_gpio_twl4030(struct device *dev)
+{
+ struct twl4030_gpio_platform_data *omap_twl_info;
+
+ omap_twl_info = devm_kzalloc(dev, sizeof(*omap_twl_info), GFP_KERNEL);
+ if (!omap_twl_info)
+ return NULL;
+
+ omap_twl_info->use_leds = of_property_read_bool(dev->of_node,
+ "ti,use-leds");
+
+ of_property_read_u32(dev->of_node, "ti,debounce",
+ &omap_twl_info->debounce);
+ of_property_read_u32(dev->of_node, "ti,mmc-cd",
+ (u32 *)&omap_twl_info->mmc_cd);
+ of_property_read_u32(dev->of_node, "ti,pullups",
+ &omap_twl_info->pullups);
+ of_property_read_u32(dev->of_node, "ti,pulldowns",
+ &omap_twl_info->pulldowns);
+
+ return omap_twl_info;
+}
+
static int __devinit gpio_twl4030_probe(struct platform_device *pdev)
{
struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;
@@ -428,33 +451,37 @@ no_irqs:
twl_gpiochip.ngpio = TWL4030_GPIO_MAX;
twl_gpiochip.dev = &pdev->dev;
- if (pdata) {
- /*
- * NOTE: boards may waste power if they don't set pullups
- * and pulldowns correctly ... default for non-ULPI pins is
- * pulldown, and some other pins may have external pullups
- * or pulldowns. Careful!
- */
- ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
- if (ret)
- dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
- pdata->pullups, pdata->pulldowns,
- ret);
-
- ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
- if (ret)
- dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
- pdata->debounce, pdata->mmc_cd,
- ret);
-
- /*
- * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
- * is (still) clear if use_leds is set.
- */
- if (pdata->use_leds)
- twl_gpiochip.ngpio += 2;
+ if (node)
+ pdata = of_gpio_twl4030(&pdev->dev);
+
+ if (pdata == NULL) {
+ dev_err(&pdev->dev, "Platform data is missing\n");
+ return -ENXIO;
}
+ /*
+ * NOTE: boards may waste power if they don't set pullups
+ * and pulldowns correctly ... default for non-ULPI pins is
+ * pulldown, and some other pins may have external pullups
+ * or pulldowns. Careful!
+ */
+ ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns);
+ if (ret)
+ dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n",
+ pdata->pullups, pdata->pulldowns, ret);
+
+ ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd);
+ if (ret)
+ dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n",
+ pdata->debounce, pdata->mmc_cd, ret);
+
+ /*
+ * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE,
+ * is (still) clear if use_leds is set.
+ */
+ if (pdata->use_leds)
+ twl_gpiochip.ngpio += 2;
+
ret = gpiochip_add(&twl_gpiochip);
if (ret < 0) {
dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);
diff --git a/drivers/gpio/gpio-vt8500.c b/drivers/gpio/gpio-vt8500.c
new file mode 100644
index 000000000000..bcd8e4aa7c7d
--- /dev/null
+++ b/drivers/gpio/gpio-vt8500.c
@@ -0,0 +1,316 @@
+/* drivers/gpio/gpio-vt8500.c
+ *
+ * Copyright (C) 2012 Tony Prisk <linux@prisktech.co.nz>
+ * Based on arch/arm/mach-vt8500/gpio.c:
+ * - Copyright (C) 2010 Alexey Charkov <alchark@gmail.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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 <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/bitops.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+
+/*
+ We handle GPIOs by bank, each bank containing up to 32 GPIOs covered
+ by one set of registers (although not all may be valid).
+
+ Because different SoC's have different register offsets, we pass the
+ register offsets as data in vt8500_gpio_dt_ids[].
+
+ A value of NO_REG is used to indicate that this register is not
+ supported. Only used for ->en at the moment.
+*/
+
+#define NO_REG 0xFFFF
+
+/*
+ * struct vt8500_gpio_bank_regoffsets
+ * @en: offset to enable register of the bank
+ * @dir: offset to direction register of the bank
+ * @data_out: offset to the data out register of the bank
+ * @data_in: offset to the data in register of the bank
+ * @ngpio: highest valid pin in this bank
+ */
+
+struct vt8500_gpio_bank_regoffsets {
+ unsigned int en;
+ unsigned int dir;
+ unsigned int data_out;
+ unsigned int data_in;
+ unsigned char ngpio;
+};
+
+struct vt8500_gpio_data {
+ unsigned int num_banks;
+ struct vt8500_gpio_bank_regoffsets banks[];
+};
+
+#define VT8500_BANK(__en, __dir, __out, __in, __ngpio) \
+{ \
+ .en = __en, \
+ .dir = __dir, \
+ .data_out = __out, \
+ .data_in = __in, \
+ .ngpio = __ngpio, \
+}
+
+static struct vt8500_gpio_data vt8500_data = {
+ .num_banks = 7,
+ .banks = {
+ VT8500_BANK(0x00, 0x20, 0x40, 0x60, 26),
+ VT8500_BANK(0x04, 0x24, 0x44, 0x64, 28),
+ VT8500_BANK(0x08, 0x28, 0x48, 0x68, 31),
+ VT8500_BANK(0x0C, 0x2C, 0x4C, 0x6C, 19),
+ VT8500_BANK(0x10, 0x30, 0x50, 0x70, 19),
+ VT8500_BANK(0x14, 0x34, 0x54, 0x74, 23),
+ VT8500_BANK(NO_REG, 0x3C, 0x5C, 0x7C, 9),
+ },
+};
+
+static struct vt8500_gpio_data wm8505_data = {
+ .num_banks = 10,
+ .banks = {
+ VT8500_BANK(0x40, 0x68, 0x90, 0xB8, 8),
+ VT8500_BANK(0x44, 0x6C, 0x94, 0xBC, 32),
+ VT8500_BANK(0x48, 0x70, 0x98, 0xC0, 6),
+ VT8500_BANK(0x4C, 0x74, 0x9C, 0xC4, 16),
+ VT8500_BANK(0x50, 0x78, 0xA0, 0xC8, 25),
+ VT8500_BANK(0x54, 0x7C, 0xA4, 0xCC, 5),
+ VT8500_BANK(0x58, 0x80, 0xA8, 0xD0, 5),
+ VT8500_BANK(0x5C, 0x84, 0xAC, 0xD4, 12),
+ VT8500_BANK(0x60, 0x88, 0xB0, 0xD8, 16),
+ VT8500_BANK(0x64, 0x8C, 0xB4, 0xDC, 22),
+ },
+};
+
+/*
+ * No information about which bits are valid so we just make
+ * them all available until its figured out.
+ */
+static struct vt8500_gpio_data wm8650_data = {
+ .num_banks = 9,
+ .banks = {
+ VT8500_BANK(0x40, 0x80, 0xC0, 0x00, 32),
+ VT8500_BANK(0x44, 0x84, 0xC4, 0x04, 32),
+ VT8500_BANK(0x48, 0x88, 0xC8, 0x08, 32),
+ VT8500_BANK(0x4C, 0x8C, 0xCC, 0x0C, 32),
+ VT8500_BANK(0x50, 0x90, 0xD0, 0x10, 32),
+ VT8500_BANK(0x54, 0x94, 0xD4, 0x14, 32),
+ VT8500_BANK(0x58, 0x98, 0xD8, 0x18, 32),
+ VT8500_BANK(0x5C, 0x9C, 0xDC, 0x1C, 32),
+ VT8500_BANK(0x7C, 0xBC, 0xFC, 0x3C, 32),
+ },
+};
+
+struct vt8500_gpio_chip {
+ struct gpio_chip chip;
+
+ const struct vt8500_gpio_bank_regoffsets *regs;
+ void __iomem *base;
+};
+
+
+#define to_vt8500(__chip) container_of(__chip, struct vt8500_gpio_chip, chip)
+
+static int vt8500_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ u32 val;
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ if (vt8500_chip->regs->en == NO_REG)
+ return 0;
+
+ val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en);
+
+ return 0;
+}
+
+static void vt8500_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+ u32 val;
+
+ if (vt8500_chip->regs->en == NO_REG)
+ return;
+
+ val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->en);
+ val &= ~BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->en);
+}
+
+static int vt8500_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir);
+ val &= ~BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir);
+
+ return 0;
+}
+
+static int vt8500_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base + vt8500_chip->regs->dir);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->dir);
+
+ if (value) {
+ val = readl_relaxed(vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ val |= BIT(offset);
+ writel_relaxed(val, vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ }
+ return 0;
+}
+
+static int vt8500_gpio_get_value(struct gpio_chip *chip, unsigned offset)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ return (readl_relaxed(vt8500_chip->base + vt8500_chip->regs->data_in) >>
+ offset) & 1;
+}
+
+static void vt8500_gpio_set_value(struct gpio_chip *chip, unsigned offset,
+ int value)
+{
+ struct vt8500_gpio_chip *vt8500_chip = to_vt8500(chip);
+
+ u32 val = readl_relaxed(vt8500_chip->base +
+ vt8500_chip->regs->data_out);
+ if (value)
+ val |= BIT(offset);
+ else
+ val &= ~BIT(offset);
+
+ writel_relaxed(val, vt8500_chip->base + vt8500_chip->regs->data_out);
+}
+
+static int vt8500_of_xlate(struct gpio_chip *gc,
+ const struct of_phandle_args *gpiospec, u32 *flags)
+{
+ /* bank if specificed in gpiospec->args[0] */
+ if (flags)
+ *flags = gpiospec->args[2];
+
+ return gpiospec->args[1];
+}
+
+static int vt8500_add_chips(struct platform_device *pdev, void __iomem *base,
+ const struct vt8500_gpio_data *data)
+{
+ struct vt8500_gpio_chip *vtchip;
+ struct gpio_chip *chip;
+ int i;
+ int pin_cnt = 0;
+
+ vtchip = devm_kzalloc(&pdev->dev,
+ sizeof(struct vt8500_gpio_chip) * data->num_banks,
+ GFP_KERNEL);
+ if (!vtchip) {
+ pr_err("%s: failed to allocate chip memory\n", __func__);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < data->num_banks; i++) {
+ vtchip[i].base = base;
+ vtchip[i].regs = &data->banks[i];
+
+ chip = &vtchip[i].chip;
+
+ chip->of_xlate = vt8500_of_xlate;
+ chip->of_gpio_n_cells = 3;
+ chip->of_node = pdev->dev.of_node;
+
+ chip->request = vt8500_gpio_request;
+ chip->free = vt8500_gpio_free;
+ chip->direction_input = vt8500_gpio_direction_input;
+ chip->direction_output = vt8500_gpio_direction_output;
+ chip->get = vt8500_gpio_get_value;
+ chip->set = vt8500_gpio_set_value;
+ chip->can_sleep = 0;
+ chip->base = pin_cnt;
+ chip->ngpio = data->banks[i].ngpio;
+
+ pin_cnt += data->banks[i].ngpio;
+
+ gpiochip_add(chip);
+ }
+ return 0;
+}
+
+static struct of_device_id vt8500_gpio_dt_ids[] = {
+ { .compatible = "via,vt8500-gpio", .data = &vt8500_data, },
+ { .compatible = "wm,wm8505-gpio", .data = &wm8505_data, },
+ { .compatible = "wm,wm8650-gpio", .data = &wm8650_data, },
+ { /* Sentinel */ },
+};
+
+static int __devinit vt8500_gpio_probe(struct platform_device *pdev)
+{
+ void __iomem *gpio_base;
+ struct device_node *np;
+ const struct of_device_id *of_id =
+ of_match_device(vt8500_gpio_dt_ids, &pdev->dev);
+
+ if (!of_id) {
+ dev_err(&pdev->dev, "Failed to find gpio controller\n");
+ return -ENODEV;
+ }
+
+ np = pdev->dev.of_node;
+ if (!np) {
+ dev_err(&pdev->dev, "Missing GPIO description in devicetree\n");
+ return -EFAULT;
+ }
+
+ gpio_base = of_iomap(np, 0);
+ if (!gpio_base) {
+ dev_err(&pdev->dev, "Unable to map GPIO registers\n");
+ of_node_put(np);
+ return -ENOMEM;
+ }
+
+ vt8500_add_chips(pdev, gpio_base, of_id->data);
+
+ return 0;
+}
+
+static struct platform_driver vt8500_gpio_driver = {
+ .probe = vt8500_gpio_probe,
+ .driver = {
+ .name = "vt8500-gpio",
+ .owner = THIS_MODULE,
+ .of_match_table = vt8500_gpio_dt_ids,
+ },
+};
+
+module_platform_driver(vt8500_gpio_driver);
+
+MODULE_DESCRIPTION("VT8500 GPIO Driver");
+MODULE_AUTHOR("Tony Prisk <linux@prisktech.co.nz>");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, vt8500_gpio_dt_ids);
diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c
index 252aaefcacfa..d944d6ef7da8 100644
--- a/drivers/mtd/nand/pxa3xx_nand.c
+++ b/drivers/mtd/nand/pxa3xx_nand.c
@@ -22,6 +22,8 @@
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <mach/dma.h>
#include <plat/pxa3xx_nand.h>
@@ -1032,7 +1034,7 @@ static int alloc_nand_resource(struct platform_device *pdev)
struct pxa3xx_nand_platform_data *pdata;
struct pxa3xx_nand_info *info;
struct pxa3xx_nand_host *host;
- struct nand_chip *chip;
+ struct nand_chip *chip = NULL;
struct mtd_info *mtd;
struct resource *r;
int ret, irq, cs;
@@ -1081,21 +1083,31 @@ static int alloc_nand_resource(struct platform_device *pdev)
}
clk_enable(info->clk);
- r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
- if (r == NULL) {
- dev_err(&pdev->dev, "no resource defined for data DMA\n");
- ret = -ENXIO;
- goto fail_put_clk;
- }
- info->drcmr_dat = r->start;
+ /*
+ * This is a dirty hack to make this driver work from devicetree
+ * bindings. It can be removed once we have a prober DMA controller
+ * framework for DT.
+ */
+ if (pdev->dev.of_node && cpu_is_pxa3xx()) {
+ info->drcmr_dat = 97;
+ info->drcmr_cmd = 99;
+ } else {
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no resource defined for data DMA\n");
+ ret = -ENXIO;
+ goto fail_put_clk;
+ }
+ info->drcmr_dat = r->start;
- r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
- if (r == NULL) {
- dev_err(&pdev->dev, "no resource defined for command DMA\n");
- ret = -ENXIO;
- goto fail_put_clk;
+ r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+ if (r == NULL) {
+ dev_err(&pdev->dev, "no resource defined for command DMA\n");
+ ret = -ENXIO;
+ goto fail_put_clk;
+ }
+ info->drcmr_cmd = r->start;
}
- info->drcmr_cmd = r->start;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -1200,12 +1212,55 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static struct of_device_id pxa3xx_nand_dt_ids[] = {
+ { .compatible = "marvell,pxa3xx-nand" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, i2c_pxa_dt_ids);
+
+static int pxa3xx_nand_probe_dt(struct platform_device *pdev)
+{
+ struct pxa3xx_nand_platform_data *pdata;
+ struct device_node *np = pdev->dev.of_node;
+ const struct of_device_id *of_id =
+ of_match_device(pxa3xx_nand_dt_ids, &pdev->dev);
+
+ if (!of_id)
+ return 0;
+
+ pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (of_get_property(np, "marvell,nand-enable-arbiter", NULL))
+ pdata->enable_arbiter = 1;
+ if (of_get_property(np, "marvell,nand-keep-config", NULL))
+ pdata->keep_config = 1;
+ of_property_read_u32(np, "num-cs", &pdata->num_cs);
+
+ pdev->dev.platform_data = pdata;
+
+ return 0;
+}
+#else
+static inline int pxa3xx_nand_probe_dt(struct platform_device *pdev)
+{
+ return 0;
+}
+#endif
+
static int pxa3xx_nand_probe(struct platform_device *pdev)
{
struct pxa3xx_nand_platform_data *pdata;
+ struct mtd_part_parser_data ppdata = {};
struct pxa3xx_nand_info *info;
int ret, cs, probe_success;
+ ret = pxa3xx_nand_probe_dt(pdev);
+ if (ret)
+ return ret;
+
pdata = pdev->dev.platform_data;
if (!pdata) {
dev_err(&pdev->dev, "no platform data defined\n");
@@ -1229,8 +1284,9 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
continue;
}
+ ppdata.of_node = pdev->dev.of_node;
ret = mtd_device_parse_register(info->host[cs]->mtd, NULL,
- NULL, pdata->parts[cs],
+ &ppdata, pdata->parts[cs],
pdata->nr_parts[cs]);
if (!ret)
probe_success = 1;
@@ -1306,6 +1362,7 @@ static int pxa3xx_nand_resume(struct platform_device *pdev)
static struct platform_driver pxa3xx_nand_driver = {
.driver = {
.name = "pxa3xx-nand",
+ .of_match_table = of_match_ptr(pxa3xx_nand_dt_ids),
},
.probe = pxa3xx_nand_probe,
.remove = pxa3xx_nand_remove,
diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c
index 7fca6ce5952b..304360cd213e 100644
--- a/drivers/pinctrl/pinctrl-sirf.c
+++ b/drivers/pinctrl/pinctrl-sirf.c
@@ -17,6 +17,7 @@
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
@@ -916,11 +917,66 @@ static void sirfsoc_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s
seq_printf(s, " " DRIVER_NAME);
}
+static int sirfsoc_dt_node_to_map(struct pinctrl_dev *pctldev,
+ struct device_node *np_config,
+ struct pinctrl_map **map, unsigned *num_maps)
+{
+ struct sirfsoc_pmx *spmx = pinctrl_dev_get_drvdata(pctldev);
+ struct device_node *np;
+ struct property *prop;
+ const char *function, *group;
+ int ret, index = 0, count = 0;
+
+ /* calculate number of maps required */
+ for_each_child_of_node(np_config, np) {
+ ret = of_property_read_string(np, "sirf,function", &function);
+ if (ret < 0)
+ return ret;
+
+ ret = of_property_count_strings(np, "sirf,pins");
+ if (ret < 0)
+ return ret;
+
+ count += ret;
+ }
+
+ if (!count) {
+ dev_err(spmx->dev, "No child nodes passed via DT\n");
+ return -ENODEV;
+ }
+
+ *map = kzalloc(sizeof(**map) * count, GFP_KERNEL);
+ if (!*map)
+ return -ENOMEM;
+
+ for_each_child_of_node(np_config, np) {
+ of_property_read_string(np, "sirf,function", &function);
+ of_property_for_each_string(np, "sirf,pins", prop, group) {
+ (*map)[index].type = PIN_MAP_TYPE_MUX_GROUP;
+ (*map)[index].data.mux.group = group;
+ (*map)[index].data.mux.function = function;
+ index++;
+ }
+ }
+
+ *num_maps = count;
+
+ return 0;
+}
+
+static void sirfsoc_dt_free_map(struct pinctrl_dev *pctldev,
+ struct pinctrl_map *map, unsigned num_maps)
+{
+ kfree(map);
+}
+
static struct pinctrl_ops sirfsoc_pctrl_ops = {
.get_groups_count = sirfsoc_get_groups_count,
.get_group_name = sirfsoc_get_group_name,
.get_group_pins = sirfsoc_get_group_pins,
.pin_dbg_show = sirfsoc_pin_dbg_show,
+ .dt_node_to_map = sirfsoc_dt_node_to_map,
+ .dt_free_map = sirfsoc_dt_free_map,
};
struct sirfsoc_pmx_func {
@@ -1221,7 +1277,7 @@ out_no_gpio_remap:
}
static const struct of_device_id pinmux_ids[] __devinitconst = {
- { .compatible = "sirf,prima2-gpio-pinmux" },
+ { .compatible = "sirf,prima2-pinctrl" },
{}
};
diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c
index bf3c2f669c3c..2e5970fe9eeb 100644
--- a/drivers/rtc/rtc-ab8500.c
+++ b/drivers/rtc/rtc-ab8500.c
@@ -462,16 +462,10 @@ static int __devexit ab8500_rtc_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id ab8500_rtc_match[] = {
- { .compatible = "stericsson,ab8500-rtc", },
- {}
-};
-
static struct platform_driver ab8500_rtc_driver = {
.driver = {
.name = "ab8500-rtc",
.owner = THIS_MODULE,
- .of_match_table = ab8500_rtc_match,
},
.probe = ab8500_rtc_probe,
.remove = __devexit_p(ab8500_rtc_remove),
diff --git a/drivers/rtc/rtc-pxa.c b/drivers/rtc/rtc-pxa.c
index 0075c8fd93d8..f771b2ee4b18 100644
--- a/drivers/rtc/rtc-pxa.c
+++ b/drivers/rtc/rtc-pxa.c
@@ -27,6 +27,8 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <mach/hardware.h>
@@ -396,6 +398,14 @@ static int __exit pxa_rtc_remove(struct platform_device *pdev)
return 0;
}
+#ifdef CONFIG_OF
+static struct of_device_id pxa_rtc_dt_ids[] = {
+ { .compatible = "marvell,pxa-rtc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, pxa_rtc_dt_ids);
+#endif
+
#ifdef CONFIG_PM
static int pxa_rtc_suspend(struct device *dev)
{
@@ -425,6 +435,7 @@ static struct platform_driver pxa_rtc_driver = {
.remove = __exit_p(pxa_rtc_remove),
.driver = {
.name = "pxa-rtc",
+ .of_match_table = of_match_ptr(pxa_rtc_dt_ids),
#ifdef CONFIG_PM
.pm = &pxa_rtc_pm_ops,
#endif
diff --git a/drivers/rtc/rtc-vt8500.c b/drivers/rtc/rtc-vt8500.c
index 9e94fb147c26..07bf19364a74 100644
--- a/drivers/rtc/rtc-vt8500.c
+++ b/drivers/rtc/rtc-vt8500.c
@@ -23,6 +23,7 @@
#include <linux/bcd.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
+#include <linux/of.h>
/*
* Register definitions
@@ -302,12 +303,18 @@ static int __devexit vt8500_rtc_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "via,vt8500-rtc", },
+ {}
+};
+
static struct platform_driver vt8500_rtc_driver = {
.probe = vt8500_rtc_probe,
.remove = __devexit_p(vt8500_rtc_remove),
.driver = {
.name = "vt8500-rtc",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -315,5 +322,5 @@ module_platform_driver(vt8500_rtc_driver);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("VIA VT8500 SoC Realtime Clock Driver (RTC)");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:vt8500-rtc");
diff --git a/drivers/tty/serial/vt8500_serial.c b/drivers/tty/serial/vt8500_serial.c
index 2be006fb3da0..205d4cf4a063 100644
--- a/drivers/tty/serial/vt8500_serial.c
+++ b/drivers/tty/serial/vt8500_serial.c
@@ -34,6 +34,7 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
/*
* UART Register offsets
@@ -76,6 +77,8 @@
#define RX_FIFO_INTS (RXFAF | RXFF | RXOVER | PER | FER | RXTOUT)
#define TX_FIFO_INTS (TXFAE | TXFE | TXUDR)
+#define VT8500_MAX_PORTS 6
+
struct vt8500_port {
struct uart_port uart;
char name[16];
@@ -83,6 +86,13 @@ struct vt8500_port {
unsigned int ier;
};
+/*
+ * we use this variable to keep track of which ports
+ * have been allocated as we can't use pdev->id in
+ * devicetree
+ */
+static unsigned long vt8500_ports_in_use;
+
static inline void vt8500_write(struct uart_port *port, unsigned int val,
unsigned int off)
{
@@ -431,7 +441,7 @@ static int vt8500_verify_port(struct uart_port *port,
return 0;
}
-static struct vt8500_port *vt8500_uart_ports[4];
+static struct vt8500_port *vt8500_uart_ports[VT8500_MAX_PORTS];
static struct uart_driver vt8500_uart_driver;
#ifdef CONFIG_SERIAL_VT8500_CONSOLE
@@ -548,7 +558,9 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
{
struct vt8500_port *vt8500_port;
struct resource *mmres, *irqres;
+ struct device_node *np = pdev->dev.of_node;
int ret;
+ int port;
mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -559,16 +571,46 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
if (!vt8500_port)
return -ENOMEM;
+ if (np)
+ port = of_alias_get_id(np, "serial");
+ if (port > VT8500_MAX_PORTS)
+ port = -1;
+ else
+ port = -1;
+
+ if (port < 0) {
+ /* calculate the port id */
+ port = find_first_zero_bit(&vt8500_ports_in_use,
+ sizeof(vt8500_ports_in_use));
+ }
+
+ if (port > VT8500_MAX_PORTS)
+ return -ENODEV;
+
+ /* reserve the port id */
+ if (test_and_set_bit(port, &vt8500_ports_in_use)) {
+ /* port already in use - shouldn't really happen */
+ return -EBUSY;
+ }
+
vt8500_port->uart.type = PORT_VT8500;
vt8500_port->uart.iotype = UPIO_MEM;
vt8500_port->uart.mapbase = mmres->start;
vt8500_port->uart.irq = irqres->start;
vt8500_port->uart.fifosize = 16;
vt8500_port->uart.ops = &vt8500_uart_pops;
- vt8500_port->uart.line = pdev->id;
+ vt8500_port->uart.line = port;
vt8500_port->uart.dev = &pdev->dev;
vt8500_port->uart.flags = UPF_IOREMAP | UPF_BOOT_AUTOCONF;
- vt8500_port->uart.uartclk = 24000000;
+
+ vt8500_port->clk = of_clk_get(pdev->dev.of_node, 0);
+ if (vt8500_port->clk) {
+ vt8500_port->uart.uartclk = clk_get_rate(vt8500_port->clk);
+ } else {
+ /* use the default of 24Mhz if not specified and warn */
+ pr_warn("%s: serial clock source not specified\n", __func__);
+ vt8500_port->uart.uartclk = 24000000;
+ }
snprintf(vt8500_port->name, sizeof(vt8500_port->name),
"VT8500 UART%d", pdev->id);
@@ -579,7 +621,7 @@ static int __devinit vt8500_serial_probe(struct platform_device *pdev)
goto err;
}
- vt8500_uart_ports[pdev->id] = vt8500_port;
+ vt8500_uart_ports[port] = vt8500_port;
uart_add_one_port(&vt8500_uart_driver, &vt8500_port->uart);
@@ -603,12 +645,18 @@ static int __devexit vt8500_serial_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "via,vt8500-uart", },
+ {}
+};
+
static struct platform_driver vt8500_platform_driver = {
.probe = vt8500_serial_probe,
.remove = __devexit_p(vt8500_serial_remove),
.driver = {
.name = "vt8500_serial",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -642,4 +690,4 @@ module_exit(vt8500_serial_exit);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("Driver for vt8500 serial device");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 0217f7415ef5..b66d951b8e32 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -1788,7 +1788,7 @@ config FB_AU1200
config FB_VT8500
bool "VT8500 LCD Driver"
- depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500
+ depends on (FB = y) && ARM && ARCH_VT8500
select FB_WMT_GE_ROPS
select FB_SYS_IMAGEBLIT
help
@@ -1797,11 +1797,11 @@ config FB_VT8500
config FB_WM8505
bool "WM8505 frame buffer support"
- depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505
+ depends on (FB = y) && ARM && ARCH_VT8500
select FB_WMT_GE_ROPS
select FB_SYS_IMAGEBLIT
help
- This is the framebuffer driver for WonderMedia WM8505
+ This is the framebuffer driver for WonderMedia WM8505/WM8650
integrated LCD controller.
source "drivers/video/geode/Kconfig"
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c
index 2a5fe6ede845..d24595cd0c9b 100644
--- a/drivers/video/vt8500lcdfb.c
+++ b/drivers/video/vt8500lcdfb.c
@@ -35,6 +35,13 @@
#include "vt8500lcdfb.h"
#include "wmt_ge_rops.h"
+#ifdef CONFIG_OF
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/memblock.h>
+#endif
+
+
#define to_vt8500lcd_info(__info) container_of(__info, \
struct vt8500lcd_info, fb)
@@ -270,15 +277,21 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev)
{
struct vt8500lcd_info *fbi;
struct resource *res;
- struct vt8500fb_platform_data *pdata = pdev->dev.platform_data;
void *addr;
int irq, ret;
+ struct fb_videomode of_mode;
+ struct device_node *np;
+ u32 bpp;
+ dma_addr_t fb_mem_phys;
+ unsigned long fb_mem_len;
+ void *fb_mem_virt;
+
ret = -ENOMEM;
fbi = NULL;
- fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16,
- GFP_KERNEL);
+ fbi = devm_kzalloc(&pdev->dev, sizeof(struct vt8500lcd_info)
+ + sizeof(u32) * 16, GFP_KERNEL);
if (!fbi) {
dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
ret = -ENOMEM;
@@ -333,9 +346,45 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev)
goto failed_free_res;
}
- fbi->fb.fix.smem_start = pdata->video_mem_phys;
- fbi->fb.fix.smem_len = pdata->video_mem_len;
- fbi->fb.screen_base = pdata->video_mem_virt;
+ np = of_parse_phandle(pdev->dev.of_node, "default-mode", 0);
+ if (!np) {
+ pr_err("%s: No display description in Device Tree\n", __func__);
+ ret = -EINVAL;
+ goto failed_free_res;
+ }
+
+ /*
+ * This code is copied from Sascha Hauer's of_videomode helper
+ * and can be replaced with a call to the helper once mainlined
+ */
+ ret = 0;
+ ret |= of_property_read_u32(np, "hactive", &of_mode.xres);
+ ret |= of_property_read_u32(np, "vactive", &of_mode.yres);
+ ret |= of_property_read_u32(np, "hback-porch", &of_mode.left_margin);
+ ret |= of_property_read_u32(np, "hfront-porch", &of_mode.right_margin);
+ ret |= of_property_read_u32(np, "hsync-len", &of_mode.hsync_len);
+ ret |= of_property_read_u32(np, "vback-porch", &of_mode.upper_margin);
+ ret |= of_property_read_u32(np, "vfront-porch", &of_mode.lower_margin);
+ ret |= of_property_read_u32(np, "vsync-len", &of_mode.vsync_len);
+ ret |= of_property_read_u32(np, "bpp", &bpp);
+ if (ret) {
+ pr_err("%s: Unable to read display properties\n", __func__);
+ goto failed_free_res;
+ }
+ of_mode.vmode = FB_VMODE_NONINTERLACED;
+
+ /* try allocating the framebuffer */
+ fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8);
+ fb_mem_virt = dma_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys,
+ GFP_KERNEL);
+ if (!fb_mem_virt) {
+ pr_err("%s: Failed to allocate framebuffer\n", __func__);
+ return -ENOMEM;
+ };
+
+ fbi->fb.fix.smem_start = fb_mem_phys;
+ fbi->fb.fix.smem_len = fb_mem_len;
+ fbi->fb.screen_base = fb_mem_virt;
fbi->palette_size = PAGE_ALIGN(512);
fbi->palette_cpu = dma_alloc_coherent(&pdev->dev,
@@ -370,10 +419,11 @@ static int __devinit vt8500lcd_probe(struct platform_device *pdev)
goto failed_free_irq;
}
- fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
- fbi->fb.var.bits_per_pixel = pdata->bpp;
- fbi->fb.var.xres_virtual = pdata->xres_virtual;
- fbi->fb.var.yres_virtual = pdata->yres_virtual;
+ fb_videomode_to_var(&fbi->fb.var, &of_mode);
+
+ fbi->fb.var.xres_virtual = of_mode.xres;
+ fbi->fb.var.yres_virtual = of_mode.yres * 2;
+ fbi->fb.var.bits_per_pixel = bpp;
ret = vt8500lcd_set_par(&fbi->fb);
if (ret) {
@@ -448,12 +498,18 @@ static int __devexit vt8500lcd_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id via_dt_ids[] = {
+ { .compatible = "via,vt8500-fb", },
+ {}
+};
+
static struct platform_driver vt8500lcd_driver = {
.probe = vt8500lcd_probe,
.remove = __devexit_p(vt8500lcd_remove),
.driver = {
.owner = THIS_MODULE,
.name = "vt8500-lcd",
+ .of_match_table = of_match_ptr(via_dt_ids),
},
};
@@ -461,4 +517,5 @@ module_platform_driver(vt8500lcd_driver);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>");
MODULE_DESCRIPTION("LCD controller driver for VIA VT8500");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, via_dt_ids);
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c
index c8703bd61b74..ec4742442103 100644
--- a/drivers/video/wm8505fb.c
+++ b/drivers/video/wm8505fb.c
@@ -28,6 +28,9 @@
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/wait.h>
+#include <linux/of.h>
+#include <linux/of_fdt.h>
+#include <linux/memblock.h>
#include <mach/vt8500fb.h>
@@ -59,8 +62,12 @@ static int wm8505fb_init_hw(struct fb_info *info)
writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR);
writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1);
- /* Set in-memory picture format to RGB 32bpp */
- writel(0x1c, fbi->regbase + WMT_GOVR_COLORSPACE);
+ /*
+ * Set in-memory picture format to RGB
+ * 0x31C sets the correct color mode (RGB565) for WM8650
+ * Bit 8+9 (0x300) are ignored on WM8505 as reserved
+ */
+ writel(0x31c, fbi->regbase + WMT_GOVR_COLORSPACE);
writel(1, fbi->regbase + WMT_GOVR_COLORSPACE1);
/* Virtual buffer size */
@@ -127,6 +134,18 @@ static int wm8505fb_set_par(struct fb_info *info)
info->var.blue.msb_right = 0;
info->fix.visual = FB_VISUAL_TRUECOLOR;
info->fix.line_length = info->var.xres_virtual << 2;
+ } else if (info->var.bits_per_pixel == 16) {
+ info->var.red.offset = 11;
+ info->var.red.length = 5;
+ info->var.red.msb_right = 0;
+ info->var.green.offset = 5;
+ info->var.green.length = 6;
+ info->var.green.msb_right = 0;
+ info->var.blue.offset = 0;
+ info->var.blue.length = 5;
+ info->var.blue.msb_right = 0;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.line_length = info->var.xres_virtual << 1;
}
wm8505fb_set_timing(info);
@@ -246,16 +265,20 @@ static int __devinit wm8505fb_probe(struct platform_device *pdev)
struct wm8505fb_info *fbi;
struct resource *res;
void *addr;
- struct vt8500fb_platform_data *pdata;
int ret;
- pdata = pdev->dev.platform_data;
+ struct fb_videomode of_mode;
+ struct device_node *np;
+ u32 bpp;
+ dma_addr_t fb_mem_phys;
+ unsigned long fb_mem_len;
+ void *fb_mem_virt;
ret = -ENOMEM;
fbi = NULL;
- fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16,
- GFP_KERNEL);
+ fbi = devm_kzalloc(&pdev->dev, sizeof(struct wm8505fb_info) +
+ sizeof(u32) * 16, GFP_KERNEL);
if (!fbi) {
dev_err(&pdev->dev, "Failed to initialize framebuffer device\n");
ret = -ENOMEM;
@@ -305,21 +328,58 @@ static int __devinit wm8505fb_probe(struct platform_device *pdev)
goto failed_free_res;
}
- fb_videomode_to_var(&fbi->fb.var, &pdata->mode);
+ np = of_parse_phandle(pdev->dev.of_node, "default-mode", 0);
+ if (!np) {
+ pr_err("%s: No display description in Device Tree\n", __func__);
+ ret = -EINVAL;
+ goto failed_free_res;
+ }
+
+ /*
+ * This code is copied from Sascha Hauer's of_videomode helper
+ * and can be replaced with a call to the helper once mainlined
+ */
+ ret = 0;
+ ret |= of_property_read_u32(np, "hactive", &of_mode.xres);
+ ret |= of_property_read_u32(np, "vactive", &of_mode.yres);
+ ret |= of_property_read_u32(np, "hback-porch", &of_mode.left_margin);
+ ret |= of_property_read_u32(np, "hfront-porch", &of_mode.right_margin);
+ ret |= of_property_read_u32(np, "hsync-len", &of_mode.hsync_len);
+ ret |= of_property_read_u32(np, "vback-porch", &of_mode.upper_margin);
+ ret |= of_property_read_u32(np, "vfront-porch", &of_mode.lower_margin);
+ ret |= of_property_read_u32(np, "vsync-len", &of_mode.vsync_len);
+ ret |= of_property_read_u32(np, "bpp", &bpp);
+ if (ret) {
+ pr_err("%s: Unable to read display properties\n", __func__);
+ goto failed_free_res;
+ }
+
+ of_mode.vmode = FB_VMODE_NONINTERLACED;
+ fb_videomode_to_var(&fbi->fb.var, &of_mode);
fbi->fb.var.nonstd = 0;
fbi->fb.var.activate = FB_ACTIVATE_NOW;
fbi->fb.var.height = -1;
fbi->fb.var.width = -1;
- fbi->fb.var.xres_virtual = pdata->xres_virtual;
- fbi->fb.var.yres_virtual = pdata->yres_virtual;
- fbi->fb.var.bits_per_pixel = pdata->bpp;
- fbi->fb.fix.smem_start = pdata->video_mem_phys;
- fbi->fb.fix.smem_len = pdata->video_mem_len;
- fbi->fb.screen_base = pdata->video_mem_virt;
- fbi->fb.screen_size = pdata->video_mem_len;
+ /* try allocating the framebuffer */
+ fb_mem_len = of_mode.xres * of_mode.yres * 2 * (bpp / 8);
+ fb_mem_virt = dma_alloc_coherent(&pdev->dev, fb_mem_len, &fb_mem_phys,
+ GFP_KERNEL);
+ if (!fb_mem_virt) {
+ pr_err("%s: Failed to allocate framebuffer\n", __func__);
+ return -ENOMEM;
+ };
+
+ fbi->fb.var.xres_virtual = of_mode.xres;
+ fbi->fb.var.yres_virtual = of_mode.yres * 2;
+ fbi->fb.var.bits_per_pixel = bpp;
+
+ fbi->fb.fix.smem_start = fb_mem_phys;
+ fbi->fb.fix.smem_len = fb_mem_len;
+ fbi->fb.screen_base = fb_mem_virt;
+ fbi->fb.screen_size = fb_mem_len;
if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) {
dev_err(&pdev->dev, "Failed to allocate color map\n");
@@ -395,12 +455,18 @@ static int __devexit wm8505fb_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "wm,wm8505-fb", },
+ {}
+};
+
static struct platform_driver wm8505fb_driver = {
.probe = wm8505fb_probe,
.remove = __devexit_p(wm8505fb_remove),
.driver = {
.owner = THIS_MODULE,
.name = DRIVER_NAME,
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -408,4 +474,5 @@ module_platform_driver(wm8505fb_driver);
MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>");
MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_dt_ids);
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c
index 55be3865015b..ba025b4c7d09 100644
--- a/drivers/video/wmt_ge_rops.c
+++ b/drivers/video/wmt_ge_rops.c
@@ -158,12 +158,18 @@ static int __devexit wmt_ge_rops_remove(struct platform_device *pdev)
return 0;
}
+static const struct of_device_id wmt_dt_ids[] = {
+ { .compatible = "wm,prizm-ge-rops", },
+ { /* sentinel */ }
+};
+
static struct platform_driver wmt_ge_rops_driver = {
.probe = wmt_ge_rops_probe,
.remove = __devexit_p(wmt_ge_rops_remove),
.driver = {
.owner = THIS_MODULE,
.name = "wmt_ge_rops",
+ .of_match_table = of_match_ptr(wmt_dt_ids),
},
};
@@ -172,4 +178,5 @@ module_platform_driver(wmt_ge_rops_driver);
MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com");
MODULE_DESCRIPTION("Accelerators for raster operations using "
"WonderMedia Graphics Engine");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
+MODULE_DEVICE_TABLE(of, wmt_dt_ids);