summaryrefslogtreecommitdiff
path: root/drivers/clk
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig16
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-fixed-rate.c2
-rw-r--r--drivers/clk/clk-prima2.c84
-rw-r--r--drivers/clk/clk-twl6040.c126
-rw-r--r--drivers/clk/clk.c20
-rw-r--r--drivers/clk/spear/clk-vco-pll.c2
-rw-r--r--drivers/clk/ux500/clk-prcmu.c55
-rw-r--r--drivers/clk/ux500/clk.h6
-rw-r--r--drivers/clk/ux500/u8500_clk.c5
-rw-r--r--drivers/clk/versatile/Makefile2
-rw-r--r--drivers/clk/versatile/clk-vexpress-osc.c146
-rw-r--r--drivers/clk/versatile/clk-vexpress.c142
13 files changed, 548 insertions, 59 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index bace9e98f75d..823f62d900ba 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -42,10 +42,12 @@ config COMMON_CLK_WM831X
config COMMON_CLK_VERSATILE
bool "Clock driver for ARM Reference designs"
- depends on ARCH_INTEGRATOR || ARCH_REALVIEW
+ depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS
---help---
- Supports clocking on ARM Reference designs Integrator/AP,
- Integrator/CP, RealView PB1176, EB, PB11MP and PBX.
+ Supports clocking on ARM Reference designs:
+ - Integrator/AP and Integrator/CP
+ - RealView PB1176, EB, PB11MP and PBX
+ - Versatile Express
config COMMON_CLK_MAX77686
tristate "Clock driver for Maxim 77686 MFD"
@@ -53,4 +55,12 @@ config COMMON_CLK_MAX77686
---help---
This driver supports Maxim 77686 crystal oscillator clock.
+config CLK_TWL6040
+ tristate "External McPDM functional clock from twl6040"
+ depends on TWL6040_CORE
+ ---help---
+ Enable the external functional clock support on OMAP4+ platforms for
+ McPDM. McPDM module is using the external bit clock on the McPDM bus
+ as functional clock.
+
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 9c300a828ede..a96bda3d3b84 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -24,3 +24,4 @@ obj-$(CONFIG_ARCH_SUNXI) += clk-sunxi.o
# Chip specific
obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o
diff --git a/drivers/clk/clk-fixed-rate.c b/drivers/clk/clk-fixed-rate.c
index f5ec0eebd4d7..af78ed6b67ef 100644
--- a/drivers/clk/clk-fixed-rate.c
+++ b/drivers/clk/clk-fixed-rate.c
@@ -97,7 +97,7 @@ void __init of_fixed_clk_setup(struct device_node *node)
of_property_read_string(node, "clock-output-names", &clk_name);
clk = clk_register_fixed_rate(NULL, clk_name, NULL, CLK_IS_ROOT, rate);
- if (clk)
+ if (!IS_ERR(clk))
of_clk_add_provider(node, of_clk_src_simple_get, clk);
}
EXPORT_SYMBOL_GPL(of_fixed_clk_setup);
diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c
index 517874fa6858..a203ecccdc4f 100644
--- a/drivers/clk/clk-prima2.c
+++ b/drivers/clk/clk-prima2.c
@@ -1054,118 +1054,118 @@ void __init sirfsoc_of_clk_init(void)
/* These are always available (RTC and 26MHz OSC)*/
clk = clk_register_fixed_rate(NULL, "rtc", NULL,
CLK_IS_ROOT, 32768);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register_fixed_rate(NULL, "osc", NULL,
CLK_IS_ROOT, 26000000);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_pll1.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_pll2.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_pll3.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_mem.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_sys.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_security.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b8030000.security");
clk = clk_register(NULL, &clk_dsp.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_gps.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "a8010000.gps");
clk = clk_register(NULL, &clk_mf.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_io.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "io");
clk = clk_register(NULL, &clk_cpu.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "cpu");
clk = clk_register(NULL, &clk_uart0.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0050000.uart");
clk = clk_register(NULL, &clk_uart1.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0060000.uart");
clk = clk_register(NULL, &clk_uart2.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0070000.uart");
clk = clk_register(NULL, &clk_tsc.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0110000.tsc");
clk = clk_register(NULL, &clk_i2c0.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00e0000.i2c");
clk = clk_register(NULL, &clk_i2c1.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00f0000.i2c");
clk = clk_register(NULL, &clk_spi0.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00d0000.spi");
clk = clk_register(NULL, &clk_spi1.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0170000.spi");
clk = clk_register(NULL, &clk_pwmc.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0130000.pwm");
clk = clk_register(NULL, &clk_efuse.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0140000.efusesys");
clk = clk_register(NULL, &clk_pulse.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0150000.pulsec");
clk = clk_register(NULL, &clk_dmac0.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00b0000.dma-controller");
clk = clk_register(NULL, &clk_dmac1.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0160000.dma-controller");
clk = clk_register(NULL, &clk_nand.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0030000.nand");
clk = clk_register(NULL, &clk_audio.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0040000.audio");
clk = clk_register(NULL, &clk_usp0.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0080000.usp");
clk = clk_register(NULL, &clk_usp1.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b0090000.usp");
clk = clk_register(NULL, &clk_usp2.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00a0000.usp");
clk = clk_register(NULL, &clk_vip.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00c0000.vip");
clk = clk_register(NULL, &clk_gfx.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "98000000.graphics");
clk = clk_register(NULL, &clk_mm.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "a0000000.multimedia");
clk = clk_register(NULL, &clk_lcd.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "90010000.display");
clk = clk_register(NULL, &clk_vpp.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "90020000.vpp");
clk = clk_register(NULL, &clk_mmc01.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_mmc23.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_mmc45.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &usb_pll_clk_hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk = clk_register(NULL, &clk_usb0.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00e0000.usb");
clk = clk_register(NULL, &clk_usb1.hw);
- BUG_ON(!clk);
+ BUG_ON(IS_ERR(clk));
clk_register_clkdev(clk, NULL, "b00f0000.usb");
}
diff --git a/drivers/clk/clk-twl6040.c b/drivers/clk/clk-twl6040.c
new file mode 100644
index 000000000000..f4a3389c3d09
--- /dev/null
+++ b/drivers/clk/clk-twl6040.c
@@ -0,0 +1,126 @@
+/*
+* TWL6040 clock module driver for OMAP4 McPDM functional clock
+*
+* Copyright (C) 2012 Texas Instruments Inc.
+* Peter Ujfalusi <peter.ujfalusi@ti.com>
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* 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., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA
+*
+*/
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/twl6040.h>
+#include <linux/clk-provider.h>
+
+struct twl6040_clk {
+ struct twl6040 *twl6040;
+ struct device *dev;
+ struct clk_hw mcpdm_fclk;
+ struct clk *clk;
+ int enabled;
+};
+
+static int twl6040_bitclk_is_enabled(struct clk_hw *hw)
+{
+ struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
+ mcpdm_fclk);
+ return twl6040_clk->enabled;
+}
+
+static int twl6040_bitclk_prepare(struct clk_hw *hw)
+{
+ struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
+ mcpdm_fclk);
+ int ret;
+
+ ret = twl6040_power(twl6040_clk->twl6040, 1);
+ if (!ret)
+ twl6040_clk->enabled = 1;
+
+ return ret;
+}
+
+static void twl6040_bitclk_unprepare(struct clk_hw *hw)
+{
+ struct twl6040_clk *twl6040_clk = container_of(hw, struct twl6040_clk,
+ mcpdm_fclk);
+ int ret;
+
+ ret = twl6040_power(twl6040_clk->twl6040, 0);
+ if (!ret)
+ twl6040_clk->enabled = 0;
+}
+
+static const struct clk_ops twl6040_mcpdm_ops = {
+ .is_enabled = twl6040_bitclk_is_enabled,
+ .prepare = twl6040_bitclk_prepare,
+ .unprepare = twl6040_bitclk_unprepare,
+};
+
+static struct clk_init_data wm831x_clkout_init = {
+ .name = "mcpdm_fclk",
+ .ops = &twl6040_mcpdm_ops,
+ .flags = CLK_IS_ROOT,
+};
+
+static int __devinit twl6040_clk_probe(struct platform_device *pdev)
+{
+ struct twl6040 *twl6040 = dev_get_drvdata(pdev->dev.parent);
+ struct twl6040_clk *clkdata;
+
+ clkdata = devm_kzalloc(&pdev->dev, sizeof(*clkdata), GFP_KERNEL);
+ if (!clkdata)
+ return -ENOMEM;
+
+ clkdata->dev = &pdev->dev;
+ clkdata->twl6040 = twl6040;
+
+ clkdata->mcpdm_fclk.init = &wm831x_clkout_init;
+ clkdata->clk = clk_register(&pdev->dev, &clkdata->mcpdm_fclk);
+ if (!clkdata->clk)
+ return -EINVAL;
+
+ dev_set_drvdata(&pdev->dev, clkdata);
+
+ return 0;
+}
+
+static int __devexit twl6040_clk_remove(struct platform_device *pdev)
+{
+ struct twl6040_clk *clkdata = dev_get_drvdata(&pdev->dev);
+
+ clk_unregister(clkdata->clk);
+
+ return 0;
+}
+
+static struct platform_driver twl6040_clk_driver = {
+ .driver = {
+ .name = "twl6040-clk",
+ .owner = THIS_MODULE,
+ },
+ .probe = twl6040_clk_probe,
+ .remove = __devexit_p(twl6040_clk_remove),
+};
+
+module_platform_driver(twl6040_clk_driver);
+
+MODULE_DESCRIPTION("TWL6040 clock driver for McPDM functional clock");
+MODULE_AUTHOR("Peter Ujfalusi <peter.ujfalusi@ti.com>");
+MODULE_ALIAS("platform:twl6040-clk");
+MODULE_LICENSE("GPL");
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 56e4495ebeb1..bbe52c4ae7ca 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -261,7 +261,7 @@ inline struct clk_hw *__clk_get_hw(struct clk *clk)
inline u8 __clk_get_num_parents(struct clk *clk)
{
- return !clk ? -EINVAL : clk->num_parents;
+ return !clk ? 0 : clk->num_parents;
}
inline struct clk *__clk_get_parent(struct clk *clk)
@@ -269,14 +269,14 @@ inline struct clk *__clk_get_parent(struct clk *clk)
return !clk ? NULL : clk->parent;
}
-inline int __clk_get_enable_count(struct clk *clk)
+inline unsigned int __clk_get_enable_count(struct clk *clk)
{
- return !clk ? -EINVAL : clk->enable_count;
+ return !clk ? 0 : clk->enable_count;
}
-inline int __clk_get_prepare_count(struct clk *clk)
+inline unsigned int __clk_get_prepare_count(struct clk *clk)
{
- return !clk ? -EINVAL : clk->prepare_count;
+ return !clk ? 0 : clk->prepare_count;
}
unsigned long __clk_get_rate(struct clk *clk)
@@ -302,15 +302,15 @@ out:
inline unsigned long __clk_get_flags(struct clk *clk)
{
- return !clk ? -EINVAL : clk->flags;
+ return !clk ? 0 : clk->flags;
}
-int __clk_is_enabled(struct clk *clk)
+bool __clk_is_enabled(struct clk *clk)
{
int ret;
if (!clk)
- return -EINVAL;
+ return false;
/*
* .is_enabled is only mandatory for clocks that gate
@@ -323,7 +323,7 @@ int __clk_is_enabled(struct clk *clk)
ret = clk->ops->is_enabled(clk->hw);
out:
- return ret;
+ return !!ret;
}
static struct clk *__clk_lookup_subtree(const char *name, struct clk *clk)
@@ -568,7 +568,7 @@ unsigned long __clk_round_rate(struct clk *clk, unsigned long rate)
unsigned long parent_rate = 0;
if (!clk)
- return -EINVAL;
+ return 0;
if (!clk->ops->round_rate) {
if (clk->flags & CLK_SET_RATE_PARENT)
diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c
index 5f1b6badeb15..1b9b65bca51e 100644
--- a/drivers/clk/spear/clk-vco-pll.c
+++ b/drivers/clk/spear/clk-vco-pll.c
@@ -147,7 +147,7 @@ static int clk_pll_set_rate(struct clk_hw *hw, unsigned long drate,
struct clk_pll *pll = to_clk_pll(hw);
struct pll_rate_tbl *rtbl = pll->vco->rtbl;
unsigned long flags = 0, val;
- int i;
+ int uninitialized_var(i);
clk_pll_round_rate_index(hw, drate, NULL, &i);
diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c
index 930cdfeb47ab..04577ca6a308 100644
--- a/drivers/clk/ux500/clk-prcmu.c
+++ b/drivers/clk/ux500/clk-prcmu.c
@@ -133,6 +133,40 @@ out_error:
hw->init->name);
}
+static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw)
+{
+ int err;
+ struct clk_prcmu *clk = to_clk_prcmu(hw);
+
+ err = prcmu_request_ape_opp_100_voltage(true);
+ if (err) {
+ pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n",
+ __func__, hw->init->name);
+ return err;
+ }
+
+ err = prcmu_request_clock(clk->cg_sel, true);
+ if (err)
+ prcmu_request_ape_opp_100_voltage(false);
+
+ return err;
+}
+
+static void clk_prcmu_opp_volt_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 (prcmu_request_ape_opp_100_voltage(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,
@@ -167,6 +201,17 @@ static struct clk_ops clk_prcmu_opp_gate_ops = {
.recalc_rate = clk_prcmu_recalc_rate,
};
+static struct clk_ops clk_prcmu_opp_volt_scalable_ops = {
+ .prepare = clk_prcmu_opp_volt_prepare,
+ .unprepare = clk_prcmu_opp_volt_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 *clk_reg_prcmu(const char *name,
const char *parent_name,
u8 cg_sel,
@@ -250,3 +295,13 @@ struct clk *clk_reg_prcmu_opp_gate(const char *name,
return clk_reg_prcmu(name, parent_name, cg_sel, 0, flags,
&clk_prcmu_opp_gate_ops);
}
+
+struct clk *clk_reg_prcmu_opp_volt_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_opp_volt_scalable_ops);
+}
diff --git a/drivers/clk/ux500/clk.h b/drivers/clk/ux500/clk.h
index 836d7d16751e..f36eeedca493 100644
--- a/drivers/clk/ux500/clk.h
+++ b/drivers/clk/ux500/clk.h
@@ -45,4 +45,10 @@ struct clk *clk_reg_prcmu_opp_gate(const char *name,
u8 cg_sel,
unsigned long flags);
+struct clk *clk_reg_prcmu_opp_volt_scalable(const char *name,
+ const char *parent_name,
+ u8 cg_sel,
+ unsigned long rate,
+ unsigned long flags);
+
#endif /* __UX500_CLK_H */
diff --git a/drivers/clk/ux500/u8500_clk.c b/drivers/clk/ux500/u8500_clk.c
index ca4a25ed844c..7bebf1f62c65 100644
--- a/drivers/clk/ux500/u8500_clk.c
+++ b/drivers/clk/ux500/u8500_clk.c
@@ -170,10 +170,11 @@ void u8500_clk_init(void)
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 = clk_reg_prcmu_opp_volt_scalable("sdmmcclk", NULL, PRCMU_SDMMCCLK,
+ 100000000,
+ CLK_IS_ROOT|CLK_SET_RATE_GATE);
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");
diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile
index c0a0f6478798..c776053e5bb4 100644
--- a/drivers/clk/versatile/Makefile
+++ b/drivers/clk/versatile/Makefile
@@ -2,3 +2,5 @@
obj-$(CONFIG_ICST) += clk-icst.o
obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
+obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o
+obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o
diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c
new file mode 100644
index 000000000000..dcb6ae0a0425
--- /dev/null
+++ b/drivers/clk/versatile/clk-vexpress-osc.c
@@ -0,0 +1,146 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#define pr_fmt(fmt) "vexpress-osc: " fmt
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/vexpress.h>
+
+struct vexpress_osc {
+ struct vexpress_config_func *func;
+ struct clk_hw hw;
+ unsigned long rate_min;
+ unsigned long rate_max;
+};
+
+#define to_vexpress_osc(osc) container_of(osc, struct vexpress_osc, hw)
+
+static unsigned long vexpress_osc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct vexpress_osc *osc = to_vexpress_osc(hw);
+ u32 rate;
+
+ vexpress_config_read(osc->func, 0, &rate);
+
+ return rate;
+}
+
+static long vexpress_osc_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ struct vexpress_osc *osc = to_vexpress_osc(hw);
+
+ if (WARN_ON(osc->rate_min && rate < osc->rate_min))
+ rate = osc->rate_min;
+
+ if (WARN_ON(osc->rate_max && rate > osc->rate_max))
+ rate = osc->rate_max;
+
+ return rate;
+}
+
+static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct vexpress_osc *osc = to_vexpress_osc(hw);
+
+ return vexpress_config_write(osc->func, 0, rate);
+}
+
+static struct clk_ops vexpress_osc_ops = {
+ .recalc_rate = vexpress_osc_recalc_rate,
+ .round_rate = vexpress_osc_round_rate,
+ .set_rate = vexpress_osc_set_rate,
+};
+
+
+struct clk * __init vexpress_osc_setup(struct device *dev)
+{
+ struct clk_init_data init;
+ struct vexpress_osc *osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+
+ if (!osc)
+ return NULL;
+
+ osc->func = vexpress_config_func_get_by_dev(dev);
+ if (!osc->func) {
+ kfree(osc);
+ return NULL;
+ }
+
+ init.name = dev_name(dev);
+ init.ops = &vexpress_osc_ops;
+ init.flags = CLK_IS_ROOT;
+ init.num_parents = 0;
+ osc->hw.init = &init;
+
+ return clk_register(NULL, &osc->hw);
+}
+
+void __init vexpress_osc_of_setup(struct device_node *node)
+{
+ struct clk_init_data init;
+ struct vexpress_osc *osc;
+ struct clk *clk;
+ u32 range[2];
+
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ goto error;
+
+ osc->func = vexpress_config_func_get_by_node(node);
+ if (!osc->func) {
+ pr_err("Failed to obtain config func for node '%s'!\n",
+ node->name);
+ goto error;
+ }
+
+ if (of_property_read_u32_array(node, "freq-range", range,
+ ARRAY_SIZE(range)) == 0) {
+ osc->rate_min = range[0];
+ osc->rate_max = range[1];
+ }
+
+ of_property_read_string(node, "clock-output-names", &init.name);
+ if (!init.name)
+ init.name = node->name;
+
+ init.ops = &vexpress_osc_ops;
+ init.flags = CLK_IS_ROOT;
+ init.num_parents = 0;
+
+ osc->hw.init = &init;
+
+ clk = clk_register(NULL, &osc->hw);
+ if (IS_ERR(clk)) {
+ pr_err("Failed to register clock '%s'!\n", init.name);
+ goto error;
+ }
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+
+ pr_debug("Registered clock '%s'\n", init.name);
+
+ return;
+
+error:
+ if (osc->func)
+ vexpress_config_func_put(osc->func);
+ kfree(osc);
+}
diff --git a/drivers/clk/versatile/clk-vexpress.c b/drivers/clk/versatile/clk-vexpress.c
new file mode 100644
index 000000000000..c742ac7c60bb
--- /dev/null
+++ b/drivers/clk/versatile/clk-vexpress.c
@@ -0,0 +1,142 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Copyright (C) 2012 ARM Limited
+ */
+
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/vexpress.h>
+
+#include <asm/hardware/sp810.h>
+
+static struct clk *vexpress_sp810_timerclken[4];
+static DEFINE_SPINLOCK(vexpress_sp810_lock);
+
+static void __init vexpress_sp810_init(void __iomem *base)
+{
+ int i;
+
+ if (WARN_ON(!base))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++) {
+ char name[12];
+ const char *parents[] = {
+ "v2m:refclk32khz", /* REFCLK */
+ "v2m:refclk1mhz" /* TIMCLK */
+ };
+
+ snprintf(name, ARRAY_SIZE(name), "timerclken%d", i);
+
+ vexpress_sp810_timerclken[i] = clk_register_mux(NULL, name,
+ parents, 2, 0, base + SCCTRL,
+ SCCTRL_TIMERENnSEL_SHIFT(i), 1,
+ 0, &vexpress_sp810_lock);
+
+ if (WARN_ON(IS_ERR(vexpress_sp810_timerclken[i])))
+ break;
+ }
+}
+
+
+static const char * const vexpress_clk_24mhz_periphs[] __initconst = {
+ "mb:uart0", "mb:uart1", "mb:uart2", "mb:uart3",
+ "mb:mmci", "mb:kmi0", "mb:kmi1"
+};
+
+void __init vexpress_clk_init(void __iomem *sp810_base)
+{
+ struct clk *clk;
+ int i;
+
+ clk = clk_register_fixed_rate(NULL, "dummy_apb_pclk", NULL,
+ CLK_IS_ROOT, 0);
+ WARN_ON(clk_register_clkdev(clk, "apb_pclk", NULL));
+
+ clk = clk_register_fixed_rate(NULL, "v2m:clk_24mhz", NULL,
+ CLK_IS_ROOT, 24000000);
+ for (i = 0; i < ARRAY_SIZE(vexpress_clk_24mhz_periphs); i++)
+ WARN_ON(clk_register_clkdev(clk, NULL,
+ vexpress_clk_24mhz_periphs[i]));
+
+ clk = clk_register_fixed_rate(NULL, "v2m:refclk32khz", NULL,
+ CLK_IS_ROOT, 32768);
+ WARN_ON(clk_register_clkdev(clk, NULL, "v2m:wdt"));
+
+ clk = clk_register_fixed_rate(NULL, "v2m:refclk1mhz", NULL,
+ CLK_IS_ROOT, 1000000);
+
+ vexpress_sp810_init(sp810_base);
+
+ for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++)
+ WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i], clk));
+
+ WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0],
+ "v2m-timer0", "sp804"));
+ WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
+ "v2m-timer1", "sp804"));
+}
+
+#if defined(CONFIG_OF)
+
+struct clk *vexpress_sp810_of_get(struct of_phandle_args *clkspec, void *data)
+{
+ if (WARN_ON(clkspec->args_count != 1 || clkspec->args[0] >
+ ARRAY_SIZE(vexpress_sp810_timerclken)))
+ return NULL;
+
+ return vexpress_sp810_timerclken[clkspec->args[0]];
+}
+
+static const __initconst struct of_device_id vexpress_fixed_clk_match[] = {
+ { .compatible = "fixed-clock", .data = of_fixed_clk_setup, },
+ { .compatible = "arm,vexpress-osc", .data = vexpress_osc_of_setup, },
+ {}
+};
+
+void __init vexpress_clk_of_init(void)
+{
+ struct device_node *node;
+ struct clk *clk;
+ struct clk *refclk, *timclk;
+
+ of_clk_init(vexpress_fixed_clk_match);
+
+ node = of_find_compatible_node(NULL, NULL, "arm,sp810");
+ vexpress_sp810_init(of_iomap(node, 0));
+ of_clk_add_provider(node, vexpress_sp810_of_get, NULL);
+
+ /* Select "better" (faster) parent for SP804 timers */
+ refclk = of_clk_get_by_name(node, "refclk");
+ timclk = of_clk_get_by_name(node, "timclk");
+ if (!WARN_ON(IS_ERR(refclk) || IS_ERR(timclk))) {
+ int i = 0;
+
+ if (clk_get_rate(refclk) > clk_get_rate(timclk))
+ clk = refclk;
+ else
+ clk = timclk;
+
+ for (i = 0; i < ARRAY_SIZE(vexpress_sp810_timerclken); i++)
+ WARN_ON(clk_set_parent(vexpress_sp810_timerclken[i],
+ clk));
+ }
+
+ WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[0],
+ "v2m-timer0", "sp804"));
+ WARN_ON(clk_register_clkdev(vexpress_sp810_timerclken[1],
+ "v2m-timer1", "sp804"));
+}
+
+#endif