summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-imx/clk-fixup-div.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-09-06 22:30:06 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2013-09-06 22:30:06 +0200
commitb4b50fd78b1e31989940dfc647e64453d0f7176a (patch)
tree1a55f110e021c02963b63759f3f18ea7ba3aa228 /arch/arm/mach-imx/clk-fixup-div.c
parentMerge tag 'dt-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm... (diff)
parenttegra-cpuidle: provide stub when !CONFIG_CPU_IDLE (diff)
downloadlinux-b4b50fd78b1e31989940dfc647e64453d0f7176a.tar.xz
linux-b4b50fd78b1e31989940dfc647e64453d0f7176a.zip
Merge tag 'soc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform changes from Olof Johansson: "This branch contains mostly additions and changes to platform enablement and SoC-level drivers. Since there's sometimes a dependency on device-tree changes, there's also a fair amount of those in this branch. Pieces worth mentioning are: - Mbus driver for Marvell platforms, allowing kernel configuration and resource allocation of on-chip peripherals. - Enablement of the mbus infrastructure from Marvell PCI-e drivers. - Preparation of MSI support for Marvell platforms. - Addition of new PCI-e host controller driver for Tegra platforms - Some churn caused by sharing of macro names between i.MX 6Q and 6DL platforms in the device tree sources and header files. - Various suspend/PM updates for Tegra, including LP1 support. - Versatile Express support for MCPM, part of big little support. - Allwinner platform support for A20 and A31 SoCs (dual and quad Cortex-A7) - OMAP2+ support for DRA7, a new Cortex-A15-based SoC. The code that touches other architectures are patches moving MSI arch-specific functions over to weak symbols and removal of ARCH_SUPPORTS_MSI, acked by PCI maintainers" * tag 'soc-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (266 commits) tegra-cpuidle: provide stub when !CONFIG_CPU_IDLE PCI: tegra: replace devm_request_and_ioremap by devm_ioremap_resource ARM: tegra: Drop ARCH_SUPPORTS_MSI and sort list ARM: dts: vf610-twr: enable i2c0 device ARM: dts: i.MX51: Add one more I2C2 pinmux entry ARM: dts: i.MX51: Move pins configuration under "iomuxc" label ARM: dtsi: imx6qdl-sabresd: Add USB OTG vbus pin to pinctrl_hog ARM: dtsi: imx6qdl-sabresd: Add USB host 1 VBUS regulator ARM: dts: imx27-phytec-phycore-som: Enable AUDMUX ARM: dts: i.MX27: Disable AUDMUX in the template ARM: dts: wandboard: Add support for SDIO bcm4329 ARM: i.MX5 clocks: Remove optional clock setup (CKIH1) from i.MX51 template ARM: dts: imx53-qsb: Make USBH1 functional ARM i.MX6Q: dts: Enable I2C1 with EEPROM and PMIC on Phytec phyFLEX-i.MX6 Ouad module ARM i.MX6Q: dts: Enable SPI NOR flash on Phytec phyFLEX-i.MX6 Ouad module ARM: dts: imx6qdl-sabresd: Add touchscreen support ARM: imx: add ocram clock for imx53 ARM: dts: imx: ocram size is different between imx6q and imx6dl ARM: dts: imx27-phytec-phycore-som: Fix regulator settings ARM: dts: i.MX27: Remove clock name from CPU node ...
Diffstat (limited to 'arch/arm/mach-imx/clk-fixup-div.c')
-rw-r--r--arch/arm/mach-imx/clk-fixup-div.c129
1 files changed, 129 insertions, 0 deletions
diff --git a/arch/arm/mach-imx/clk-fixup-div.c b/arch/arm/mach-imx/clk-fixup-div.c
new file mode 100644
index 000000000000..21db020b1f2d
--- /dev/null
+++ b/arch/arm/mach-imx/clk-fixup-div.c
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "clk.h"
+
+#define to_clk_div(_hw) container_of(_hw, struct clk_divider, hw)
+#define div_mask(d) ((1 << (d->width)) - 1)
+
+/**
+ * struct clk_fixup_div - imx integer fixup divider clock
+ * @divider: the parent class
+ * @ops: pointer to clk_ops of parent class
+ * @fixup: a hook to fixup the write value
+ *
+ * The imx fixup divider clock is a subclass of basic clk_divider
+ * with an addtional fixup hook.
+ */
+struct clk_fixup_div {
+ struct clk_divider divider;
+ const struct clk_ops *ops;
+ void (*fixup)(u32 *val);
+};
+
+static inline struct clk_fixup_div *to_clk_fixup_div(struct clk_hw *hw)
+{
+ struct clk_divider *divider = to_clk_div(hw);
+
+ return container_of(divider, struct clk_fixup_div, divider);
+}
+
+static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
+
+ return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate);
+}
+
+static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long *prate)
+{
+ struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
+
+ return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate);
+}
+
+static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate,
+ unsigned long parent_rate)
+{
+ struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
+ struct clk_divider *div = to_clk_div(hw);
+ unsigned int divider, value;
+ unsigned long flags = 0;
+ u32 val;
+
+ divider = parent_rate / rate;
+
+ /* Zero based divider */
+ value = divider - 1;
+
+ if (value > div_mask(div))
+ value = div_mask(div);
+
+ spin_lock_irqsave(div->lock, flags);
+
+ val = readl(div->reg);
+ val &= ~(div_mask(div) << div->shift);
+ val |= value << div->shift;
+ fixup_div->fixup(&val);
+ writel(val, div->reg);
+
+ spin_unlock_irqrestore(div->lock, flags);
+
+ return 0;
+}
+
+static const struct clk_ops clk_fixup_div_ops = {
+ .recalc_rate = clk_fixup_div_recalc_rate,
+ .round_rate = clk_fixup_div_round_rate,
+ .set_rate = clk_fixup_div_set_rate,
+};
+
+struct clk *imx_clk_fixup_divider(const char *name, const char *parent,
+ void __iomem *reg, u8 shift, u8 width,
+ void (*fixup)(u32 *val))
+{
+ struct clk_fixup_div *fixup_div;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ if (!fixup)
+ return ERR_PTR(-EINVAL);
+
+ fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL);
+ if (!fixup_div)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &clk_fixup_div_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.parent_names = parent ? &parent : NULL;
+ init.num_parents = parent ? 1 : 0;
+
+ fixup_div->divider.reg = reg;
+ fixup_div->divider.shift = shift;
+ fixup_div->divider.width = width;
+ fixup_div->divider.lock = &imx_ccm_lock;
+ fixup_div->divider.hw.init = &init;
+ fixup_div->ops = &clk_divider_ops;
+ fixup_div->fixup = fixup;
+
+ clk = clk_register(NULL, &fixup_div->divider.hw);
+ if (IS_ERR(clk))
+ kfree(fixup_div);
+
+ return clk;
+}