diff options
Diffstat (limited to 'drivers')
32 files changed, 3427 insertions, 264 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index 7f0b5ca78516..bace9e98f75d 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -40,4 +40,17 @@ config COMMON_CLK_WM831X Supports the clocking subsystem of the WM831x/2x series of PMICs from Wolfson Microlectronics. +config COMMON_CLK_VERSATILE + bool "Clock driver for ARM Reference designs" + depends on ARCH_INTEGRATOR || ARCH_REALVIEW + ---help--- + Supports clocking on ARM Reference designs Integrator/AP, + Integrator/CP, RealView PB1176, EB, PB11MP and PBX. + +config COMMON_CLK_MAX77686 + tristate "Clock driver for Maxim 77686 MFD" + depends on MFD_MAX77686 + ---help--- + This driver supports Maxim 77686 crystal oscillator clock. + endmenu diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index d4c7253eb307..6327536b4900 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -9,8 +9,14 @@ obj-$(CONFIG_ARCH_MXS) += mxs/ obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/ obj-$(CONFIG_PLAT_SPEAR) += spear/ obj-$(CONFIG_ARCH_U300) += clk-u300.o -obj-$(CONFIG_ARCH_INTEGRATOR) += versatile/ +obj-$(CONFIG_COMMON_CLK_VERSATILE) += versatile/ obj-$(CONFIG_ARCH_PRIMA2) += clk-prima2.o +ifeq ($(CONFIG_COMMON_CLK), y) +obj-$(CONFIG_ARCH_MMP) += mmp/ +endif +obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_U8500) += ux500/ # Chip specific obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o +obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o diff --git a/drivers/clk/clk-ls1x.c b/drivers/clk/clk-ls1x.c new file mode 100644 index 000000000000..f20b750235f6 --- /dev/null +++ b/drivers/clk/clk-ls1x.c @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2012 Zhang, Keguang <keguang.zhang@gmail.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/err.h> + +#include <loongson1.h> + +#define OSC 33 + +static DEFINE_SPINLOCK(_lock); + +static int ls1x_pll_clk_enable(struct clk_hw *hw) +{ + return 0; +} + +static void ls1x_pll_clk_disable(struct clk_hw *hw) +{ +} + +static unsigned long ls1x_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u32 pll, rate; + + pll = __raw_readl(LS1X_CLK_PLL_FREQ); + rate = ((12 + (pll & 0x3f)) * 1000000) + + ((((pll >> 8) & 0x3ff) * 1000000) >> 10); + rate *= OSC; + rate >>= 1; + + return rate; +} + +static const struct clk_ops ls1x_pll_clk_ops = { + .enable = ls1x_pll_clk_enable, + .disable = ls1x_pll_clk_disable, + .recalc_rate = ls1x_pll_recalc_rate, +}; + +static struct clk * __init clk_register_pll(struct device *dev, + const char *name, const char *parent_name, unsigned long flags) +{ + struct clk_hw *hw; + struct clk *clk; + struct clk_init_data init; + + /* allocate the divider */ + hw = kzalloc(sizeof(struct clk_hw), GFP_KERNEL); + if (!hw) { + pr_err("%s: could not allocate clk_hw\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &ls1x_pll_clk_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + hw->init = &init; + + /* register the clock */ + clk = clk_register(dev, hw); + + if (IS_ERR(clk)) + kfree(hw); + + return clk; +} + +void __init ls1x_clk_init(void) +{ + struct clk *clk; + + clk = clk_register_pll(NULL, "pll_clk", NULL, CLK_IS_ROOT); + clk_prepare_enable(clk); + + clk = clk_register_divider(NULL, "cpu_clk", "pll_clk", + CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_CPU_SHIFT, + DIV_CPU_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); + clk_prepare_enable(clk); + clk_register_clkdev(clk, "cpu", NULL); + + clk = clk_register_divider(NULL, "dc_clk", "pll_clk", + CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DC_SHIFT, + DIV_DC_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); + clk_prepare_enable(clk); + clk_register_clkdev(clk, "dc", NULL); + + clk = clk_register_divider(NULL, "ahb_clk", "pll_clk", + CLK_SET_RATE_PARENT, LS1X_CLK_PLL_DIV, DIV_DDR_SHIFT, + DIV_DDR_WIDTH, CLK_DIVIDER_ONE_BASED, &_lock); + clk_prepare_enable(clk); + clk_register_clkdev(clk, "ahb", NULL); + clk_register_clkdev(clk, "stmmaceth", NULL); + + clk = clk_register_fixed_factor(NULL, "apb_clk", "ahb_clk", 0, 1, 2); + clk_prepare_enable(clk); + clk_register_clkdev(clk, "apb", NULL); + clk_register_clkdev(clk, "serial8250", NULL); +} diff --git a/drivers/clk/clk-max77686.c b/drivers/clk/clk-max77686.c new file mode 100644 index 000000000000..ac5f5434cb9a --- /dev/null +++ b/drivers/clk/clk-max77686.c @@ -0,0 +1,244 @@ +/* + * clk-max77686.c - Clock driver for Maxim 77686 + * + * Copyright (C) 2012 Samsung Electornics + * Jonghwa Lee <jonghwa3.lee@samsung.com> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/mfd/max77686.h> +#include <linux/mfd/max77686-private.h> +#include <linux/clk-provider.h> +#include <linux/mutex.h> +#include <linux/clkdev.h> + +enum { + MAX77686_CLK_AP = 0, + MAX77686_CLK_CP, + MAX77686_CLK_PMIC, + MAX77686_CLKS_NUM, +}; + +struct max77686_clk { + struct max77686_dev *iodev; + u32 mask; + struct clk_hw hw; + struct clk_lookup *lookup; +}; + +static struct max77686_clk *get_max77686_clk(struct clk_hw *hw) +{ + return container_of(hw, struct max77686_clk, hw); +} + +static int max77686_clk_prepare(struct clk_hw *hw) +{ + struct max77686_clk *max77686; + int ret; + + max77686 = get_max77686_clk(hw); + if (!max77686) + return -ENOMEM; + + ret = regmap_update_bits(max77686->iodev->regmap, + MAX77686_REG_32KHZ, max77686->mask, max77686->mask); + + return ret; +} + +static void max77686_clk_unprepare(struct clk_hw *hw) +{ + struct max77686_clk *max77686; + + max77686 = get_max77686_clk(hw); + if (!max77686) + return; + + regmap_update_bits(max77686->iodev->regmap, + MAX77686_REG_32KHZ, max77686->mask, ~max77686->mask); +} + +static int max77686_clk_is_enabled(struct clk_hw *hw) +{ + struct max77686_clk *max77686; + int ret; + u32 val; + + max77686 = get_max77686_clk(hw); + if (!max77686) + return -ENOMEM; + + ret = regmap_read(max77686->iodev->regmap, + MAX77686_REG_32KHZ, &val); + + if (ret < 0) + return -EINVAL; + + return val & max77686->mask; +} + +static struct clk_ops max77686_clk_ops = { + .prepare = max77686_clk_prepare, + .unprepare = max77686_clk_unprepare, + .is_enabled = max77686_clk_is_enabled, +}; + +static struct clk_init_data max77686_clks_init[MAX77686_CLKS_NUM] = { + [MAX77686_CLK_AP] = { + .name = "32khz_ap", + .ops = &max77686_clk_ops, + .flags = CLK_IS_ROOT, + }, + [MAX77686_CLK_CP] = { + .name = "32khz_cp", + .ops = &max77686_clk_ops, + .flags = CLK_IS_ROOT, + }, + [MAX77686_CLK_PMIC] = { + .name = "32khz_pmic", + .ops = &max77686_clk_ops, + .flags = CLK_IS_ROOT, + }, +}; + +static int max77686_clk_register(struct device *dev, + struct max77686_clk *max77686) +{ + struct clk *clk; + struct clk_hw *hw = &max77686->hw; + + clk = clk_register(dev, hw); + + if (IS_ERR(clk)) + return -ENOMEM; + + max77686->lookup = devm_kzalloc(dev, sizeof(struct clk_lookup), + GFP_KERNEL); + if (IS_ERR(max77686->lookup)) + return -ENOMEM; + + max77686->lookup->con_id = hw->init->name; + max77686->lookup->clk = clk; + + clkdev_add(max77686->lookup); + + return 0; +} + +static __devinit int max77686_clk_probe(struct platform_device *pdev) +{ + struct max77686_dev *iodev = dev_get_drvdata(pdev->dev.parent); + struct max77686_clk **max77686_clks; + int i, ret; + + max77686_clks = devm_kzalloc(&pdev->dev, sizeof(struct max77686_clk *) + * MAX77686_CLKS_NUM, GFP_KERNEL); + if (IS_ERR(max77686_clks)) + return -ENOMEM; + + for (i = 0; i < MAX77686_CLKS_NUM; i++) { + max77686_clks[i] = devm_kzalloc(&pdev->dev, + sizeof(struct max77686_clk), GFP_KERNEL); + if (IS_ERR(max77686_clks[i])) + return -ENOMEM; + } + + for (i = 0; i < MAX77686_CLKS_NUM; i++) { + max77686_clks[i]->iodev = iodev; + max77686_clks[i]->mask = 1 << i; + max77686_clks[i]->hw.init = &max77686_clks_init[i]; + + ret = max77686_clk_register(&pdev->dev, max77686_clks[i]); + if (ret) { + switch (i) { + case MAX77686_CLK_AP: + dev_err(&pdev->dev, "Fail to register CLK_AP\n"); + goto err_clk_ap; + break; + case MAX77686_CLK_CP: + dev_err(&pdev->dev, "Fail to register CLK_CP\n"); + goto err_clk_cp; + break; + case MAX77686_CLK_PMIC: + dev_err(&pdev->dev, "Fail to register CLK_PMIC\n"); + goto err_clk_pmic; + } + } + } + + platform_set_drvdata(pdev, max77686_clks); + + goto out; + +err_clk_pmic: + clkdev_drop(max77686_clks[MAX77686_CLK_CP]->lookup); + kfree(max77686_clks[MAX77686_CLK_CP]->hw.clk); +err_clk_cp: + clkdev_drop(max77686_clks[MAX77686_CLK_AP]->lookup); + kfree(max77686_clks[MAX77686_CLK_AP]->hw.clk); +err_clk_ap: +out: + return ret; +} + +static int __devexit max77686_clk_remove(struct platform_device *pdev) +{ + struct max77686_clk **max77686_clks = platform_get_drvdata(pdev); + int i; + + for (i = 0; i < MAX77686_CLKS_NUM; i++) { + clkdev_drop(max77686_clks[i]->lookup); + kfree(max77686_clks[i]->hw.clk); + } + return 0; +} + +static const struct platform_device_id max77686_clk_id[] = { + { "max77686-clk", 0}, + { }, +}; +MODULE_DEVICE_TABLE(platform, max77686_clk_id); + +static struct platform_driver max77686_clk_driver = { + .driver = { + .name = "max77686-clk", + .owner = THIS_MODULE, + }, + .probe = max77686_clk_probe, + .remove = __devexit_p(max77686_clk_remove), + .id_table = max77686_clk_id, +}; + +static int __init max77686_clk_init(void) +{ + return platform_driver_register(&max77686_clk_driver); +} +subsys_initcall(max77686_clk_init); + +static void __init max77686_clk_cleanup(void) +{ + platform_driver_unregister(&max77686_clk_driver); +} +module_exit(max77686_clk_cleanup); + +MODULE_DESCRIPTION("MAXIM 77686 Clock Driver"); +MODULE_AUTHOR("Jonghwa Lee <jonghwa3.lee@samsung.com>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index efdfd009c270..56e4495ebeb1 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -558,25 +558,6 @@ int clk_enable(struct clk *clk) EXPORT_SYMBOL_GPL(clk_enable); /** - * clk_get_rate - return the rate of clk - * @clk: the clk whose rate is being returned - * - * Simply returns the cached rate of the clk. Does not query the hardware. If - * clk is NULL then returns 0. - */ -unsigned long clk_get_rate(struct clk *clk) -{ - unsigned long rate; - - mutex_lock(&prepare_lock); - rate = __clk_get_rate(clk); - mutex_unlock(&prepare_lock); - - return rate; -} -EXPORT_SYMBOL_GPL(clk_get_rate); - -/** * __clk_round_rate - round the given rate for a clk * @clk: round the rate of this clock * @@ -702,6 +683,30 @@ static void __clk_recalc_rates(struct clk *clk, unsigned long msg) } /** + * clk_get_rate - return the rate of clk + * @clk: the clk whose rate is being returned + * + * Simply returns the cached rate of the clk, unless CLK_GET_RATE_NOCACHE flag + * is set, which means a recalc_rate will be issued. + * If clk is NULL then returns 0. + */ +unsigned long clk_get_rate(struct clk *clk) +{ + unsigned long rate; + + mutex_lock(&prepare_lock); + + if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) + __clk_recalc_rates(clk, 0); + + rate = __clk_get_rate(clk); + mutex_unlock(&prepare_lock); + + return rate; +} +EXPORT_SYMBOL_GPL(clk_get_rate); + +/** * __clk_speculate_rates * @clk: first clk in the subtree * @parent_rate: the "future" rate of clk's parent @@ -1582,6 +1587,20 @@ struct clk *of_clk_src_simple_get(struct of_phandle_args *clkspec, } EXPORT_SYMBOL_GPL(of_clk_src_simple_get); +struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data) +{ + struct clk_onecell_data *clk_data = data; + unsigned int idx = clkspec->args[0]; + + if (idx >= clk_data->clk_num) { + pr_err("%s: invalid clock index %d\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return clk_data->clks[idx]; +} +EXPORT_SYMBOL_GPL(of_clk_src_onecell_get); + /** * of_clk_add_provider() - Register a clock provider for a node * @np: Device node pointer associated with clock provider diff --git a/drivers/clk/mmp/Makefile b/drivers/clk/mmp/Makefile new file mode 100644 index 000000000000..392d78044ce3 --- /dev/null +++ b/drivers/clk/mmp/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for mmp specific clk +# + +obj-y += clk-apbc.o clk-apmu.o clk-frac.o + +obj-$(CONFIG_CPU_PXA168) += clk-pxa168.o +obj-$(CONFIG_CPU_PXA910) += clk-pxa910.o +obj-$(CONFIG_CPU_MMP2) += clk-mmp2.o diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c new file mode 100644 index 000000000000..d14120eaa71f --- /dev/null +++ b/drivers/clk/mmp/clk-apbc.c @@ -0,0 +1,152 @@ +/* + * mmp APB clock operation source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "clk.h" + +/* Common APB clock register bit definitions */ +#define APBC_APBCLK (1 << 0) /* APB Bus Clock Enable */ +#define APBC_FNCLK (1 << 1) /* Functional Clock Enable */ +#define APBC_RST (1 << 2) /* Reset Generation */ +#define APBC_POWER (1 << 7) /* Reset Generation */ + +#define to_clk_apbc(hw) container_of(hw, struct clk_apbc, hw) +struct clk_apbc { + struct clk_hw hw; + void __iomem *base; + unsigned int delay; + unsigned int flags; + spinlock_t *lock; +}; + +static int clk_apbc_prepare(struct clk_hw *hw) +{ + struct clk_apbc *apbc = to_clk_apbc(hw); + unsigned int data; + unsigned long flags = 0; + + /* + * It may share same register as MUX clock, + * and it will impact FNCLK enable. Spinlock is needed + */ + if (apbc->lock) + spin_lock_irqsave(apbc->lock, flags); + + data = readl_relaxed(apbc->base); + if (apbc->flags & APBC_POWER_CTRL) + data |= APBC_POWER; + data |= APBC_FNCLK; + writel_relaxed(data, apbc->base); + + if (apbc->lock) + spin_unlock_irqrestore(apbc->lock, flags); + + udelay(apbc->delay); + + if (apbc->lock) + spin_lock_irqsave(apbc->lock, flags); + + data = readl_relaxed(apbc->base); + data |= APBC_APBCLK; + writel_relaxed(data, apbc->base); + + if (apbc->lock) + spin_unlock_irqrestore(apbc->lock, flags); + + udelay(apbc->delay); + + if (!(apbc->flags & APBC_NO_BUS_CTRL)) { + if (apbc->lock) + spin_lock_irqsave(apbc->lock, flags); + + data = readl_relaxed(apbc->base); + data &= ~APBC_RST; + writel_relaxed(data, apbc->base); + + if (apbc->lock) + spin_unlock_irqrestore(apbc->lock, flags); + } + + return 0; +} + +static void clk_apbc_unprepare(struct clk_hw *hw) +{ + struct clk_apbc *apbc = to_clk_apbc(hw); + unsigned long data; + unsigned long flags = 0; + + if (apbc->lock) + spin_lock_irqsave(apbc->lock, flags); + + data = readl_relaxed(apbc->base); + if (apbc->flags & APBC_POWER_CTRL) + data &= ~APBC_POWER; + data &= ~APBC_FNCLK; + writel_relaxed(data, apbc->base); + + if (apbc->lock) + spin_unlock_irqrestore(apbc->lock, flags); + + udelay(10); + + if (apbc->lock) + spin_lock_irqsave(apbc->lock, flags); + + data = readl_relaxed(apbc->base); + data &= ~APBC_APBCLK; + writel_relaxed(data, apbc->base); + + if (apbc->lock) + spin_unlock_irqrestore(apbc->lock, flags); +} + +struct clk_ops clk_apbc_ops = { + .prepare = clk_apbc_prepare, + .unprepare = clk_apbc_unprepare, +}; + +struct clk *mmp_clk_register_apbc(const char *name, const char *parent_name, + void __iomem *base, unsigned int delay, + unsigned int apbc_flags, spinlock_t *lock) +{ + struct clk_apbc *apbc; + struct clk *clk; + struct clk_init_data init; + + apbc = kzalloc(sizeof(*apbc), GFP_KERNEL); + if (!apbc) + return NULL; + + init.name = name; + init.ops = &clk_apbc_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + apbc->base = base; + apbc->delay = delay; + apbc->flags = apbc_flags; + apbc->lock = lock; + apbc->hw.init = &init; + + clk = clk_register(NULL, &apbc->hw); + if (IS_ERR(clk)) + kfree(apbc); + + return clk; +} diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c new file mode 100644 index 000000000000..abe182b2377f --- /dev/null +++ b/drivers/clk/mmp/clk-apmu.c @@ -0,0 +1,97 @@ +/* + * mmp AXI peripharal clock operation source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/kernel.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/delay.h> +#include <linux/slab.h> + +#include "clk.h" + +#define to_clk_apmu(clk) (container_of(clk, struct clk_apmu, clk)) +struct clk_apmu { + struct clk_hw hw; + void __iomem *base; + u32 rst_mask; + u32 enable_mask; + spinlock_t *lock; +}; + +static int clk_apmu_enable(struct clk_hw *hw) +{ + struct clk_apmu *apmu = to_clk_apmu(hw); + unsigned long data; + unsigned long flags = 0; + + if (apmu->lock) + spin_lock_irqsave(apmu->lock, flags); + + data = readl_relaxed(apmu->base) | apmu->enable_mask; + writel_relaxed(data, apmu->base); + + if (apmu->lock) + spin_unlock_irqrestore(apmu->lock, flags); + + return 0; +} + +static void clk_apmu_disable(struct clk_hw *hw) +{ + struct clk_apmu *apmu = to_clk_apmu(hw); + unsigned long data; + unsigned long flags = 0; + + if (apmu->lock) + spin_lock_irqsave(apmu->lock, flags); + + data = readl_relaxed(apmu->base) & ~apmu->enable_mask; + writel_relaxed(data, apmu->base); + + if (apmu->lock) + spin_unlock_irqrestore(apmu->lock, flags); +} + +struct clk_ops clk_apmu_ops = { + .enable = clk_apmu_enable, + .disable = clk_apmu_disable, +}; + +struct clk *mmp_clk_register_apmu(const char *name, const char *parent_name, + void __iomem *base, u32 enable_mask, spinlock_t *lock) +{ + struct clk_apmu *apmu; + struct clk *clk; + struct clk_init_data init; + + apmu = kzalloc(sizeof(*apmu), GFP_KERNEL); + if (!apmu) + return NULL; + + init.name = name; + init.ops = &clk_apmu_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + apmu->base = base; + apmu->enable_mask = enable_mask; + apmu->lock = lock; + apmu->hw.init = &init; + + clk = clk_register(NULL, &apmu->hw); + + if (IS_ERR(clk)) + kfree(apmu); + + return clk; +} diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c new file mode 100644 index 000000000000..80c1dd15d15c --- /dev/null +++ b/drivers/clk/mmp/clk-frac.c @@ -0,0 +1,153 @@ +/* + * mmp factor clock operation source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/clk-provider.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> + +#include "clk.h" +/* + * It is M/N clock + * + * Fout from synthesizer can be given from two equations: + * numerator/denominator = Fin / (Fout * factor) + */ + +#define to_clk_factor(hw) container_of(hw, struct clk_factor, hw) +struct clk_factor { + struct clk_hw hw; + void __iomem *base; + struct clk_factor_masks *masks; + struct clk_factor_tbl *ftbl; + unsigned int ftbl_cnt; +}; + +static long clk_factor_round_rate(struct clk_hw *hw, unsigned long drate, + unsigned long *prate) +{ + struct clk_factor *factor = to_clk_factor(hw); + unsigned long rate = 0, prev_rate; + int i; + + for (i = 0; i < factor->ftbl_cnt; i++) { + prev_rate = rate; + rate = (((*prate / 10000) * factor->ftbl[i].num) / + (factor->ftbl[i].den * factor->masks->factor)) * 10000; + if (rate > drate) + break; + } + if (i == 0) + return rate; + else + return prev_rate; +} + +static unsigned long clk_factor_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_factor *factor = to_clk_factor(hw); + struct clk_factor_masks *masks = factor->masks; + unsigned int val, num, den; + + val = readl_relaxed(factor->base); + + /* calculate numerator */ + num = (val >> masks->num_shift) & masks->num_mask; + + /* calculate denominator */ + den = (val >> masks->den_shift) & masks->num_mask; + + if (!den) + return 0; + + return (((parent_rate / 10000) * den) / + (num * factor->masks->factor)) * 10000; +} + +/* Configures new clock rate*/ +static int clk_factor_set_rate(struct clk_hw *hw, unsigned long drate, + unsigned long prate) +{ + struct clk_factor *factor = to_clk_factor(hw); + struct clk_factor_masks *masks = factor->masks; + int i; + unsigned long val; + unsigned long prev_rate, rate = 0; + + for (i = 0; i < factor->ftbl_cnt; i++) { + prev_rate = rate; + rate = (((prate / 10000) * factor->ftbl[i].num) / + (factor->ftbl[i].den * factor->masks->factor)) * 10000; + if (rate > drate) + break; + } + if (i > 0) + i--; + + val = readl_relaxed(factor->base); + + val &= ~(masks->num_mask << masks->num_shift); + val |= (factor->ftbl[i].num & masks->num_mask) << masks->num_shift; + + val &= ~(masks->den_mask << masks->den_shift); + val |= (factor->ftbl[i].den & masks->den_mask) << masks->den_shift; + + writel_relaxed(val, factor->base); + + return 0; +} + +static struct clk_ops clk_factor_ops = { + .recalc_rate = clk_factor_recalc_rate, + .round_rate = clk_factor_round_rate, + .set_rate = clk_factor_set_rate, +}; + +struct clk *mmp_clk_register_factor(const char *name, const char *parent_name, + unsigned long flags, void __iomem *base, + struct clk_factor_masks *masks, struct clk_factor_tbl *ftbl, + unsigned int ftbl_cnt) +{ + struct clk_factor *factor; + struct clk_init_data init; + struct clk *clk; + + if (!masks) { + pr_err("%s: must pass a clk_factor_mask\n", __func__); + return ERR_PTR(-EINVAL); + } + + factor = kzalloc(sizeof(*factor), GFP_KERNEL); + if (!factor) { + pr_err("%s: could not allocate factor clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + /* struct clk_aux assignments */ + factor->base = base; + factor->masks = masks; + factor->ftbl = ftbl; + factor->ftbl_cnt = ftbl_cnt; + factor->hw.init = &init; + + init.name = name; + init.ops = &clk_factor_ops; + init.flags = flags; + init.parent_names = &parent_name; + init.num_parents = 1; + + clk = clk_register(NULL, &factor->hw); + if (IS_ERR_OR_NULL(clk)) + kfree(factor); + + return clk; +} diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c new file mode 100644 index 000000000000..ade435820c7e --- /dev/null +++ b/drivers/clk/mmp/clk-mmp2.c @@ -0,0 +1,449 @@ +/* + * mmp2 clock framework source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <mach/addr-map.h> + +#include "clk.h" + +#define APBC_RTC 0x0 +#define APBC_TWSI0 0x4 +#define APBC_TWSI1 0x8 +#define APBC_TWSI2 0xc +#define APBC_TWSI3 0x10 +#define APBC_TWSI4 0x7c +#define APBC_TWSI5 0x80 +#define APBC_KPC 0x18 +#define APBC_UART0 0x2c +#define APBC_UART1 0x30 +#define APBC_UART2 0x34 +#define APBC_UART3 0x88 +#define APBC_GPIO 0x38 +#define APBC_PWM0 0x3c +#define APBC_PWM1 0x40 +#define APBC_PWM2 0x44 +#define APBC_PWM3 0x48 +#define APBC_SSP0 0x50 +#define APBC_SSP1 0x54 +#define APBC_SSP2 0x58 +#define APBC_SSP3 0x5c +#define APMU_SDH0 0x54 +#define APMU_SDH1 0x58 +#define APMU_SDH2 0xe8 +#define APMU_SDH3 0xec +#define APMU_USB 0x5c +#define APMU_DISP0 0x4c +#define APMU_DISP1 0x110 +#define APMU_CCIC0 0x50 +#define APMU_CCIC1 0xf4 +#define MPMU_UART_PLL 0x14 + +static DEFINE_SPINLOCK(clk_lock); + +static struct clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct clk_factor_tbl uart_factor_tbl[] = { + {.num = 14634, .den = 2165}, /*14.745MHZ */ + {.num = 3521, .den = 689}, /*19.23MHZ */ + {.num = 9679, .den = 5728}, /*58.9824MHZ */ + {.num = 15850, .den = 9451}, /*59.429MHZ */ +}; + +static const char *uart_parent[] = {"uart_pll", "vctcxo"}; +static const char *ssp_parent[] = {"vctcxo_4", "vctcxo_2", "vctcxo", "pll1_16"}; +static const char *sdh_parent[] = {"pll1_4", "pll2", "usb_pll", "pll1"}; +static const char *disp_parent[] = {"pll1", "pll1_16", "pll2", "vctcxo"}; +static const char *ccic_parent[] = {"pll1_2", "pll1_16", "vctcxo"}; + +void __init mmp2_clk_init(void) +{ + struct clk *clk; + struct clk *vctcxo; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + + mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K); + if (mpmu_base == NULL) { + pr_err("error to ioremap MPMU base\n"); + return; + } + + apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K); + if (apmu_base == NULL) { + pr_err("error to ioremap APMU base\n"); + return; + } + + apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K); + if (apbc_base == NULL) { + pr_err("error to ioremap APBC base\n"); + return; + } + + clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200); + clk_register_clkdev(clk, "clk32", NULL); + + vctcxo = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT, + 26000000); + clk_register_clkdev(vctcxo, "vctcxo", NULL); + + clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT, + 800000000); + clk_register_clkdev(clk, "pll1", NULL); + + clk = clk_register_fixed_rate(NULL, "usb_pll", NULL, CLK_IS_ROOT, + 480000000); + clk_register_clkdev(clk, "usb_pll", NULL); + + clk = clk_register_fixed_rate(NULL, "pll2", NULL, CLK_IS_ROOT, + 960000000); + clk_register_clkdev(clk, "pll2", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_2", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_4", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_8", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_16", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_20", "pll1_4", + CLK_SET_RATE_PARENT, 1, 5); + clk_register_clkdev(clk, "pll1_20", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_3", "pll1", + CLK_SET_RATE_PARENT, 1, 3); + clk_register_clkdev(clk, "pll1_3", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_3", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_6", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_12", NULL); + + clk = clk_register_fixed_factor(NULL, "pll2_2", "pll2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll2_2", NULL); + + clk = clk_register_fixed_factor(NULL, "pll2_4", "pll2_2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll2_4", NULL); + + clk = clk_register_fixed_factor(NULL, "pll2_8", "pll2_4", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll2_8", NULL); + + clk = clk_register_fixed_factor(NULL, "pll2_16", "pll2_8", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll2_16", NULL); + + clk = clk_register_fixed_factor(NULL, "pll2_3", "pll2", + CLK_SET_RATE_PARENT, 1, 3); + clk_register_clkdev(clk, "pll2_3", NULL); + + clk = clk_register_fixed_factor(NULL, "pll2_6", "pll2_3", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll2_6", NULL); + + clk = clk_register_fixed_factor(NULL, "pll2_12", "pll2_6", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll2_12", NULL); + + clk = clk_register_fixed_factor(NULL, "vctcxo_2", "vctcxo", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "vctcxo_2", NULL); + + clk = clk_register_fixed_factor(NULL, "vctcxo_4", "vctcxo_2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "vctcxo_4", NULL); + + clk = mmp_clk_register_factor("uart_pll", "pll1_4", 0, + mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl)); + clk_set_rate(clk, 14745600); + clk_register_clkdev(clk, "uart_pll", NULL); + + clk = mmp_clk_register_apbc("twsi0", "vctcxo", + apbc_base + APBC_TWSI0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0"); + + clk = mmp_clk_register_apbc("twsi1", "vctcxo", + apbc_base + APBC_TWSI1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1"); + + clk = mmp_clk_register_apbc("twsi2", "vctcxo", + apbc_base + APBC_TWSI2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.2"); + + clk = mmp_clk_register_apbc("twsi3", "vctcxo", + apbc_base + APBC_TWSI3, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.3"); + + clk = mmp_clk_register_apbc("twsi4", "vctcxo", + apbc_base + APBC_TWSI4, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.4"); + + clk = mmp_clk_register_apbc("twsi5", "vctcxo", + apbc_base + APBC_TWSI5, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.5"); + + clk = mmp_clk_register_apbc("gpio", "vctcxo", + apbc_base + APBC_GPIO, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa-gpio"); + + clk = mmp_clk_register_apbc("kpc", "clk32", + apbc_base + APBC_KPC, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa27x-keypad"); + + clk = mmp_clk_register_apbc("rtc", "clk32", + apbc_base + APBC_RTC, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-rtc"); + + clk = mmp_clk_register_apbc("pwm0", "vctcxo", + apbc_base + APBC_PWM0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp2-pwm.0"); + + clk = mmp_clk_register_apbc("pwm1", "vctcxo", + apbc_base + APBC_PWM1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp2-pwm.1"); + + clk = mmp_clk_register_apbc("pwm2", "vctcxo", + apbc_base + APBC_PWM2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp2-pwm.2"); + + clk = mmp_clk_register_apbc("pwm3", "vctcxo", + apbc_base + APBC_PWM3, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp2-pwm.3"); + + clk = clk_register_mux(NULL, "uart0_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); + clk_set_parent(clk, vctcxo); + clk_register_clkdev(clk, "uart_mux.0", NULL); + + clk = mmp_clk_register_apbc("uart0", "uart0_mux", + apbc_base + APBC_UART0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); + + clk = clk_register_mux(NULL, "uart1_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); + clk_set_parent(clk, vctcxo); + clk_register_clkdev(clk, "uart_mux.1", NULL); + + clk = mmp_clk_register_apbc("uart1", "uart1_mux", + apbc_base + APBC_UART1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); + + clk = clk_register_mux(NULL, "uart2_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); + clk_set_parent(clk, vctcxo); + clk_register_clkdev(clk, "uart_mux.2", NULL); + + clk = mmp_clk_register_apbc("uart2", "uart2_mux", + apbc_base + APBC_UART2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); + + clk = clk_register_mux(NULL, "uart3_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART3, 4, 3, 0, &clk_lock); + clk_set_parent(clk, vctcxo); + clk_register_clkdev(clk, "uart_mux.3", NULL); + + clk = mmp_clk_register_apbc("uart3", "uart3_mux", + apbc_base + APBC_UART3, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.3"); + + clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "uart_mux.0", NULL); + + clk = mmp_clk_register_apbc("ssp0", "ssp0_mux", + apbc_base + APBC_SSP0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.0"); + + clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.1", NULL); + + clk = mmp_clk_register_apbc("ssp1", "ssp1_mux", + apbc_base + APBC_SSP1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.1"); + + clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.2", NULL); + + clk = mmp_clk_register_apbc("ssp2", "ssp2_mux", + apbc_base + APBC_SSP2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.2"); + + clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.3", NULL); + + clk = mmp_clk_register_apbc("ssp3", "ssp3_mux", + apbc_base + APBC_SSP3, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.3"); + + clk = clk_register_mux(NULL, "sdh_mux", sdh_parent, + ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_SDH0, 8, 2, 0, &clk_lock); + clk_register_clkdev(clk, "sdh_mux", NULL); + + clk = clk_register_divider(NULL, "sdh_div", "sdh_mux", + CLK_SET_RATE_PARENT, apmu_base + APMU_SDH0, + 10, 4, CLK_DIVIDER_ONE_BASED, &clk_lock); + clk_register_clkdev(clk, "sdh_div", NULL); + + clk = mmp_clk_register_apmu("sdh0", "sdh_div", apmu_base + APMU_SDH0, + 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxav3.0"); + + clk = mmp_clk_register_apmu("sdh1", "sdh_div", apmu_base + APMU_SDH1, + 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxav3.1"); + + clk = mmp_clk_register_apmu("sdh2", "sdh_div", apmu_base + APMU_SDH2, + 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxav3.2"); + + clk = mmp_clk_register_apmu("sdh3", "sdh_div", apmu_base + APMU_SDH3, + 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxav3.3"); + + clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB, + 0x9, &clk_lock); + clk_register_clkdev(clk, "usb_clk", NULL); + + clk = clk_register_mux(NULL, "disp0_mux", disp_parent, + ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_DISP0, 6, 2, 0, &clk_lock); + clk_register_clkdev(clk, "disp_mux.0", NULL); + + clk = clk_register_divider(NULL, "disp0_div", "disp0_mux", + CLK_SET_RATE_PARENT, apmu_base + APMU_DISP0, + 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock); + clk_register_clkdev(clk, "disp_div.0", NULL); + + clk = mmp_clk_register_apmu("disp0", "disp0_div", + apmu_base + APMU_DISP0, 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-disp.0"); + + clk = clk_register_divider(NULL, "disp0_sphy_div", "disp0_mux", 0, + apmu_base + APMU_DISP0, 15, 5, 0, &clk_lock); + clk_register_clkdev(clk, "disp_sphy_div.0", NULL); + + clk = mmp_clk_register_apmu("disp0_sphy", "disp0_sphy_div", + apmu_base + APMU_DISP0, 0x1024, &clk_lock); + clk_register_clkdev(clk, "disp_sphy.0", NULL); + + clk = clk_register_mux(NULL, "disp1_mux", disp_parent, + ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_DISP1, 6, 2, 0, &clk_lock); + clk_register_clkdev(clk, "disp_mux.1", NULL); + + clk = clk_register_divider(NULL, "disp1_div", "disp1_mux", + CLK_SET_RATE_PARENT, apmu_base + APMU_DISP1, + 8, 4, CLK_DIVIDER_ONE_BASED, &clk_lock); + clk_register_clkdev(clk, "disp_div.1", NULL); + + clk = mmp_clk_register_apmu("disp1", "disp1_div", + apmu_base + APMU_DISP1, 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-disp.1"); + + clk = mmp_clk_register_apmu("ccic_arbiter", "vctcxo", + apmu_base + APMU_CCIC0, 0x1800, &clk_lock); + clk_register_clkdev(clk, "ccic_arbiter", NULL); + + clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, + ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_CCIC0, 6, 2, 0, &clk_lock); + clk_register_clkdev(clk, "ccic_mux.0", NULL); + + clk = clk_register_divider(NULL, "ccic0_div", "ccic0_mux", + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, + 17, 4, CLK_DIVIDER_ONE_BASED, &clk_lock); + clk_register_clkdev(clk, "ccic_div.0", NULL); + + clk = mmp_clk_register_apmu("ccic0", "ccic0_div", + apmu_base + APMU_CCIC0, 0x1b, &clk_lock); + clk_register_clkdev(clk, "fnclk", "mmp-ccic.0"); + + clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_div", + apmu_base + APMU_CCIC0, 0x24, &clk_lock); + clk_register_clkdev(clk, "phyclk", "mmp-ccic.0"); + + clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_div", + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, + 10, 5, 0, &clk_lock); + clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.0"); + + clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div", + apmu_base + APMU_CCIC0, 0x300, &clk_lock); + clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0"); + + clk = clk_register_mux(NULL, "ccic1_mux", ccic_parent, + ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_CCIC1, 6, 2, 0, &clk_lock); + clk_register_clkdev(clk, "ccic_mux.1", NULL); + + clk = clk_register_divider(NULL, "ccic1_div", "ccic1_mux", + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1, + 16, 4, CLK_DIVIDER_ONE_BASED, &clk_lock); + clk_register_clkdev(clk, "ccic_div.1", NULL); + + clk = mmp_clk_register_apmu("ccic1", "ccic1_div", + apmu_base + APMU_CCIC1, 0x1b, &clk_lock); + clk_register_clkdev(clk, "fnclk", "mmp-ccic.1"); + + clk = mmp_clk_register_apmu("ccic1_phy", "ccic1_div", + apmu_base + APMU_CCIC1, 0x24, &clk_lock); + clk_register_clkdev(clk, "phyclk", "mmp-ccic.1"); + + clk = clk_register_divider(NULL, "ccic1_sphy_div", "ccic1_div", + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC1, + 10, 5, 0, &clk_lock); + clk_register_clkdev(clk, "sphyclk_div", "mmp-ccic.1"); + + clk = mmp_clk_register_apmu("ccic1_sphy", "ccic1_sphy_div", + apmu_base + APMU_CCIC1, 0x300, &clk_lock); + clk_register_clkdev(clk, "sphyclk", "mmp-ccic.1"); +} diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c new file mode 100644 index 000000000000..e8d036c12cbf --- /dev/null +++ b/drivers/clk/mmp/clk-pxa168.c @@ -0,0 +1,346 @@ +/* + * pxa168 clock framework source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <mach/addr-map.h> + +#include "clk.h" + +#define APBC_RTC 0x28 +#define APBC_TWSI0 0x2c +#define APBC_KPC 0x30 +#define APBC_UART0 0x0 +#define APBC_UART1 0x4 +#define APBC_GPIO 0x8 +#define APBC_PWM0 0xc +#define APBC_PWM1 0x10 +#define APBC_PWM2 0x14 +#define APBC_PWM3 0x18 +#define APBC_SSP0 0x81c +#define APBC_SSP1 0x820 +#define APBC_SSP2 0x84c +#define APBC_SSP3 0x858 +#define APBC_SSP4 0x85c +#define APBC_TWSI1 0x6c +#define APBC_UART2 0x70 +#define APMU_SDH0 0x54 +#define APMU_SDH1 0x58 +#define APMU_USB 0x5c +#define APMU_DISP0 0x4c +#define APMU_CCIC0 0x50 +#define APMU_DFC 0x60 +#define MPMU_UART_PLL 0x14 + +static DEFINE_SPINLOCK(clk_lock); + +static struct clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct clk_factor_tbl uart_factor_tbl[] = { + {.num = 8125, .den = 1536}, /*14.745MHZ */ +}; + +static const char *uart_parent[] = {"pll1_3_16", "uart_pll"}; +static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static const char *sdh_parent[] = {"pll1_12", "pll1_13"}; +static const char *disp_parent[] = {"pll1_2", "pll1_12"}; +static const char *ccic_parent[] = {"pll1_2", "pll1_12"}; +static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"}; + +void __init pxa168_clk_init(void) +{ + struct clk *clk; + struct clk *uart_pll; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbc_base; + + mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K); + if (mpmu_base == NULL) { + pr_err("error to ioremap MPMU base\n"); + return; + } + + apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K); + if (apmu_base == NULL) { + pr_err("error to ioremap APMU base\n"); + return; + } + + apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K); + if (apbc_base == NULL) { + pr_err("error to ioremap APBC base\n"); + return; + } + + clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200); + clk_register_clkdev(clk, "clk32", NULL); + + clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT, + 26000000); + clk_register_clkdev(clk, "vctcxo", NULL); + + clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT, + 624000000); + clk_register_clkdev(clk, "pll1", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_2", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_4", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_8", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_16", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2", + CLK_SET_RATE_PARENT, 1, 3); + clk_register_clkdev(clk, "pll1_6", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_12", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_24", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_48", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_96", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1", + CLK_SET_RATE_PARENT, 1, 13); + clk_register_clkdev(clk, "pll1_13", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1", + CLK_SET_RATE_PARENT, 2, 3); + clk_register_clkdev(clk, "pll1_13_1_5", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1", + CLK_SET_RATE_PARENT, 2, 3); + clk_register_clkdev(clk, "pll1_2_1_5", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1", + CLK_SET_RATE_PARENT, 3, 16); + clk_register_clkdev(clk, "pll1_3_16", NULL); + + uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0, + mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl)); + clk_set_rate(uart_pll, 14745600); + clk_register_clkdev(uart_pll, "uart_pll", NULL); + + clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5", + apbc_base + APBC_TWSI0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0"); + + clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5", + apbc_base + APBC_TWSI1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1"); + + clk = mmp_clk_register_apbc("gpio", "vctcxo", + apbc_base + APBC_GPIO, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa-gpio"); + + clk = mmp_clk_register_apbc("kpc", "clk32", + apbc_base + APBC_KPC, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa27x-keypad"); + + clk = mmp_clk_register_apbc("rtc", "clk32", + apbc_base + APBC_RTC, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "sa1100-rtc"); + + clk = mmp_clk_register_apbc("pwm0", "pll1_48", + apbc_base + APBC_PWM0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa168-pwm.0"); + + clk = mmp_clk_register_apbc("pwm1", "pll1_48", + apbc_base + APBC_PWM1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa168-pwm.1"); + + clk = mmp_clk_register_apbc("pwm2", "pll1_48", + apbc_base + APBC_PWM2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa168-pwm.2"); + + clk = mmp_clk_register_apbc("pwm3", "pll1_48", + apbc_base + APBC_PWM3, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa168-pwm.3"); + + clk = clk_register_mux(NULL, "uart0_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); + clk_set_parent(clk, uart_pll); + clk_register_clkdev(clk, "uart_mux.0", NULL); + + clk = mmp_clk_register_apbc("uart0", "uart0_mux", + apbc_base + APBC_UART0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); + + clk = clk_register_mux(NULL, "uart1_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); + clk_set_parent(clk, uart_pll); + clk_register_clkdev(clk, "uart_mux.1", NULL); + + clk = mmp_clk_register_apbc("uart1", "uart1_mux", + apbc_base + APBC_UART1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); + + clk = clk_register_mux(NULL, "uart2_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART2, 4, 3, 0, &clk_lock); + clk_set_parent(clk, uart_pll); + clk_register_clkdev(clk, "uart_mux.2", NULL); + + clk = mmp_clk_register_apbc("uart2", "uart2_mux", + apbc_base + APBC_UART2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); + + clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "uart_mux.0", NULL); + + clk = mmp_clk_register_apbc("ssp0", "ssp0_mux", apbc_base + APBC_SSP0, + 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.0"); + + clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.1", NULL); + + clk = mmp_clk_register_apbc("ssp1", "ssp1_mux", apbc_base + APBC_SSP1, + 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.1"); + + clk = clk_register_mux(NULL, "ssp2_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP2, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.2", NULL); + + clk = mmp_clk_register_apbc("ssp2", "ssp1_mux", apbc_base + APBC_SSP2, + 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.2"); + + clk = clk_register_mux(NULL, "ssp3_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP3, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.3", NULL); + + clk = mmp_clk_register_apbc("ssp3", "ssp1_mux", apbc_base + APBC_SSP3, + 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.3"); + + clk = clk_register_mux(NULL, "ssp4_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP4, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.4", NULL); + + clk = mmp_clk_register_apbc("ssp4", "ssp1_mux", apbc_base + APBC_SSP4, + 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.4"); + + clk = mmp_clk_register_apmu("dfc", "pll1_4", apmu_base + APMU_DFC, + 0x19b, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); + + clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, + ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "sdh0_mux", NULL); + + clk = mmp_clk_register_apmu("sdh0", "sdh_mux", apmu_base + APMU_SDH0, + 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); + + clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, + ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "sdh1_mux", NULL); + + clk = mmp_clk_register_apmu("sdh1", "sdh1_mux", apmu_base + APMU_SDH1, + 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxa.1"); + + clk = mmp_clk_register_apmu("usb", "usb_pll", apmu_base + APMU_USB, + 0x9, &clk_lock); + clk_register_clkdev(clk, "usb_clk", NULL); + + clk = mmp_clk_register_apmu("sph", "usb_pll", apmu_base + APMU_USB, + 0x12, &clk_lock); + clk_register_clkdev(clk, "sph_clk", NULL); + + clk = clk_register_mux(NULL, "disp0_mux", disp_parent, + ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "disp_mux.0", NULL); + + clk = mmp_clk_register_apmu("disp0", "disp0_mux", + apmu_base + APMU_DISP0, 0x1b, &clk_lock); + clk_register_clkdev(clk, "fnclk", "mmp-disp.0"); + + clk = mmp_clk_register_apmu("disp0_hclk", "disp0_mux", + apmu_base + APMU_DISP0, 0x24, &clk_lock); + clk_register_clkdev(clk, "hclk", "mmp-disp.0"); + + clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, + ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "ccic_mux.0", NULL); + + clk = mmp_clk_register_apmu("ccic0", "ccic0_mux", + apmu_base + APMU_CCIC0, 0x1b, &clk_lock); + clk_register_clkdev(clk, "fnclk", "mmp-ccic.0"); + + clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, + ARRAY_SIZE(ccic_phy_parent), + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, + 7, 1, 0, &clk_lock); + clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); + + clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", + apmu_base + APMU_CCIC0, 0x24, &clk_lock); + clk_register_clkdev(clk, "phyclk", "mmp-ccic.0"); + + clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux", + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, + 10, 5, 0, &clk_lock); + clk_register_clkdev(clk, "sphyclk_div", NULL); + + clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div", + apmu_base + APMU_CCIC0, 0x300, &clk_lock); + clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0"); +} diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c new file mode 100644 index 000000000000..7048c31d6e7e --- /dev/null +++ b/drivers/clk/mmp/clk-pxa910.c @@ -0,0 +1,320 @@ +/* + * pxa910 clock framework source file + * + * Copyright (C) 2012 Marvell + * Chao Xie <xiechao.mail@gmail.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/spinlock.h> +#include <linux/io.h> +#include <linux/delay.h> +#include <linux/err.h> + +#include <mach/addr-map.h> + +#include "clk.h" + +#define APBC_RTC 0x28 +#define APBC_TWSI0 0x2c +#define APBC_KPC 0x18 +#define APBC_UART0 0x0 +#define APBC_UART1 0x4 +#define APBC_GPIO 0x8 +#define APBC_PWM0 0xc +#define APBC_PWM1 0x10 +#define APBC_PWM2 0x14 +#define APBC_PWM3 0x18 +#define APBC_SSP0 0x1c +#define APBC_SSP1 0x20 +#define APBC_SSP2 0x4c +#define APBCP_TWSI1 0x28 +#define APBCP_UART2 0x1c +#define APMU_SDH0 0x54 +#define APMU_SDH1 0x58 +#define APMU_USB 0x5c +#define APMU_DISP0 0x4c +#define APMU_CCIC0 0x50 +#define APMU_DFC 0x60 +#define MPMU_UART_PLL 0x14 + +static DEFINE_SPINLOCK(clk_lock); + +static struct clk_factor_masks uart_factor_masks = { + .factor = 2, + .num_mask = 0x1fff, + .den_mask = 0x1fff, + .num_shift = 16, + .den_shift = 0, +}; + +static struct clk_factor_tbl uart_factor_tbl[] = { + {.num = 8125, .den = 1536}, /*14.745MHZ */ +}; + +static const char *uart_parent[] = {"pll1_3_16", "uart_pll"}; +static const char *ssp_parent[] = {"pll1_96", "pll1_48", "pll1_24", "pll1_12"}; +static const char *sdh_parent[] = {"pll1_12", "pll1_13"}; +static const char *disp_parent[] = {"pll1_2", "pll1_12"}; +static const char *ccic_parent[] = {"pll1_2", "pll1_12"}; +static const char *ccic_phy_parent[] = {"pll1_6", "pll1_12"}; + +void __init pxa910_clk_init(void) +{ + struct clk *clk; + struct clk *uart_pll; + void __iomem *mpmu_base; + void __iomem *apmu_base; + void __iomem *apbcp_base; + void __iomem *apbc_base; + + mpmu_base = ioremap(APB_PHYS_BASE + 0x50000, SZ_4K); + if (mpmu_base == NULL) { + pr_err("error to ioremap MPMU base\n"); + return; + } + + apmu_base = ioremap(AXI_PHYS_BASE + 0x82800, SZ_4K); + if (apmu_base == NULL) { + pr_err("error to ioremap APMU base\n"); + return; + } + + apbcp_base = ioremap(APB_PHYS_BASE + 0x3b000, SZ_4K); + if (apbcp_base == NULL) { + pr_err("error to ioremap APBC extension base\n"); + return; + } + + apbc_base = ioremap(APB_PHYS_BASE + 0x15000, SZ_4K); + if (apbc_base == NULL) { + pr_err("error to ioremap APBC base\n"); + return; + } + + clk = clk_register_fixed_rate(NULL, "clk32", NULL, CLK_IS_ROOT, 3200); + clk_register_clkdev(clk, "clk32", NULL); + + clk = clk_register_fixed_rate(NULL, "vctcxo", NULL, CLK_IS_ROOT, + 26000000); + clk_register_clkdev(clk, "vctcxo", NULL); + + clk = clk_register_fixed_rate(NULL, "pll1", NULL, CLK_IS_ROOT, + 624000000); + clk_register_clkdev(clk, "pll1", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_2", "pll1", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_2", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_4", "pll1_2", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_4", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_8", "pll1_4", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_8", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_16", "pll1_8", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_16", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_6", "pll1_2", + CLK_SET_RATE_PARENT, 1, 3); + clk_register_clkdev(clk, "pll1_6", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_12", "pll1_6", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_12", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_24", "pll1_12", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_24", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_48", "pll1_24", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_48", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_96", "pll1_48", + CLK_SET_RATE_PARENT, 1, 2); + clk_register_clkdev(clk, "pll1_96", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_13", "pll1", + CLK_SET_RATE_PARENT, 1, 13); + clk_register_clkdev(clk, "pll1_13", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_13_1_5", "pll1", + CLK_SET_RATE_PARENT, 2, 3); + clk_register_clkdev(clk, "pll1_13_1_5", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_2_1_5", "pll1", + CLK_SET_RATE_PARENT, 2, 3); + clk_register_clkdev(clk, "pll1_2_1_5", NULL); + + clk = clk_register_fixed_factor(NULL, "pll1_3_16", "pll1", + CLK_SET_RATE_PARENT, 3, 16); + clk_register_clkdev(clk, "pll1_3_16", NULL); + + uart_pll = mmp_clk_register_factor("uart_pll", "pll1_4", 0, + mpmu_base + MPMU_UART_PLL, + &uart_factor_masks, uart_factor_tbl, + ARRAY_SIZE(uart_factor_tbl)); + clk_set_rate(uart_pll, 14745600); + clk_register_clkdev(uart_pll, "uart_pll", NULL); + + clk = mmp_clk_register_apbc("twsi0", "pll1_13_1_5", + apbc_base + APBC_TWSI0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.0"); + + clk = mmp_clk_register_apbc("twsi1", "pll1_13_1_5", + apbcp_base + APBCP_TWSI1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-i2c.1"); + + clk = mmp_clk_register_apbc("gpio", "vctcxo", + apbc_base + APBC_GPIO, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa-gpio"); + + clk = mmp_clk_register_apbc("kpc", "clk32", + apbc_base + APBC_KPC, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa27x-keypad"); + + clk = mmp_clk_register_apbc("rtc", "clk32", + apbc_base + APBC_RTC, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "sa1100-rtc"); + + clk = mmp_clk_register_apbc("pwm0", "pll1_48", + apbc_base + APBC_PWM0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa910-pwm.0"); + + clk = mmp_clk_register_apbc("pwm1", "pll1_48", + apbc_base + APBC_PWM1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa910-pwm.1"); + + clk = mmp_clk_register_apbc("pwm2", "pll1_48", + apbc_base + APBC_PWM2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa910-pwm.2"); + + clk = mmp_clk_register_apbc("pwm3", "pll1_48", + apbc_base + APBC_PWM3, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa910-pwm.3"); + + clk = clk_register_mux(NULL, "uart0_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART0, 4, 3, 0, &clk_lock); + clk_set_parent(clk, uart_pll); + clk_register_clkdev(clk, "uart_mux.0", NULL); + + clk = mmp_clk_register_apbc("uart0", "uart0_mux", + apbc_base + APBC_UART0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.0"); + + clk = clk_register_mux(NULL, "uart1_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_UART1, 4, 3, 0, &clk_lock); + clk_set_parent(clk, uart_pll); + clk_register_clkdev(clk, "uart_mux.1", NULL); + + clk = mmp_clk_register_apbc("uart1", "uart1_mux", + apbc_base + APBC_UART1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.1"); + + clk = clk_register_mux(NULL, "uart2_mux", uart_parent, + ARRAY_SIZE(uart_parent), CLK_SET_RATE_PARENT, + apbcp_base + APBCP_UART2, 4, 3, 0, &clk_lock); + clk_set_parent(clk, uart_pll); + clk_register_clkdev(clk, "uart_mux.2", NULL); + + clk = mmp_clk_register_apbc("uart2", "uart2_mux", + apbcp_base + APBCP_UART2, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa2xx-uart.2"); + + clk = clk_register_mux(NULL, "ssp0_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP0, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "uart_mux.0", NULL); + + clk = mmp_clk_register_apbc("ssp0", "ssp0_mux", + apbc_base + APBC_SSP0, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.0"); + + clk = clk_register_mux(NULL, "ssp1_mux", ssp_parent, + ARRAY_SIZE(ssp_parent), CLK_SET_RATE_PARENT, + apbc_base + APBC_SSP1, 4, 3, 0, &clk_lock); + clk_register_clkdev(clk, "ssp_mux.1", NULL); + + clk = mmp_clk_register_apbc("ssp1", "ssp1_mux", + apbc_base + APBC_SSP1, 10, 0, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-ssp.1"); + + clk = mmp_clk_register_apmu("dfc", "pll1_4", + apmu_base + APMU_DFC, 0x19b, &clk_lock); + clk_register_clkdev(clk, NULL, "pxa3xx-nand.0"); + + clk = clk_register_mux(NULL, "sdh0_mux", sdh_parent, + ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_SDH0, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "sdh0_mux", NULL); + + clk = mmp_clk_register_apmu("sdh0", "sdh_mux", + apmu_base + APMU_SDH0, 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxa.0"); + + clk = clk_register_mux(NULL, "sdh1_mux", sdh_parent, + ARRAY_SIZE(sdh_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_SDH1, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "sdh1_mux", NULL); + + clk = mmp_clk_register_apmu("sdh1", "sdh1_mux", + apmu_base + APMU_SDH1, 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "sdhci-pxa.1"); + + clk = mmp_clk_register_apmu("usb", "usb_pll", + apmu_base + APMU_USB, 0x9, &clk_lock); + clk_register_clkdev(clk, "usb_clk", NULL); + + clk = mmp_clk_register_apmu("sph", "usb_pll", + apmu_base + APMU_USB, 0x12, &clk_lock); + clk_register_clkdev(clk, "sph_clk", NULL); + + clk = clk_register_mux(NULL, "disp0_mux", disp_parent, + ARRAY_SIZE(disp_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_DISP0, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "disp_mux.0", NULL); + + clk = mmp_clk_register_apmu("disp0", "disp0_mux", + apmu_base + APMU_DISP0, 0x1b, &clk_lock); + clk_register_clkdev(clk, NULL, "mmp-disp.0"); + + clk = clk_register_mux(NULL, "ccic0_mux", ccic_parent, + ARRAY_SIZE(ccic_parent), CLK_SET_RATE_PARENT, + apmu_base + APMU_CCIC0, 6, 1, 0, &clk_lock); + clk_register_clkdev(clk, "ccic_mux.0", NULL); + + clk = mmp_clk_register_apmu("ccic0", "ccic0_mux", + apmu_base + APMU_CCIC0, 0x1b, &clk_lock); + clk_register_clkdev(clk, "fnclk", "mmp-ccic.0"); + + clk = clk_register_mux(NULL, "ccic0_phy_mux", ccic_phy_parent, + ARRAY_SIZE(ccic_phy_parent), + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, + 7, 1, 0, &clk_lock); + clk_register_clkdev(clk, "ccic_phy_mux.0", NULL); + + clk = mmp_clk_register_apmu("ccic0_phy", "ccic0_phy_mux", + apmu_base + APMU_CCIC0, 0x24, &clk_lock); + clk_register_clkdev(clk, "phyclk", "mmp-ccic.0"); + + clk = clk_register_divider(NULL, "ccic0_sphy_div", "ccic0_mux", + CLK_SET_RATE_PARENT, apmu_base + APMU_CCIC0, + 10, 5, 0, &clk_lock); + clk_register_clkdev(clk, "sphyclk_div", NULL); + + clk = mmp_clk_register_apmu("ccic0_sphy", "ccic0_sphy_div", + apmu_base + APMU_CCIC0, 0x300, &clk_lock); + clk_register_clkdev(clk, "sphyclk", "mmp-ccic.0"); +} diff --git a/drivers/clk/mmp/clk.h b/drivers/clk/mmp/clk.h new file mode 100644 index 000000000000..ab86dd4a416a --- /dev/null +++ b/drivers/clk/mmp/clk.h @@ -0,0 +1,35 @@ +#ifndef __MACH_MMP_CLK_H +#define __MACH_MMP_CLK_H + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> + +#define APBC_NO_BUS_CTRL BIT(0) +#define APBC_POWER_CTRL BIT(1) + +struct clk_factor_masks { + unsigned int factor; + unsigned int num_mask; + unsigned int den_mask; + unsigned int num_shift; + unsigned int den_shift; +}; + +struct clk_factor_tbl { + unsigned int num; + unsigned int den; +}; + +extern struct clk *mmp_clk_register_pll2(const char *name, + const char *parent_name, unsigned long flags); +extern struct clk *mmp_clk_register_apbc(const char *name, + const char *parent_name, void __iomem *base, + unsigned int delay, unsigned int apbc_flags, spinlock_t *lock); +extern struct clk *mmp_clk_register_apmu(const char *name, + const char *parent_name, void __iomem *base, u32 enable_mask, + spinlock_t *lock); +extern struct clk *mmp_clk_register_factor(const char *name, + const char *parent_name, unsigned long flags, + void __iomem *base, struct clk_factor_masks *masks, + struct clk_factor_tbl *ftbl, unsigned int ftbl_cnt); +#endif 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/clk/ux500/Makefile b/drivers/clk/ux500/Makefile new file mode 100644 index 000000000000..858fbfe66281 --- /dev/null +++ b/drivers/clk/ux500/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for ux500 clocks +# + +# Clock types +obj-y += clk-prcc.o +obj-y += clk-prcmu.o + +# Clock definitions +obj-y += u8500_clk.o +obj-y += u9540_clk.o +obj-y += u8540_clk.o diff --git a/drivers/clk/ux500/clk-prcc.c b/drivers/clk/ux500/clk-prcc.c new file mode 100644 index 000000000000..7eee7f768355 --- /dev/null +++ b/drivers/clk/ux500/clk-prcc.c @@ -0,0 +1,164 @@ +/* + * PRCC clock implementation for ux500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk-provider.h> +#include <linux/clk-private.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/types.h> +#include <mach/hardware.h> + +#include "clk.h" + +#define PRCC_PCKEN 0x000 +#define PRCC_PCKDIS 0x004 +#define PRCC_KCKEN 0x008 +#define PRCC_KCKDIS 0x00C +#define PRCC_PCKSR 0x010 +#define PRCC_KCKSR 0x014 + +#define to_clk_prcc(_hw) container_of(_hw, struct clk_prcc, hw) + +struct clk_prcc { + struct clk_hw hw; + void __iomem *base; + u32 cg_sel; + int is_enabled; +}; + +/* PRCC clock operations. */ + +static int clk_prcc_pclk_enable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_PCKEN)); + while (!(readl(clk->base + PRCC_PCKSR) & clk->cg_sel)) + cpu_relax(); + + clk->is_enabled = 1; + return 0; +} + +static void clk_prcc_pclk_disable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_PCKDIS)); + clk->is_enabled = 0; +} + +static int clk_prcc_kclk_enable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_KCKEN)); + while (!(readl(clk->base + PRCC_KCKSR) & clk->cg_sel)) + cpu_relax(); + + clk->is_enabled = 1; + return 0; +} + +static void clk_prcc_kclk_disable(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + + writel(clk->cg_sel, (clk->base + PRCC_KCKDIS)); + clk->is_enabled = 0; +} + +static int clk_prcc_is_enabled(struct clk_hw *hw) +{ + struct clk_prcc *clk = to_clk_prcc(hw); + return clk->is_enabled; +} + +static struct clk_ops clk_prcc_pclk_ops = { + .enable = clk_prcc_pclk_enable, + .disable = clk_prcc_pclk_disable, + .is_enabled = clk_prcc_is_enabled, +}; + +static struct clk_ops clk_prcc_kclk_ops = { + .enable = clk_prcc_kclk_enable, + .disable = clk_prcc_kclk_disable, + .is_enabled = clk_prcc_is_enabled, +}; + +static struct clk *clk_reg_prcc(const char *name, + const char *parent_name, + resource_size_t phy_base, + u32 cg_sel, + unsigned long flags, + struct clk_ops *clk_prcc_ops) +{ + struct clk_prcc *clk; + struct clk_init_data clk_prcc_init; + struct clk *clk_reg; + + if (!name) { + pr_err("clk_prcc: %s invalid arguments passed\n", __func__); + return ERR_PTR(-EINVAL); + } + + clk = kzalloc(sizeof(struct clk_prcc), GFP_KERNEL); + if (!clk) { + pr_err("clk_prcc: %s could not allocate clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + clk->base = ioremap(phy_base, SZ_4K); + if (!clk->base) + goto free_clk; + + clk->cg_sel = cg_sel; + clk->is_enabled = 1; + + clk_prcc_init.name = name; + clk_prcc_init.ops = clk_prcc_ops; + clk_prcc_init.flags = flags; + clk_prcc_init.parent_names = (parent_name ? &parent_name : NULL); + clk_prcc_init.num_parents = (parent_name ? 1 : 0); + clk->hw.init = &clk_prcc_init; + + clk_reg = clk_register(NULL, &clk->hw); + if (IS_ERR_OR_NULL(clk_reg)) + goto unmap_clk; + + return clk_reg; + +unmap_clk: + iounmap(clk->base); +free_clk: + kfree(clk); + pr_err("clk_prcc: %s failed to register clk\n", __func__); + return ERR_PTR(-ENOMEM); +} + +struct clk *clk_reg_prcc_pclk(const char *name, + const char *parent_name, + resource_size_t phy_base, + u32 cg_sel, + unsigned long flags) +{ + return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags, + &clk_prcc_pclk_ops); +} + +struct clk *clk_reg_prcc_kclk(const char *name, + const char *parent_name, + resource_size_t phy_base, + u32 cg_sel, + unsigned long flags) +{ + return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags, + &clk_prcc_kclk_ops); +} diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c new file mode 100644 index 000000000000..930cdfeb47ab --- /dev/null +++ b/drivers/clk/ux500/clk-prcmu.c @@ -0,0 +1,252 @@ +/* + * PRCMU clock implementation for ux500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk-provider.h> +#include <linux/clk-private.h> +#include <linux/mfd/dbx500-prcmu.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include "clk.h" + +#define to_clk_prcmu(_hw) container_of(_hw, struct clk_prcmu, hw) + +struct clk_prcmu { + struct clk_hw hw; + u8 cg_sel; + int is_enabled; +}; + +/* PRCMU clock operations. */ + +static int clk_prcmu_prepare(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_request_clock(clk->cg_sel, true); +} + +static void clk_prcmu_unprepare(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + if (prcmu_request_clock(clk->cg_sel, false)) + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); +} + +static int clk_prcmu_enable(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + clk->is_enabled = 1; + return 0; +} + +static void clk_prcmu_disable(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + clk->is_enabled = 0; +} + +static int clk_prcmu_is_enabled(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return clk->is_enabled; +} + +static unsigned long clk_prcmu_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_clock_rate(clk->cg_sel); +} + +static long clk_prcmu_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_round_clock_rate(clk->cg_sel, rate); +} + +static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return prcmu_set_clock_rate(clk->cg_sel, rate); +} + +static int request_ape_opp100(bool enable) +{ + static int reqs; + int err = 0; + + if (enable) { + if (!reqs) + err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, + "clock", 100); + if (!err) + reqs++; + } else { + reqs--; + if (!reqs) + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + "clock"); + } + return err; +} + +static int clk_prcmu_opp_prepare(struct clk_hw *hw) +{ + int err; + struct clk_prcmu *clk = to_clk_prcmu(hw); + + err = request_ape_opp100(true); + if (err) { + pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", + __func__, hw->init->name); + return err; + } + + err = prcmu_request_clock(clk->cg_sel, true); + if (err) + request_ape_opp100(false); + + return err; +} + +static void clk_prcmu_opp_unprepare(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + + if (prcmu_request_clock(clk->cg_sel, false)) + goto out_error; + if (request_ape_opp100(false)) + goto out_error; + return; + +out_error: + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); +} + +static struct clk_ops clk_prcmu_scalable_ops = { + .prepare = clk_prcmu_prepare, + .unprepare = clk_prcmu_unprepare, + .enable = clk_prcmu_enable, + .disable = clk_prcmu_disable, + .is_enabled = clk_prcmu_is_enabled, + .recalc_rate = clk_prcmu_recalc_rate, + .round_rate = clk_prcmu_round_rate, + .set_rate = clk_prcmu_set_rate, +}; + +static struct clk_ops clk_prcmu_gate_ops = { + .prepare = clk_prcmu_prepare, + .unprepare = clk_prcmu_unprepare, + .enable = clk_prcmu_enable, + .disable = clk_prcmu_disable, + .is_enabled = clk_prcmu_is_enabled, + .recalc_rate = clk_prcmu_recalc_rate, +}; + +static struct clk_ops clk_prcmu_rate_ops = { + .is_enabled = clk_prcmu_is_enabled, + .recalc_rate = clk_prcmu_recalc_rate, +}; + +static struct clk_ops clk_prcmu_opp_gate_ops = { + .prepare = clk_prcmu_opp_prepare, + .unprepare = clk_prcmu_opp_unprepare, + .enable = clk_prcmu_enable, + .disable = clk_prcmu_disable, + .is_enabled = clk_prcmu_is_enabled, + .recalc_rate = clk_prcmu_recalc_rate, +}; + +static struct clk *clk_reg_prcmu(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long rate, + unsigned long flags, + struct clk_ops *clk_prcmu_ops) +{ + struct clk_prcmu *clk; + struct clk_init_data clk_prcmu_init; + struct clk *clk_reg; + + if (!name) { + pr_err("clk_prcmu: %s invalid arguments passed\n", __func__); + return ERR_PTR(-EINVAL); + } + + clk = kzalloc(sizeof(struct clk_prcmu), GFP_KERNEL); + if (!clk) { + pr_err("clk_prcmu: %s could not allocate clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + clk->cg_sel = cg_sel; + clk->is_enabled = 1; + /* "rate" can be used for changing the initial frequency */ + if (rate) + prcmu_set_clock_rate(cg_sel, rate); + + clk_prcmu_init.name = name; + clk_prcmu_init.ops = clk_prcmu_ops; + clk_prcmu_init.flags = flags; + clk_prcmu_init.parent_names = (parent_name ? &parent_name : NULL); + clk_prcmu_init.num_parents = (parent_name ? 1 : 0); + clk->hw.init = &clk_prcmu_init; + + clk_reg = clk_register(NULL, &clk->hw); + if (IS_ERR_OR_NULL(clk_reg)) + goto free_clk; + + return clk_reg; + +free_clk: + kfree(clk); + pr_err("clk_prcmu: %s failed to register clk\n", __func__); + return ERR_PTR(-ENOMEM); +} + +struct clk *clk_reg_prcmu_scalable(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long rate, + unsigned long flags) +{ + return clk_reg_prcmu(name, parent_name, cg_sel, rate, flags, + &clk_prcmu_scalable_ops); +} + +struct clk *clk_reg_prcmu_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags) +{ + return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, + &clk_prcmu_gate_ops); +} + +struct clk *clk_reg_prcmu_rate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags) +{ + return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, + &clk_prcmu_rate_ops); +} + +struct clk *clk_reg_prcmu_opp_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags) +{ + return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags, + &clk_prcmu_opp_gate_ops); +} diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h new file mode 100644 index 000000000000..836d7d16751e --- /dev/null +++ b/drivers/clk/ux500/clk.h @@ -0,0 +1,48 @@ +/* + * Clocks for ux500 platforms + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#ifndef __UX500_CLK_H +#define __UX500_CLK_H + +#include <linux/clk.h> + +struct clk *clk_reg_prcc_pclk(const char *name, + const char *parent_name, + unsigned int phy_base, + u32 cg_sel, + unsigned long flags); + +struct clk *clk_reg_prcc_kclk(const char *name, + const char *parent_name, + unsigned int phy_base, + u32 cg_sel, + unsigned long flags); + +struct clk *clk_reg_prcmu_scalable(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long rate, + unsigned long flags); + +struct clk *clk_reg_prcmu_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags); + +struct clk *clk_reg_prcmu_rate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags); + +struct clk *clk_reg_prcmu_opp_gate(const char *name, + const char *parent_name, + u8 cg_sel, + unsigned long flags); + +#endif /* __UX500_CLK_H */ diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c new file mode 100644 index 000000000000..ca4a25ed844c --- /dev/null +++ b/drivers/clk/ux500/u8500_clk.c @@ -0,0 +1,477 @@ +/* + * Clock definitions for u8500 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/mfd/dbx500-prcmu.h> +#include <linux/platform_data/clk-ux500.h> + +#include "clk.h" + +void u8500_clk_init(void) +{ + struct prcmu_fw_version *fw_version; + const char *sgaclk_parent = NULL; + struct clk *clk; + + /* Clock sources */ + clk = clk_reg_prcmu_gate("soc0_pll", NULL, PRCMU_PLLSOC0, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc0_pll", NULL); + + clk = clk_reg_prcmu_gate("soc1_pll", NULL, PRCMU_PLLSOC1, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "soc1_pll", NULL); + + clk = clk_reg_prcmu_gate("ddr_pll", NULL, PRCMU_PLLDDR, + CLK_IS_ROOT|CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, "ddr_pll", NULL); + + /* FIXME: Add sys, ulp and int clocks here. */ + + clk = clk_register_fixed_rate(NULL, "rtc32k", "NULL", + CLK_IS_ROOT|CLK_IGNORE_UNUSED, + 32768); + clk_register_clkdev(clk, "clk32k", NULL); + clk_register_clkdev(clk, NULL, "rtc-pl031"); + + /* PRCMU clocks */ + fw_version = prcmu_get_fw_version(); + if (fw_version != NULL) { + switch (fw_version->project) { + case PRCMU_FW_PROJECT_U8500_C2: + case PRCMU_FW_PROJECT_U8520: + case PRCMU_FW_PROJECT_U8420: + sgaclk_parent = "soc0_pll"; + break; + default: + break; + } + } + + if (sgaclk_parent) + clk = clk_reg_prcmu_gate("sgclk", sgaclk_parent, + PRCMU_SGACLK, 0); + else + clk = clk_reg_prcmu_gate("sgclk", NULL, + PRCMU_SGACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mali"); + + clk = clk_reg_prcmu_gate("uartclk", NULL, PRCMU_UARTCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "UART"); + + clk = clk_reg_prcmu_gate("msp02clk", NULL, PRCMU_MSP02CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "MSP02"); + + clk = clk_reg_prcmu_gate("msp1clk", NULL, PRCMU_MSP1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "MSP1"); + + clk = clk_reg_prcmu_gate("i2cclk", NULL, PRCMU_I2CCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "I2C"); + + clk = clk_reg_prcmu_gate("slimclk", NULL, PRCMU_SLIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "slim"); + + clk = clk_reg_prcmu_gate("per1clk", NULL, PRCMU_PER1CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH1"); + + clk = clk_reg_prcmu_gate("per2clk", NULL, PRCMU_PER2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH2"); + + clk = clk_reg_prcmu_gate("per3clk", NULL, PRCMU_PER3CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH3"); + + clk = clk_reg_prcmu_gate("per5clk", NULL, PRCMU_PER5CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH5"); + + clk = clk_reg_prcmu_gate("per6clk", NULL, PRCMU_PER6CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH6"); + + clk = clk_reg_prcmu_gate("per7clk", NULL, PRCMU_PER7CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "PERIPH7"); + + clk = clk_reg_prcmu_scalable("lcdclk", NULL, PRCMU_LCDCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "lcd"); + clk_register_clkdev(clk, "lcd", "mcde"); + + clk = clk_reg_prcmu_opp_gate("bmlclk", NULL, PRCMU_BMLCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "bml"); + + clk = clk_reg_prcmu_scalable("hsitxclk", NULL, PRCMU_HSITXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hsirxclk", NULL, PRCMU_HSIRXCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + + clk = clk_reg_prcmu_scalable("hdmiclk", NULL, PRCMU_HDMICLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "hdmi"); + clk_register_clkdev(clk, "hdmi", "mcde"); + + clk = clk_reg_prcmu_gate("apeatclk", NULL, PRCMU_APEATCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apeat"); + + clk = clk_reg_prcmu_gate("apetraceclk", NULL, PRCMU_APETRACECLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "apetrace"); + + clk = clk_reg_prcmu_gate("mcdeclk", NULL, PRCMU_MCDECLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mcde"); + clk_register_clkdev(clk, "mcde", "mcde"); + clk_register_clkdev(clk, "dsisys", "dsilink.0"); + clk_register_clkdev(clk, "dsisys", "dsilink.1"); + clk_register_clkdev(clk, "dsisys", "dsilink.2"); + + clk = clk_reg_prcmu_opp_gate("ipi2cclk", NULL, PRCMU_IPI2CCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "ipi2"); + + clk = clk_reg_prcmu_gate("dsialtclk", NULL, PRCMU_DSIALTCLK, + CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dsialt"); + + clk = clk_reg_prcmu_gate("dmaclk", NULL, PRCMU_DMACLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "dma40.0"); + + clk = clk_reg_prcmu_gate("b2r2clk", NULL, PRCMU_B2R2CLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "b2r2"); + clk_register_clkdev(clk, NULL, "b2r2_core"); + clk_register_clkdev(clk, NULL, "U8500-B2R2.0"); + + clk = clk_reg_prcmu_scalable("tvclk", NULL, PRCMU_TVCLK, 0, + CLK_IS_ROOT|CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "tv"); + clk_register_clkdev(clk, "tv", "mcde"); + + clk = clk_reg_prcmu_gate("sspclk", NULL, PRCMU_SSPCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "SSP"); + + clk = clk_reg_prcmu_gate("rngclk", NULL, PRCMU_RNGCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "rngclk"); + + clk = clk_reg_prcmu_gate("uiccclk", NULL, PRCMU_UICCCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "uicc"); + + /* + * FIXME: The MTU clocks might need some kind of "parent muxed join" + * and these have no K-clocks. For now, we ignore the missing + * connection to the corresponding P-clocks, p6_mtu0_clk and + * p6_mtu1_clk. Instead timclk is used which is the valid parent. + */ + clk = clk_reg_prcmu_gate("timclk", NULL, PRCMU_TIMCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "mtu0"); + clk_register_clkdev(clk, NULL, "mtu1"); + + clk = clk_reg_prcmu_gate("sdmmcclk", NULL, PRCMU_SDMMCCLK, CLK_IS_ROOT); + clk_register_clkdev(clk, NULL, "sdmmc"); + + + clk = clk_reg_prcmu_scalable("dsi_pll", "hdmiclk", + PRCMU_PLLDSI, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs2", "mcde"); + clk_register_clkdev(clk, "dsihs2", "dsilink.2"); + + + clk = clk_reg_prcmu_scalable("dsi0clk", "dsi_pll", + PRCMU_DSI0CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs0", "mcde"); + clk_register_clkdev(clk, "dsihs0", "dsilink.0"); + + clk = clk_reg_prcmu_scalable("dsi1clk", "dsi_pll", + PRCMU_DSI1CLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsihs1", "mcde"); + clk_register_clkdev(clk, "dsihs1", "dsilink.1"); + + clk = clk_reg_prcmu_scalable("dsi0escclk", "tvclk", + PRCMU_DSI0ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilp0", "dsilink.0"); + clk_register_clkdev(clk, "dsilp0", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi1escclk", "tvclk", + PRCMU_DSI1ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilp1", "dsilink.1"); + clk_register_clkdev(clk, "dsilp1", "mcde"); + + clk = clk_reg_prcmu_scalable("dsi2escclk", "tvclk", + PRCMU_DSI2ESCCLK, 0, CLK_SET_RATE_GATE); + clk_register_clkdev(clk, "dsilp2", "dsilink.2"); + clk_register_clkdev(clk, "dsilp2", "mcde"); + + clk = clk_reg_prcmu_rate("smp_twd", NULL, PRCMU_ARMSS, + CLK_IS_ROOT|CLK_GET_RATE_NOCACHE| + CLK_IGNORE_UNUSED); + clk_register_clkdev(clk, NULL, "smp_twd"); + + /* + * FIXME: Add special handled PRCMU clocks here: + * 1. clk_arm, use PRCMU_ARMCLK. + * 2. clkout0yuv, use PRCMU as parent + need regulator + pinctrl. + * 3. ab9540_clkout1yuv, see clkout0yuv + */ + + /* PRCC P-clocks */ + clk = clk_reg_prcc_pclk("p1_pclk0", "per1clk", U8500_CLKRST1_BASE, + BIT(0), 0); + clk_register_clkdev(clk, "apb_pclk", "uart0"); + + clk = clk_reg_prcc_pclk("p1_pclk1", "per1clk", U8500_CLKRST1_BASE, + BIT(1), 0); + clk_register_clkdev(clk, "apb_pclk", "uart1"); + + clk = clk_reg_prcc_pclk("p1_pclk2", "per1clk", U8500_CLKRST1_BASE, + BIT(2), 0); + clk = clk_reg_prcc_pclk("p1_pclk3", "per1clk", U8500_CLKRST1_BASE, + BIT(3), 0); + clk = clk_reg_prcc_pclk("p1_pclk4", "per1clk", U8500_CLKRST1_BASE, + BIT(4), 0); + + clk = clk_reg_prcc_pclk("p1_pclk5", "per1clk", U8500_CLKRST1_BASE, + BIT(5), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi0"); + + clk = clk_reg_prcc_pclk("p1_pclk6", "per1clk", U8500_CLKRST1_BASE, + BIT(6), 0); + + clk = clk_reg_prcc_pclk("p1_pclk7", "per1clk", U8500_CLKRST1_BASE, + BIT(7), 0); + clk_register_clkdev(clk, NULL, "spi3"); + + clk = clk_reg_prcc_pclk("p1_pclk8", "per1clk", U8500_CLKRST1_BASE, + BIT(8), 0); + + clk = clk_reg_prcc_pclk("p1_pclk9", "per1clk", U8500_CLKRST1_BASE, + BIT(9), 0); + clk_register_clkdev(clk, NULL, "gpio.0"); + clk_register_clkdev(clk, NULL, "gpio.1"); + clk_register_clkdev(clk, NULL, "gpioblock0"); + + clk = clk_reg_prcc_pclk("p1_pclk10", "per1clk", U8500_CLKRST1_BASE, + BIT(10), 0); + clk = clk_reg_prcc_pclk("p1_pclk11", "per1clk", U8500_CLKRST1_BASE, + BIT(11), 0); + + clk = clk_reg_prcc_pclk("p2_pclk0", "per2clk", U8500_CLKRST2_BASE, + BIT(0), 0); + + clk = clk_reg_prcc_pclk("p2_pclk1", "per2clk", U8500_CLKRST2_BASE, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "spi2"); + + clk = clk_reg_prcc_pclk("p2_pclk2", "per2clk", U8500_CLKRST2_BASE, + BIT(2), 0); + clk_register_clkdev(clk, NULL, "spi1"); + + clk = clk_reg_prcc_pclk("p2_pclk3", "per2clk", U8500_CLKRST2_BASE, + BIT(3), 0); + clk_register_clkdev(clk, NULL, "pwl"); + + clk = clk_reg_prcc_pclk("p2_pclk4", "per2clk", U8500_CLKRST2_BASE, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi4"); + + clk = clk_reg_prcc_pclk("p2_pclk5", "per2clk", U8500_CLKRST2_BASE, + BIT(5), 0); + + clk = clk_reg_prcc_pclk("p2_pclk6", "per2clk", U8500_CLKRST2_BASE, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi1"); + + + clk = clk_reg_prcc_pclk("p2_pclk7", "per2clk", U8500_CLKRST2_BASE, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi3"); + + clk = clk_reg_prcc_pclk("p2_pclk8", "per2clk", U8500_CLKRST2_BASE, + BIT(8), 0); + clk_register_clkdev(clk, NULL, "spi0"); + + clk = clk_reg_prcc_pclk("p2_pclk9", "per2clk", U8500_CLKRST2_BASE, + BIT(9), 0); + clk_register_clkdev(clk, "hsir_hclk", "ste_hsi.0"); + + clk = clk_reg_prcc_pclk("p2_pclk10", "per2clk", U8500_CLKRST2_BASE, + BIT(10), 0); + clk_register_clkdev(clk, "hsit_hclk", "ste_hsi.0"); + + clk = clk_reg_prcc_pclk("p2_pclk11", "per2clk", U8500_CLKRST2_BASE, + BIT(11), 0); + clk_register_clkdev(clk, NULL, "gpio.6"); + clk_register_clkdev(clk, NULL, "gpio.7"); + clk_register_clkdev(clk, NULL, "gpioblock1"); + + clk = clk_reg_prcc_pclk("p2_pclk12", "per2clk", U8500_CLKRST2_BASE, + BIT(11), 0); + + clk = clk_reg_prcc_pclk("p3_pclk0", "per3clk", U8500_CLKRST3_BASE, + BIT(0), 0); + clk_register_clkdev(clk, NULL, "fsmc"); + + clk = clk_reg_prcc_pclk("p3_pclk1", "per3clk", U8500_CLKRST3_BASE, + BIT(1), 0); + clk = clk_reg_prcc_pclk("p3_pclk2", "per3clk", U8500_CLKRST3_BASE, + BIT(2), 0); + clk = clk_reg_prcc_pclk("p3_pclk3", "per3clk", U8500_CLKRST3_BASE, + BIT(3), 0); + + clk = clk_reg_prcc_pclk("p3_pclk4", "per3clk", U8500_CLKRST3_BASE, + BIT(4), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi2"); + + clk = clk_reg_prcc_pclk("p3_pclk5", "per3clk", U8500_CLKRST3_BASE, + BIT(5), 0); + + clk = clk_reg_prcc_pclk("p3_pclk6", "per3clk", U8500_CLKRST3_BASE, + BIT(6), 0); + clk_register_clkdev(clk, "apb_pclk", "uart2"); + + clk = clk_reg_prcc_pclk("p3_pclk7", "per3clk", U8500_CLKRST3_BASE, + BIT(7), 0); + clk_register_clkdev(clk, "apb_pclk", "sdi5"); + + clk = clk_reg_prcc_pclk("p3_pclk8", "per3clk", U8500_CLKRST3_BASE, + BIT(8), 0); + clk_register_clkdev(clk, NULL, "gpio.2"); + clk_register_clkdev(clk, NULL, "gpio.3"); + clk_register_clkdev(clk, NULL, "gpio.4"); + clk_register_clkdev(clk, NULL, "gpio.5"); + clk_register_clkdev(clk, NULL, "gpioblock2"); + + clk = clk_reg_prcc_pclk("p5_pclk0", "per5clk", U8500_CLKRST5_BASE, + BIT(0), 0); + clk_register_clkdev(clk, "usb", "musb-ux500.0"); + + clk = clk_reg_prcc_pclk("p5_pclk1", "per5clk", U8500_CLKRST5_BASE, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "gpio.8"); + clk_register_clkdev(clk, NULL, "gpioblock3"); + + clk = clk_reg_prcc_pclk("p6_pclk0", "per6clk", U8500_CLKRST6_BASE, + BIT(0), 0); + + clk = clk_reg_prcc_pclk("p6_pclk1", "per6clk", U8500_CLKRST6_BASE, + BIT(1), 0); + clk_register_clkdev(clk, NULL, "cryp0"); + clk_register_clkdev(clk, NULL, "cryp1"); + + clk = clk_reg_prcc_pclk("p6_pclk2", "per6clk", U8500_CLKRST6_BASE, + BIT(2), 0); + clk_register_clkdev(clk, NULL, "hash0"); + + clk = clk_reg_prcc_pclk("p6_pclk3", "per6clk", U8500_CLKRST6_BASE, + BIT(3), 0); + clk_register_clkdev(clk, NULL, "pka"); + + clk = clk_reg_prcc_pclk("p6_pclk4", "per6clk", U8500_CLKRST6_BASE, + BIT(4), 0); + clk_register_clkdev(clk, NULL, "hash1"); + + clk = clk_reg_prcc_pclk("p6_pclk5", "per6clk", U8500_CLKRST6_BASE, + BIT(5), 0); + clk_register_clkdev(clk, NULL, "cfgreg"); + + clk = clk_reg_prcc_pclk("p6_pclk6", "per6clk", U8500_CLKRST6_BASE, + BIT(6), 0); + clk = clk_reg_prcc_pclk("p6_pclk7", "per6clk", U8500_CLKRST6_BASE, + BIT(7), 0); + + /* PRCC K-clocks + * + * FIXME: Some drivers requires PERPIH[n| to be automatically enabled + * by enabling just the K-clock, even if it is not a valid parent to + * the K-clock. Until drivers get fixed we might need some kind of + * "parent muxed join". + */ + + /* Periph1 */ + clk = clk_reg_prcc_kclk("p1_uart0_kclk", "uartclk", + U8500_CLKRST1_BASE, BIT(0), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart0"); + + clk = clk_reg_prcc_kclk("p1_uart1_kclk", "uartclk", + U8500_CLKRST1_BASE, BIT(1), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart1"); + + clk = clk_reg_prcc_kclk("p1_i2c1_kclk", "i2cclk", + U8500_CLKRST1_BASE, BIT(2), CLK_SET_RATE_GATE); + clk = clk_reg_prcc_kclk("p1_msp0_kclk", "msp02clk", + U8500_CLKRST1_BASE, BIT(3), CLK_SET_RATE_GATE); + clk = clk_reg_prcc_kclk("p1_msp1_kclk", "msp1clk", + U8500_CLKRST1_BASE, BIT(4), CLK_SET_RATE_GATE); + + clk = clk_reg_prcc_kclk("p1_sdi0_kclk", "sdmmcclk", + U8500_CLKRST1_BASE, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi0"); + + clk = clk_reg_prcc_kclk("p1_i2c2_kclk", "i2cclk", + U8500_CLKRST1_BASE, BIT(6), CLK_SET_RATE_GATE); + clk = clk_reg_prcc_kclk("p1_slimbus0_kclk", "slimclk", + U8500_CLKRST1_BASE, BIT(3), CLK_SET_RATE_GATE); + /* FIXME: Redefinition of BIT(3). */ + clk = clk_reg_prcc_kclk("p1_i2c4_kclk", "i2cclk", + U8500_CLKRST1_BASE, BIT(9), CLK_SET_RATE_GATE); + clk = clk_reg_prcc_kclk("p1_msp3_kclk", "msp1clk", + U8500_CLKRST1_BASE, BIT(10), CLK_SET_RATE_GATE); + + /* Periph2 */ + clk = clk_reg_prcc_kclk("p2_i2c3_kclk", "i2cclk", + U8500_CLKRST2_BASE, BIT(0), CLK_SET_RATE_GATE); + + clk = clk_reg_prcc_kclk("p2_sdi4_kclk", "sdmmcclk", + U8500_CLKRST2_BASE, BIT(2), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi4"); + + clk = clk_reg_prcc_kclk("p2_msp2_kclk", "msp02clk", + U8500_CLKRST2_BASE, BIT(3), CLK_SET_RATE_GATE); + + clk = clk_reg_prcc_kclk("p2_sdi1_kclk", "sdmmcclk", + U8500_CLKRST2_BASE, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi1"); + + clk = clk_reg_prcc_kclk("p2_sdi3_kclk", "sdmmcclk", + U8500_CLKRST2_BASE, BIT(5), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi3"); + + /* Note that rate is received from parent. */ + clk = clk_reg_prcc_kclk("p2_ssirx_kclk", "hsirxclk", + U8500_CLKRST2_BASE, BIT(6), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + clk = clk_reg_prcc_kclk("p2_ssitx_kclk", "hsitxclk", + U8500_CLKRST2_BASE, BIT(7), + CLK_SET_RATE_GATE|CLK_SET_RATE_PARENT); + + /* Periph3 */ + clk = clk_reg_prcc_kclk("p3_ssp0_kclk", "sspclk", + U8500_CLKRST3_BASE, BIT(1), CLK_SET_RATE_GATE); + clk = clk_reg_prcc_kclk("p3_ssp1_kclk", "sspclk", + U8500_CLKRST3_BASE, BIT(2), CLK_SET_RATE_GATE); + clk = clk_reg_prcc_kclk("p3_i2c0_kclk", "i2cclk", + U8500_CLKRST3_BASE, BIT(3), CLK_SET_RATE_GATE); + + clk = clk_reg_prcc_kclk("p3_sdi2_kclk", "sdmmcclk", + U8500_CLKRST3_BASE, BIT(4), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi2"); + + clk = clk_reg_prcc_kclk("p3_ske_kclk", "rtc32k", + U8500_CLKRST3_BASE, BIT(5), CLK_SET_RATE_GATE); + + clk = clk_reg_prcc_kclk("p3_uart2_kclk", "uartclk", + U8500_CLKRST3_BASE, BIT(6), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "uart2"); + + clk = clk_reg_prcc_kclk("p3_sdi5_kclk", "sdmmcclk", + U8500_CLKRST3_BASE, BIT(7), CLK_SET_RATE_GATE); + clk_register_clkdev(clk, NULL, "sdi5"); + + /* Periph6 */ + clk = clk_reg_prcc_kclk("p3_rng_kclk", "rngclk", + U8500_CLKRST6_BASE, BIT(0), CLK_SET_RATE_GATE); + +} diff --git a/drivers/clk/ux500/u8540_clk.c b/drivers/clk/ux500/u8540_clk.c new file mode 100644 index 000000000000..10adfd2ead21 --- /dev/null +++ b/drivers/clk/ux500/u8540_clk.c @@ -0,0 +1,21 @@ +/* + * Clock definitions for u8540 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/mfd/dbx500-prcmu.h> +#include <linux/platform_data/clk-ux500.h> + +#include "clk.h" + +void u8540_clk_init(void) +{ + /* register clocks here */ +} diff --git a/drivers/clk/ux500/u9540_clk.c b/drivers/clk/ux500/u9540_clk.c new file mode 100644 index 000000000000..dbc0191e16c8 --- /dev/null +++ b/drivers/clk/ux500/u9540_clk.c @@ -0,0 +1,21 @@ +/* + * Clock definitions for u9540 platform. + * + * Copyright (C) 2012 ST-Ericsson SA + * Author: Ulf Hansson <ulf.hansson@linaro.org> + * + * License terms: GNU General Public License (GPL) version 2 + */ + +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/clk-provider.h> +#include <linux/mfd/dbx500-prcmu.h> +#include <linux/platform_data/clk-ux500.h> + +#include "clk.h" + +void u9540_clk_init(void) +{ + /* register clocks here */ +} diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile index 50cf6a2ee693..c0a0f6478798 100644 --- a/drivers/clk/versatile/Makefile +++ b/drivers/clk/versatile/Makefile @@ -1,3 +1,4 @@ # Makefile for Versatile-specific clocks obj-$(CONFIG_ICST) += clk-icst.o obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o +obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o diff --git a/drivers/clk/versatile/clk-realview.c b/drivers/clk/versatile/clk-realview.c new file mode 100644 index 000000000000..e21a99cef378 --- /dev/null +++ b/drivers/clk/versatile/clk-realview.c @@ -0,0 +1,114 @@ +#include <linux/clk.h> +#include <linux/clkdev.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/clk-provider.h> + +#include <mach/hardware.h> +#include <mach/platform.h> + +#include "clk-icst.h" + +/* + * Implementation of the ARM RealView clock trees. + */ + +static void __iomem *sys_lock; +static void __iomem *sys_vcoreg; + +/** + * realview_oscvco_get() - get ICST OSC settings for the RealView + */ +static struct icst_vco realview_oscvco_get(void) +{ + u32 val; + struct icst_vco vco; + + val = readl(sys_vcoreg); + vco.v = val & 0x1ff; + vco.r = (val >> 9) & 0x7f; + vco.s = (val >> 16) & 03; + return vco; +} + +static void realview_oscvco_set(struct icst_vco vco) +{ + u32 val; + + val = readl(sys_vcoreg) & ~0x7ffff; + val |= vco.v | (vco.r << 9) | (vco.s << 16); + + /* This magic unlocks the CM VCO so it can be controlled */ + writel(0xa05f, sys_lock); + writel(val, sys_vcoreg); + /* This locks the CM again */ + writel(0, sys_lock); +} + +static const struct icst_params realview_oscvco_params = { + .ref = 24000000, + .vco_max = ICST307_VCO_MAX, + .vco_min = ICST307_VCO_MIN, + .vd_min = 4 + 8, + .vd_max = 511 + 8, + .rd_min = 1 + 2, + .rd_max = 127 + 2, + .s2div = icst307_s2div, + .idx2s = icst307_idx2s, +}; + +static const struct clk_icst_desc __initdata realview_icst_desc = { + .params = &realview_oscvco_params, + .getvco = realview_oscvco_get, + .setvco = realview_oscvco_set, +}; + +/* + * realview_clk_init() - set up the RealView clock tree + */ +void __init realview_clk_init(void __iomem *sysbase, bool is_pb1176) +{ + struct clk *clk; + + sys_lock = sysbase + REALVIEW_SYS_LOCK_OFFSET; + if (is_pb1176) + sys_vcoreg = sysbase + REALVIEW_SYS_OSC0_OFFSET; + else + sys_vcoreg = sysbase + REALVIEW_SYS_OSC4_OFFSET; + + + /* APB clock dummy */ + clk = clk_register_fixed_rate(NULL, "apb_pclk", NULL, CLK_IS_ROOT, 0); + clk_register_clkdev(clk, "apb_pclk", NULL); + + /* 24 MHz clock */ + clk = clk_register_fixed_rate(NULL, "clk24mhz", NULL, CLK_IS_ROOT, + 24000000); + clk_register_clkdev(clk, NULL, "dev:uart0"); + clk_register_clkdev(clk, NULL, "dev:uart1"); + clk_register_clkdev(clk, NULL, "dev:uart2"); + clk_register_clkdev(clk, NULL, "fpga:kmi0"); + clk_register_clkdev(clk, NULL, "fpga:kmi1"); + clk_register_clkdev(clk, NULL, "fpga:mmc0"); + clk_register_clkdev(clk, NULL, "dev:ssp0"); + if (is_pb1176) { + /* + * UART3 is on the dev chip in PB1176 + * UART4 only exists in PB1176 + */ + clk_register_clkdev(clk, NULL, "dev:uart3"); + clk_register_clkdev(clk, NULL, "dev:uart4"); + } else + clk_register_clkdev(clk, NULL, "fpga:uart3"); + + + /* 1 MHz clock */ + clk = clk_register_fixed_rate(NULL, "clk1mhz", NULL, CLK_IS_ROOT, + 1000000); + clk_register_clkdev(clk, NULL, "sp804"); + + /* ICST VCO clock */ + clk = icst_clk_register(NULL, &realview_icst_desc); + clk_register_clkdev(clk, NULL, "dev:clcd"); + clk_register_clkdev(clk, NULL, "issp:clcd"); +} 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/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index 0e63cdd9b52a..6b67edbdbd01 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -418,6 +418,9 @@ static struct { static atomic_t ac_wake_req_state = ATOMIC_INIT(0); +/* Functions definition */ +static void compute_armss_rate(void); + /* Spinlocks */ static DEFINE_SPINLOCK(prcmu_lock); static DEFINE_SPINLOCK(clkout_lock); @@ -517,6 +520,7 @@ static struct dsiescclk dsiescclk[3] = { } }; + /* * Used by MCDE to setup all necessary PRCMU registers */ @@ -1013,6 +1017,7 @@ int db8500_prcmu_set_arm_opp(u8 opp) (mb1_transfer.ack.arm_opp != opp)) r = -EIO; + compute_armss_rate(); mutex_unlock(&mb1_transfer.lock); return r; @@ -1612,6 +1617,7 @@ static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate, if ((branch == PLL_FIX) || ((branch == PLL_DIV) && (val & PRCM_PLL_FREQ_DIV2EN) && ((reg == PRCM_PLLSOC0_FREQ) || + (reg == PRCM_PLLARM_FREQ) || (reg == PRCM_PLLDDR_FREQ)))) div *= 2; @@ -1661,6 +1667,39 @@ static unsigned long clock_rate(u8 clock) else return 0; } +static unsigned long latest_armss_rate; +static unsigned long armss_rate(void) +{ + return latest_armss_rate; +} + +static void compute_armss_rate(void) +{ + u32 r; + unsigned long rate; + + r = readl(PRCM_ARM_CHGCLKREQ); + + if (r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ) { + /* External ARMCLKFIX clock */ + + rate = pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_FIX); + + /* Check PRCM_ARM_CHGCLKREQ divider */ + if (!(r & PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL)) + rate /= 2; + + /* Check PRCM_ARMCLKFIX_MGT divider */ + r = readl(PRCM_ARMCLKFIX_MGT); + r &= PRCM_CLK_MGT_CLKPLLDIV_MASK; + rate /= r; + + } else {/* ARM PLL */ + rate = pll_rate(PRCM_PLLARM_FREQ, ROOT_CLOCK_RATE, PLL_DIV); + } + + latest_armss_rate = rate; +} static unsigned long dsiclk_rate(u8 n) { @@ -1707,6 +1746,8 @@ unsigned long prcmu_clock_rate(u8 clock) return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW); else if (clock == PRCMU_PLLSOC1) return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW); + else if (clock == PRCMU_ARMSS) + return armss_rate(); else if (clock == PRCMU_PLLDDR) return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW); else if (clock == PRCMU_PLLDSI) @@ -2693,6 +2734,7 @@ void __init db8500_prcmu_early_init(void) handle_simple_irq); set_irq_flags(irq, IRQF_VALID); } + compute_armss_rate(); } static void __init init_prcm_registers(void) diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index 23108a6e3167..79c76ebdba52 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -61,7 +61,8 @@ #define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 #define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) -#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ BIT(0) +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_DIVSEL BIT(16) #define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) #define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 @@ -140,6 +141,7 @@ /* PRCMU clock/PLL/reset registers */ #define PRCM_PLLSOC0_FREQ (_PRCMU_BASE + 0x080) #define PRCM_PLLSOC1_FREQ (_PRCMU_BASE + 0x084) +#define PRCM_PLLARM_FREQ (_PRCMU_BASE + 0x088) #define PRCM_PLLDDR_FREQ (_PRCMU_BASE + 0x08C) #define PRCM_PLL_FREQ_D_SHIFT 0 #define PRCM_PLL_FREQ_D_MASK BITS(0, 7) 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-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 |