diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 18:28:06 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-01 18:28:06 -0700 |
commit | 2a2bf85f05e42b12ea6bfe821e2d19221cf93555 (patch) | |
tree | 11abcdaef6e4f8307574056998d306d21558b6ed /drivers | |
parent | 11801e9de26992d37cb869cc74f389b6a7677e0e (diff) | |
parent | 99261fbad0a16f105b262d7525801697588ba526 (diff) | |
download | linux-2a2bf85f05e42b12ea6bfe821e2d19221cf93555.tar.bz2 |
Merge tag 'dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM soc device tree updates from Olof Johansson:
"Device tree conversion and enablement branch. Mostly a bunch of new
bindings and setup for various platforms, but the Via/Winchip VT8500
platform is also converted over from being 100% legacy to now use
device tree for probing. More of that will come for 3.8."
Trivial conflicts due to removal of vt8500 files, and one documentation
file that was added with slightly different contents both here and in
the USb tree.
* tag 'dt' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (212 commits)
arm: vt8500: Fixup for missing gpio.h
ARM: LPC32xx: LED fix in PHY3250 DTS file
ARM: dt: mmp-dma: add binding file
arm: vt8500: Update arch-vt8500 to devicetree support.
arm: vt8500: gpio: Devicetree support for arch-vt8500
arm: vt8500: doc: Add device tree bindings for arch-vt8500 devices
arm: vt8500: clk: Add Common Clock Framework support
video: vt8500: Add devicetree support for vt8500-fb and wm8505-fb
serial: vt8500: Add devicetree support for vt8500-serial
rtc: vt8500: Add devicetree support for vt8500-rtc
arm: vt8500: Add device tree files for VIA/Wondermedia SoC's
ARM: tegra: Add Avionic Design Tamonten Evaluation Carrier support
ARM: tegra: Add Avionic Design Medcom-Wide support
ARM: tegra: Add Avionic Design Plutux support
ARM: tegra: Add Avionic Design Tamonten support
ARM: tegra: dts: Add pwm label
ARM: ux500: Fix SSP register address format
ARM: ux500: Apply tc3589x's GPIO/IRQ properties to HREF's DT
ARM: ux500: Remove redundant #gpio-cell properties from Snowball DT
ARM: ux500: Add all encompassing sound node to the HREF Device Tree
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/clk-vt8500.c | 510 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx23.c | 55 | ||||
-rw-r--r-- | drivers/clk/mxs/clk-imx28.c | 113 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-pxa.c | 77 | ||||
-rw-r--r-- | drivers/gpio/gpio-samsung.c | 63 | ||||
-rw-r--r-- | drivers/gpio/gpio-twl4030.c | 77 | ||||
-rw-r--r-- | drivers/gpio/gpio-vt8500.c | 316 | ||||
-rw-r--r-- | drivers/mtd/nand/pxa3xx_nand.c | 87 | ||||
-rw-r--r-- | drivers/pinctrl/pinctrl-sirf.c | 58 | ||||
-rw-r--r-- | drivers/rtc/rtc-ab8500.c | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-pxa.c | 11 | ||||
-rw-r--r-- | drivers/rtc/rtc-vt8500.c | 9 | ||||
-rw-r--r-- | drivers/tty/serial/vt8500_serial.c | 58 | ||||
-rw-r--r-- | drivers/video/Kconfig | 6 | ||||
-rw-r--r-- | drivers/video/vt8500lcdfb.c | 79 | ||||
-rw-r--r-- | drivers/video/wm8505fb.c | 97 | ||||
-rw-r--r-- | drivers/video/wmt_ge_rops.c | 9 |
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", ®); + 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); |