aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-01 12:09:04 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-01 12:09:04 -0700
commit2c0c86d5b67ee04e8b71a2ea2a3af6d224611cfc (patch)
tree84cb3b96e68be80b3e8b6876b27fc9ff6ba0370f /drivers
parentfdb2f9c2ebd4f07d7b11a3bc86d8c669eb841697 (diff)
parent494bfec99922d54054d2d0873f1017680cfc3f13 (diff)
Merge tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux
Pull clk framework update from Michael Turquette: "The common clk framework changes for 3.7 are dominated by ARM platform ports to the framework along with one MIPS port, one MFD port, one minor framework enhancement and one helper function for platforms expressing their clock data through device tree." * tag 'clk-for-linus' of git://git.linaro.org/people/mturquette/linux: clk: add of_clk_src_onecell_get() support clk: ux500: Define smp_twd clock for u8500 mfd: dbx500: Provide a more accurate smp_twd clock clk: ux500: Support for prmcu_rate clock clk: Provide option for clk_get_rate to issue hw for new rate clock: max77686: Add driver for Maxim 77686 32Khz crystal oscillator. ARM: ux500: Switch to use common clock framework clk: ux500: Clock definitions for u8500 clk: ux500: First version of clock definitions for ux500 clk: ux500: Adapt PRCMU and PRCC clocks for common clk clk: versatile: make config option boolean clk: add Loongson1B clock support arm: mmp: make all SOCs use common clock by default clk: mmp: add clock definition for mmp2 clk: mmp: add clock definition for pxa910 clk: mmp: add clock definition for pxa168 clk: mmp: add mmp specific clocks clk: convert ARM RealView to common clk clk: prima2: move from arch/arm/mach to drivers/clk ARM: PRIMA2: convert to common clk and finish full clk tree
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig13
-rw-r--r--drivers/clk/Makefile9
-rw-r--r--drivers/clk/clk-ls1x.c111
-rw-r--r--drivers/clk/clk-max77686.c244
-rw-r--r--drivers/clk/clk-prima2.c1171
-rw-r--r--drivers/clk/clk.c57
-rw-r--r--drivers/clk/mmp/Makefile9
-rw-r--r--drivers/clk/mmp/clk-apbc.c152
-rw-r--r--drivers/clk/mmp/clk-apmu.c97
-rw-r--r--drivers/clk/mmp/clk-frac.c153
-rw-r--r--drivers/clk/mmp/clk-mmp2.c449
-rw-r--r--drivers/clk/mmp/clk-pxa168.c346
-rw-r--r--drivers/clk/mmp/clk-pxa910.c320
-rw-r--r--drivers/clk/mmp/clk.h35
-rw-r--r--drivers/clk/ux500/Makefile12
-rw-r--r--drivers/clk/ux500/clk-prcc.c164
-rw-r--r--drivers/clk/ux500/clk-prcmu.c252
-rw-r--r--drivers/clk/ux500/clk.h48
-rw-r--r--drivers/clk/ux500/u8500_clk.c477
-rw-r--r--drivers/clk/ux500/u8540_clk.c21
-rw-r--r--drivers/clk/ux500/u9540_clk.c21
-rw-r--r--drivers/clk/versatile/Makefile1
-rw-r--r--drivers/clk/versatile/clk-realview.c114
-rw-r--r--drivers/mfd/db8500-prcmu.c42
-rw-r--r--drivers/mfd/dbx500-prcmu-regs.h4
25 files changed, 4301 insertions, 21 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 7f0b5ca7851..bace9e98f75 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 72ce247a0e8..b7b862077d8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -10,7 +10,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 00000000000..f20b750235f
--- /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 00000000000..ac5f5434cb9
--- /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-prima2.c b/drivers/clk/clk-prima2.c
new file mode 100644
index 00000000000..517874fa685
--- /dev/null
+++ b/drivers/clk/clk-prima2.c
@@ -0,0 +1,1171 @@
+/*
+ * Clock tree for CSR SiRFprimaII
+ *
+ * Copyright (c) 2011 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+
+#define SIRFSOC_CLKC_CLK_EN0 0x0000
+#define SIRFSOC_CLKC_CLK_EN1 0x0004
+#define SIRFSOC_CLKC_REF_CFG 0x0014
+#define SIRFSOC_CLKC_CPU_CFG 0x0018
+#define SIRFSOC_CLKC_MEM_CFG 0x001c
+#define SIRFSOC_CLKC_SYS_CFG 0x0020
+#define SIRFSOC_CLKC_IO_CFG 0x0024
+#define SIRFSOC_CLKC_DSP_CFG 0x0028
+#define SIRFSOC_CLKC_GFX_CFG 0x002c
+#define SIRFSOC_CLKC_MM_CFG 0x0030
+#define SIRFSOC_CLKC_LCD_CFG 0x0034
+#define SIRFSOC_CLKC_MMC_CFG 0x0038
+#define SIRFSOC_CLKC_PLL1_CFG0 0x0040
+#define SIRFSOC_CLKC_PLL2_CFG0 0x0044
+#define SIRFSOC_CLKC_PLL3_CFG0 0x0048
+#define SIRFSOC_CLKC_PLL1_CFG1 0x004c
+#define SIRFSOC_CLKC_PLL2_CFG1 0x0050
+#define SIRFSOC_CLKC_PLL3_CFG1 0x0054
+#define SIRFSOC_CLKC_PLL1_CFG2 0x0058
+#define SIRFSOC_CLKC_PLL2_CFG2 0x005c
+#define SIRFSOC_CLKC_PLL3_CFG2 0x0060
+#define SIRFSOC_USBPHY_PLL_CTRL 0x0008
+#define SIRFSOC_USBPHY_PLL_POWERDOWN BIT(1)
+#define SIRFSOC_USBPHY_PLL_BYPASS BIT(2)
+#define SIRFSOC_USBPHY_PLL_LOCK BIT(3)
+
+static void *sirfsoc_clk_vbase, *sirfsoc_rsc_vbase;
+
+#define KHZ 1000
+#define MHZ (KHZ * KHZ)
+
+/*
+ * SiRFprimaII clock controller
+ * - 2 oscillators: osc-26MHz, rtc-32.768KHz
+ * - 3 standard configurable plls: pll1, pll2 & pll3
+ * - 2 exclusive plls: usb phy pll and sata phy pll
+ * - 8 clock domains: cpu/cpudiv, mem/memdiv, sys/io, dsp, graphic, multimedia,
+ * display and sdphy.
+ * Each clock domain can select its own clock source from five clock sources,
+ * X_XIN, X_XINW, PLL1, PLL2 and PLL3. The domain clock is used as the source
+ * clock of the group clock.
+ * - dsp domain: gps, mf
+ * - io domain: dmac, nand, audio, uart, i2c, spi, usp, pwm, pulse
+ * - sys domain: security
+ */
+
+struct clk_pll {
+ struct clk_hw hw;
+ unsigned short regofs; /* register offset */
+};
+
+#define to_pllclk(_hw) container_of(_hw, struct clk_pll, hw)
+
+struct clk_dmn {
+ struct clk_hw hw;
+ signed char enable_bit; /* enable bit: 0 ~ 63 */
+ unsigned short regofs; /* register offset */
+};
+
+#define to_dmnclk(_hw) container_of(_hw, struct clk_dmn, hw)
+
+struct clk_std {
+ struct clk_hw hw;
+ signed char enable_bit; /* enable bit: 0 ~ 63 */
+};
+
+#define to_stdclk(_hw) container_of(_hw, struct clk_std, hw)
+
+static int std_clk_is_enabled(struct clk_hw *hw);
+static int std_clk_enable(struct clk_hw *hw);
+static void std_clk_disable(struct clk_hw *hw);
+
+static inline unsigned long clkc_readl(unsigned reg)
+{
+ return readl(sirfsoc_clk_vbase + reg);
+}
+
+static inline void clkc_writel(u32 val, unsigned reg)
+{
+ writel(val, sirfsoc_clk_vbase + reg);
+}
+
+/*
+ * std pll
+ */
+
+static unsigned long pll_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ unsigned long fin = parent_rate;
+ struct clk_pll *clk = to_pllclk(hw);
+ u32 regcfg2 = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 -
+ SIRFSOC_CLKC_PLL1_CFG0;
+
+ if (clkc_readl(regcfg2) & BIT(2)) {
+ /* pll bypass mode */
+ return fin;
+ } else {
+ /* fout = fin * nf / nr / od */
+ u32 cfg0 = clkc_readl(clk->regofs);
+ u32 nf = (cfg0 & (BIT(13) - 1)) + 1;
+ u32 nr = ((cfg0 >> 13) & (BIT(6) - 1)) + 1;
+ u32 od = ((cfg0 >> 19) & (BIT(4) - 1)) + 1;
+ WARN_ON(fin % MHZ);
+ return fin / MHZ * nf / nr / od * MHZ;
+ }
+}
+
+static long pll_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long fin, nf, nr, od;
+
+ /*
+ * fout = fin * nf / (nr * od);
+ * set od = 1, nr = fin/MHz, so fout = nf * MHz
+ */
+ rate = rate - rate % MHZ;
+
+ nf = rate / MHZ;
+ if (nf > BIT(13))
+ nf = BIT(13);
+ if (nf < 1)
+ nf = 1;
+
+ fin = *parent_rate;
+
+ nr = fin / MHZ;
+ if (nr > BIT(6))
+ nr = BIT(6);
+ od = 1;
+
+ return fin * nf / (nr * od);
+}
+
+static int pll_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_pll *clk = to_pllclk(hw);
+ unsigned long fin, nf, nr, od, reg;
+
+ /*
+ * fout = fin * nf / (nr * od);
+ * set od = 1, nr = fin/MHz, so fout = nf * MHz
+ */
+
+ nf = rate / MHZ;
+ if (unlikely((rate % MHZ) || nf > BIT(13) || nf < 1))
+ return -EINVAL;
+
+ fin = parent_rate;
+ BUG_ON(fin < MHZ);
+
+ nr = fin / MHZ;
+ BUG_ON((fin % MHZ) || nr > BIT(6));
+
+ od = 1;
+
+ reg = (nf - 1) | ((nr - 1) << 13) | ((od - 1) << 19);
+ clkc_writel(reg, clk->regofs);
+
+ reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG1 - SIRFSOC_CLKC_PLL1_CFG0;
+ clkc_writel((nf >> 1) - 1, reg);
+
+ reg = clk->regofs + SIRFSOC_CLKC_PLL1_CFG2 - SIRFSOC_CLKC_PLL1_CFG0;
+ while (!(clkc_readl(reg) & BIT(6)))
+ cpu_relax();
+
+ return 0;
+}
+
+static struct clk_ops std_pll_ops = {
+ .recalc_rate = pll_clk_recalc_rate,
+ .round_rate = pll_clk_round_rate,
+ .set_rate = pll_clk_set_rate,
+};
+
+static const char *pll_clk_parents[] = {
+ "osc",
+};
+
+static struct clk_init_data clk_pll1_init = {
+ .name = "pll1",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_init_data clk_pll2_init = {
+ .name = "pll2",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_init_data clk_pll3_init = {
+ .name = "pll3",
+ .ops = &std_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_pll clk_pll1 = {
+ .regofs = SIRFSOC_CLKC_PLL1_CFG0,
+ .hw = {
+ .init = &clk_pll1_init,
+ },
+};
+
+static struct clk_pll clk_pll2 = {
+ .regofs = SIRFSOC_CLKC_PLL2_CFG0,
+ .hw = {
+ .init = &clk_pll2_init,
+ },
+};
+
+static struct clk_pll clk_pll3 = {
+ .regofs = SIRFSOC_CLKC_PLL3_CFG0,
+ .hw = {
+ .init = &clk_pll3_init,
+ },
+};
+
+/*
+ * usb uses specified pll
+ */
+
+static int usb_pll_clk_enable(struct clk_hw *hw)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ reg &= ~(SIRFSOC_USBPHY_PLL_POWERDOWN | SIRFSOC_USBPHY_PLL_BYPASS);
+ writel(reg, sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ while (!(readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL) &
+ SIRFSOC_USBPHY_PLL_LOCK))
+ cpu_relax();
+
+ return 0;
+}
+
+static void usb_pll_clk_disable(struct clk_hw *clk)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ reg |= (SIRFSOC_USBPHY_PLL_POWERDOWN | SIRFSOC_USBPHY_PLL_BYPASS);
+ writel(reg, sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+}
+
+static unsigned long usb_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
+{
+ u32 reg = readl(sirfsoc_rsc_vbase + SIRFSOC_USBPHY_PLL_CTRL);
+ return (reg & SIRFSOC_USBPHY_PLL_BYPASS) ? parent_rate : 48*MHZ;
+}
+
+static struct clk_ops usb_pll_ops = {
+ .enable = usb_pll_clk_enable,
+ .disable = usb_pll_clk_disable,
+ .recalc_rate = usb_pll_clk_recalc_rate,
+};
+
+static struct clk_init_data clk_usb_pll_init = {
+ .name = "usb_pll",
+ .ops = &usb_pll_ops,
+ .parent_names = pll_clk_parents,
+ .num_parents = ARRAY_SIZE(pll_clk_parents),
+};
+
+static struct clk_hw usb_pll_clk_hw = {
+ .init = &clk_usb_pll_init,
+};
+
+/*
+ * clock domains - cpu, mem, sys/io, dsp, gfx
+ */
+
+static const char *dmn_clk_parents[] = {
+ "rtc",
+ "osc",
+ "pll1",
+ "pll2",
+ "pll3",
+};
+
+static u8 dmn_clk_get_parent(struct clk_hw *hw)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ u32 cfg = clkc_readl(clk->regofs);
+
+ /* parent of io domain can only be pll3 */
+ if (strcmp(hw->init->name, "io") == 0)
+ return 4;
+
+ WARN_ON((cfg & (BIT(3) - 1)) > 4);
+
+ return cfg & (BIT(3) - 1);
+}
+
+static int dmn_clk_set_parent(struct clk_hw *hw, u8 parent)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ u32 cfg = clkc_readl(clk->regofs);
+
+ /* parent of io domain can only be pll3 */
+ if (strcmp(hw->init->name, "io") == 0)
+ return -EINVAL;
+
+ cfg &= ~(BIT(3) - 1);
+ clkc_writel(cfg | parent, clk->regofs);
+ /* BIT(3) - switching status: 1 - busy, 0 - done */
+ while (clkc_readl(clk->regofs) & BIT(3))
+ cpu_relax();
+
+ return 0;
+}
+
+static unsigned long dmn_clk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+
+{
+ unsigned long fin = parent_rate;
+ struct clk_dmn *clk = to_dmnclk(hw);
+
+ u32 cfg = clkc_readl(clk->regofs);
+
+ if (cfg & BIT(24)) {
+ /* fcd bypass mode */
+ return fin;
+ } else {
+ /*
+ * wait count: bit[19:16], hold count: bit[23:20]
+ */
+ u32 wait = (cfg >> 16) & (BIT(4) - 1);
+ u32 hold = (cfg >> 20) & (BIT(4) - 1);
+
+ return fin / (wait + hold + 2);
+ }
+}
+
+static long dmn_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *parent_rate)
+{
+ unsigned long fin;
+ unsigned ratio, wait, hold;
+ unsigned bits = (strcmp(hw->init->name, "mem") == 0) ? 3 : 4;
+
+ fin = *parent_rate;
+ ratio = fin / rate;
+
+ if (ratio < 2)
+ ratio = 2;
+ if (ratio > BIT(bits + 1))
+ ratio = BIT(bits + 1);
+
+ wait = (ratio >> 1) - 1;
+ hold = ratio - wait - 2;
+
+ return fin / (wait + hold + 2);
+}
+
+static int dmn_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_dmn *clk = to_dmnclk(hw);
+ unsigned long fin;
+ unsigned ratio, wait, hold, reg;
+ unsigned bits = (strcmp(hw->init->name, "mem") == 0) ? 3 : 4;
+
+ fin = parent_rate;
+ ratio = fin / rate;
+
+ if (unlikely(ratio < 2 || ratio > BIT(bits + 1)))
+ return -EINVAL;
+
+ WARN_ON(fin % rate);
+
+ wait = (ratio >> 1) - 1;
+ hold = ratio - wait - 2;
+
+ reg = clkc_readl(clk->regofs);
+ reg &= ~(((BIT(bits) - 1) << 16) | ((BIT(bits) - 1) << 20));
+ reg |= (wait << 16) | (hold << 20) | BIT(25);
+ clkc_writel(reg, clk->regofs);
+
+ /* waiting FCD been effective */
+ while (clkc_readl(clk->regofs) & BIT(25))
+ cpu_relax();
+
+ return 0;
+}
+
+static struct clk_ops msi_ops = {
+ .set_rate = dmn_clk_set_rate,
+ .round_rate = dmn_clk_round_rate,
+ .recalc_rate = dmn_clk_recalc_rate,
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+static struct clk_init_data clk_mem_init = {
+ .name = "mem",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mem = {
+ .regofs = SIRFSOC_CLKC_MEM_CFG,
+ .hw = {
+ .init = &clk_mem_init,
+ },
+};
+
+static struct clk_init_data clk_sys_init = {
+ .name = "sys",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+ .flags = CLK_SET_RATE_GATE,
+};
+
+static struct clk_dmn clk_sys = {
+ .regofs = SIRFSOC_CLKC_SYS_CFG,
+ .hw = {
+ .init = &clk_sys_init,
+ },
+};
+
+static struct clk_init_data clk_io_init = {
+ .name = "io",
+ .ops = &msi_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_io = {
+ .regofs = SIRFSOC_CLKC_IO_CFG,
+ .hw = {
+ .init = &clk_io_init,
+ },
+};
+
+static struct clk_ops cpu_ops = {
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+static struct clk_init_data clk_cpu_init = {
+ .name = "cpu",
+ .ops = &cpu_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+ .flags = CLK_SET_RATE_PARENT,
+};
+
+static struct clk_dmn clk_cpu = {
+ .regofs = SIRFSOC_CLKC_CPU_CFG,
+ .hw = {
+ .init = &clk_cpu_init,
+ },
+};
+
+static struct clk_ops dmn_ops = {
+ .is_enabled = std_clk_is_enabled,
+ .enable = std_clk_enable,
+ .disable = std_clk_disable,
+ .set_rate = dmn_clk_set_rate,
+ .round_rate = dmn_clk_round_rate,
+ .recalc_rate = dmn_clk_recalc_rate,
+ .set_parent = dmn_clk_set_parent,
+ .get_parent = dmn_clk_get_parent,
+};
+
+/* dsp, gfx, mm, lcd and vpp domain */
+
+static struct clk_init_data clk_dsp_init = {
+ .name = "dsp",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_dsp = {
+ .regofs = SIRFSOC_CLKC_DSP_CFG,
+ .enable_bit = 0,
+ .hw = {
+ .init = &clk_dsp_init,
+ },
+};
+
+static struct clk_init_data clk_gfx_init = {
+ .name = "gfx",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_gfx = {
+ .regofs = SIRFSOC_CLKC_GFX_CFG,
+ .enable_bit = 8,
+ .hw = {
+ .init = &clk_gfx_init,
+ },
+};
+
+static struct clk_init_data clk_mm_init = {
+ .name = "mm",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_mm = {
+ .regofs = SIRFSOC_CLKC_MM_CFG,
+ .enable_bit = 9,
+ .hw = {
+ .init = &clk_mm_init,
+ },
+};
+
+static struct clk_init_data clk_lcd_init = {
+ .name = "lcd",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_lcd = {
+ .regofs = SIRFSOC_CLKC_LCD_CFG,
+ .enable_bit = 10,
+ .hw = {
+ .init = &clk_lcd_init,
+ },
+};
+
+static struct clk_init_data clk_vpp_init = {
+ .name = "vpp",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .num_parents = ARRAY_SIZE(dmn_clk_parents),
+};
+
+static struct clk_dmn clk_vpp = {
+ .regofs = SIRFSOC_CLKC_LCD_CFG,
+ .enable_bit = 11,
+ .hw = {
+ .init = &clk_vpp_init,
+ },
+};
+
+static struct clk_init_data clk_mmc01_init = {
+ .name = "mmc01",
+ .ops = &dmn_ops,
+ .parent_names = dmn_clk_parents,
+ .