summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-10-15 07:05:03 +0200
committerLinus Torvalds <torvalds@linux-foundation.org>2014-10-15 07:05:03 +0200
commitc0fa2373f8cfed90437d8d7b17e0b1a84009a10a (patch)
tree43fb2edd0c11874d0b2e56714e53894d10321e19 /drivers
parentfcc3a5d277571bc6048e7b4ef8cd391b935de629 (diff)
parent98d147f50eb0ce4328e013f5f2c076896003c761 (diff)
Merge tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux
Pull clock tree updates from Mike Turquette: "The clk tree changes for 3.18 are dominated by clock drivers. Mostly fixes and enhancements to existing drivers as well as new drivers. This tag contains a bit more arch code than I usually take due to some OMAP2+ changes. Additionally it contains the restart notifier handlers which are merged as a dependency into several trees. The PXA changes are the only messy part. Due to having a stable tree I had to revert one patch and follow up with one more fix near the tip of this tag. Some dead code is introduced but it will soon become live code after 3.18-rc1 is released as the rest of the PXA family is converted over to the common clock framework. Another trend in this tag is that multiple vendors have started to push the complexity of changing their CPU frequency into the clock driver, whereas this used to be done in CPUfreq drivers. Changes to the clk core include a generic gpio-clock type and a clk_set_phase() function added to the top-level clk.h api. Due to some confusion on the fbdev mailing list the kernel boot parameters documentation was updated to further explain the clk_ignore_unused parameter, which is often required by users of the simplefb driver. Finally some fixes to the locking around the clock debugfs stuff was done to prevent deadlocks when interacting with other subsystems." * tag 'clk-for-linus-3.18' of git://git.linaro.org/people/mike.turquette/linux: (99 commits) clk: pxa clocks build system fix Revert "arm: pxa: Transition pxa27x to clk framework" clk: samsung: register restart handlers for s3c2412 and s3c2443 clk: rockchip: add restart handler clk: rockchip: rk3288: i2s_frac adds flag to set parent's rate doc/kernel-parameters.txt: clarify clk_ignore_unused arm: pxa: Transition pxa27x to clk framework dts: add devicetree bindings for pxa27x clocks clk: add pxa27x clock drivers arm: pxa: add clock pll selection bits clk: dts: document pxa clock binding clk: add pxa clocks infrastructure clk: gpio-gate: Ensure gpiod_ APIs are prototyped clk: ti: dra7-atl-clock: Mark the device as pm_runtime_irq_safe clk: ti: LLVMLinux: Move __init outside of type definition clk: ti: consider the fact that of_clk_get() might return an error clk: ti: dra7-atl-clock: fix a memory leak clk: ti: change clock init to use generic of_clk_init clk: hix5hd2: add I2C clocks clk: hix5hd2: add watchdog0 clocks ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig16
-rw-r--r--drivers/clk/Makefile4
-rw-r--r--drivers/clk/at91/clk-pll.c160
-rw-r--r--drivers/clk/at91/clk-usb.c20
-rw-r--r--drivers/clk/clk-axi-clkgen.c1
-rw-r--r--drivers/clk/clk-fractional-divider.c2
-rw-r--r--drivers/clk/clk-gate.c2
-rw-r--r--drivers/clk/clk-gpio-gate.c205
-rw-r--r--drivers/clk/clk-max-gen.c192
-rw-r--r--drivers/clk/clk-max-gen.h32
-rw-r--r--drivers/clk/clk-max77686.c184
-rw-r--r--drivers/clk/clk-max77802.c97
-rw-r--r--drivers/clk/clk-palmas.c1
-rw-r--r--drivers/clk/clk-twl6040.c1
-rw-r--r--drivers/clk/clk-wm831x.c1
-rw-r--r--drivers/clk/clk.c168
-rw-r--r--drivers/clk/hisilicon/clk-hix5hd2.c232
-rw-r--r--drivers/clk/mvebu/armada-370.c8
-rw-r--r--drivers/clk/mvebu/armada-375.c4
-rw-r--r--drivers/clk/mvebu/common.c91
-rw-r--r--drivers/clk/mvebu/common.h9
-rw-r--r--drivers/clk/mvebu/kirkwood.c102
-rw-r--r--drivers/clk/pxa/Makefile2
-rw-r--r--drivers/clk/pxa/clk-pxa.c97
-rw-r--r--drivers/clk/pxa/clk-pxa.h107
-rw-r--r--drivers/clk/pxa/clk-pxa27x.c370
-rw-r--r--drivers/clk/qcom/clk-pll.c68
-rw-r--r--drivers/clk/qcom/clk-pll.h20
-rw-r--r--drivers/clk/qcom/clk-rcg.c115
-rw-r--r--drivers/clk/qcom/clk-rcg.h6
-rw-r--r--drivers/clk/qcom/clk-rcg2.c19
-rw-r--r--drivers/clk/qcom/common.c16
-rw-r--r--drivers/clk/qcom/common.h4
-rw-r--r--drivers/clk/qcom/gcc-ipq806x.c31
-rw-r--r--drivers/clk/qcom/mmcc-apq8084.c1
-rw-r--r--drivers/clk/qcom/mmcc-msm8960.c29
-rw-r--r--drivers/clk/qcom/mmcc-msm8974.c1
-rw-r--r--drivers/clk/rockchip/Makefile1
-rw-r--r--drivers/clk/rockchip/clk-cpu.c329
-rw-r--r--drivers/clk/rockchip/clk-pll.c63
-rw-r--r--drivers/clk/rockchip/clk-rk3188.c171
-rw-r--r--drivers/clk/rockchip/clk-rk3288.c203
-rw-r--r--drivers/clk/rockchip/clk.c119
-rw-r--r--drivers/clk/rockchip/clk.h39
-rw-r--r--drivers/clk/samsung/clk-exynos3250.c202
-rw-r--r--drivers/clk/samsung/clk-exynos4.c18
-rw-r--r--drivers/clk/samsung/clk-exynos5260.c2
-rw-r--r--drivers/clk/samsung/clk-s3c2410-dclk.c1
-rw-r--r--drivers/clk/samsung/clk-s3c2412.c29
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c19
-rw-r--r--drivers/clk/shmobile/clk-rcar-gen2.c1
-rw-r--r--drivers/clk/sunxi/Makefile2
-rw-r--r--drivers/clk/sunxi/clk-factors.c101
-rw-r--r--drivers/clk/sunxi/clk-factors.h16
-rw-r--r--drivers/clk/sunxi/clk-mod0.c283
-rw-r--r--drivers/clk/sunxi/clk-sun6i-apb0-gates.c1
-rw-r--r--drivers/clk/sunxi/clk-sun6i-apb0.c1
-rw-r--r--drivers/clk/sunxi/clk-sun6i-ar100.c1
-rw-r--r--drivers/clk/sunxi/clk-sun8i-apb0.c1
-rw-r--r--drivers/clk/sunxi/clk-sun8i-mbus.c78
-rw-r--r--drivers/clk/sunxi/clk-sunxi.c161
-rw-r--r--drivers/clk/tegra/clk-tegra124.c8
-rw-r--r--drivers/clk/tegra/clk.c9
-rw-r--r--drivers/clk/ti/clk-dra7-atl.c3
-rw-r--r--drivers/clk/ti/clk.c68
-rw-r--r--drivers/clk/ti/clockdomain.c5
-rw-r--r--drivers/clk/ti/divider.c4
-rw-r--r--drivers/clk/zynq/clkc.c30
-rw-r--r--drivers/clk/zynq/pll.c4
-rw-r--r--drivers/cpufreq/kirkwood-cpufreq.c14
70 files changed, 3632 insertions, 773 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 84e0590e31dc..455fd17d938e 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -32,12 +32,23 @@ config COMMON_CLK_WM831X
source "drivers/clk/versatile/Kconfig"
+config COMMON_CLK_MAX_GEN
+ bool
+
config COMMON_CLK_MAX77686
tristate "Clock driver for Maxim 77686 MFD"
depends on MFD_MAX77686
+ select COMMON_CLK_MAX_GEN
---help---
This driver supports Maxim 77686 crystal oscillator clock.
+config COMMON_CLK_MAX77802
+ tristate "Clock driver for Maxim 77802 PMIC"
+ depends on MFD_MAX77686
+ select COMMON_CLK_MAX_GEN
+ ---help---
+ This driver supports Maxim 77802 crystal oscillator clock.
+
config COMMON_CLK_RK808
tristate "Clock driver for RK808"
depends on MFD_RK808
@@ -118,6 +129,11 @@ config COMMON_CLK_PALMAS
This driver supports TI Palmas devices 32KHz output KG and KG_AUDIO
using common clock framework.
+config COMMON_CLK_PXA
+ def_bool COMMON_CLK && ARCH_PXA
+ ---help---
+ Sypport for the Marvell PXA SoC.
+
source "drivers/clk/qcom/Kconfig"
endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 99f53d5f8766..d5fba5bc6e1b 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-gate.o
obj-$(CONFIG_COMMON_CLK) += clk-mux.o
obj-$(CONFIG_COMMON_CLK) += clk-composite.o
obj-$(CONFIG_COMMON_CLK) += clk-fractional-divider.o
+obj-$(CONFIG_COMMON_CLK) += clk-gpio-gate.o
ifeq ($(CONFIG_OF), y)
obj-$(CONFIG_COMMON_CLK) += clk-conf.o
endif
@@ -22,7 +23,9 @@ obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
+obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
+obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o
obj-$(CONFIG_ARCH_MOXART) += clk-moxart.o
obj-$(CONFIG_ARCH_NOMADIK) += clk-nomadik.o
obj-$(CONFIG_ARCH_NSPIRE) += clk-nspire.o
@@ -49,6 +52,7 @@ obj-$(CONFIG_ARCH_MMP) += mmp/
endif
obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
+obj-$(CONFIG_COMMON_CLK_PXA) += pxa/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
index cf6ed023504c..6ec79dbc0840 100644
--- a/drivers/clk/at91/clk-pll.c
+++ b/drivers/clk/at91/clk-pll.c
@@ -15,6 +15,7 @@
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/io.h>
+#include <linux/kernel.h>
#include <linux/wait.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
@@ -29,9 +30,12 @@
#define PLL_DIV(reg) ((reg) & PLL_DIV_MASK)
#define PLL_MUL(reg, layout) (((reg) >> (layout)->mul_shift) & \
(layout)->mul_mask)
+#define PLL_MUL_MIN 2
+#define PLL_MUL_MASK(layout) ((layout)->mul_mask)
+#define PLL_MUL_MAX(layout) (PLL_MUL_MASK(layout) + 1)
#define PLL_ICPR_SHIFT(id) ((id) * 16)
#define PLL_ICPR_MASK(id) (0xffff << PLL_ICPR_SHIFT(id))
-#define PLL_MAX_COUNT 0x3ff
+#define PLL_MAX_COUNT 0x3f
#define PLL_COUNT_SHIFT 8
#define PLL_OUT_SHIFT 14
#define PLL_MAX_ID 1
@@ -147,115 +151,113 @@ static unsigned long clk_pll_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_pll *pll = to_clk_pll(hw);
- const struct clk_pll_layout *layout = pll->layout;
- struct at91_pmc *pmc = pll->pmc;
- int offset = PLL_REG(pll->id);
- u32 tmp = pmc_read(pmc, offset) & layout->pllr_mask;
- u8 div = PLL_DIV(tmp);
- u16 mul = PLL_MUL(tmp, layout);
- if (!div || !mul)
+
+ if (!pll->div || !pll->mul)
return 0;
- return (parent_rate * (mul + 1)) / div;
+ return (parent_rate / pll->div) * (pll->mul + 1);
}
static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
unsigned long parent_rate,
u32 *div, u32 *mul,
u32 *index) {
- unsigned long maxrate;
- unsigned long minrate;
- unsigned long divrate;
- unsigned long bestdiv = 1;
- unsigned long bestmul;
- unsigned long tmpdiv;
- unsigned long roundup;
- unsigned long rounddown;
- unsigned long remainder;
- unsigned long bestremainder;
- unsigned long maxmul;
- unsigned long maxdiv;
- unsigned long mindiv;
- int i = 0;
const struct clk_pll_layout *layout = pll->layout;
const struct clk_pll_characteristics *characteristics =
pll->characteristics;
+ unsigned long bestremainder = ULONG_MAX;
+ unsigned long maxdiv, mindiv, tmpdiv;
+ long bestrate = -ERANGE;
+ unsigned long bestdiv;
+ unsigned long bestmul;
+ int i = 0;
- /* Minimum divider = 1 */
- /* Maximum multiplier = max_mul */
- maxmul = layout->mul_mask + 1;
- maxrate = (parent_rate * maxmul) / 1;
-
- /* Maximum divider = max_div */
- /* Minimum multiplier = 2 */
- maxdiv = PLL_DIV_MAX;
- minrate = (parent_rate * 2) / maxdiv;
-
+ /* Check if parent_rate is a valid input rate */
if (parent_rate < characteristics->input.min ||
- parent_rate < characteristics->input.max)
- return -ERANGE;
-
- if (parent_rate < minrate || parent_rate > maxrate)
+ parent_rate > characteristics->input.max)
return -ERANGE;
- for (i = 0; i < characteristics->num_output; i++) {
- if (parent_rate >= characteristics->output[i].min &&
- parent_rate <= characteristics->output[i].max)
- break;
- }
-
- if (i >= characteristics->num_output)
- return -ERANGE;
-
- bestmul = rate / parent_rate;
- rounddown = parent_rate % rate;
- roundup = rate - rounddown;
- bestremainder = roundup < rounddown ? roundup : rounddown;
-
- if (!bestremainder) {
- if (div)
- *div = bestdiv;
- if (mul)
- *mul = bestmul;
- if (index)
- *index = i;
- return rate;
- }
-
- maxdiv = 255 / (bestmul + 1);
- if (parent_rate / maxdiv < characteristics->input.min)
- maxdiv = parent_rate / characteristics->input.min;
- mindiv = parent_rate / characteristics->input.max;
- if (parent_rate % characteristics->input.max)
- mindiv++;
-
- for (tmpdiv = mindiv; tmpdiv < maxdiv; tmpdiv++) {
- divrate = parent_rate / tmpdiv;
-
- rounddown = rate % divrate;
- roundup = divrate - rounddown;
- remainder = roundup < rounddown ? roundup : rounddown;
-
+ /*
+ * Calculate minimum divider based on the minimum multiplier, the
+ * parent_rate and the requested rate.
+ * Should always be 2 according to the input and output characteristics
+ * of the PLL blocks.
+ */
+ mindiv = (parent_rate * PLL_MUL_MIN) / rate;
+ if (!mindiv)
+ mindiv = 1;
+
+ /*
+ * Calculate the maximum divider which is limited by PLL register
+ * layout (limited by the MUL or DIV field size).
+ */
+ maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate);
+ if (maxdiv > PLL_DIV_MAX)
+ maxdiv = PLL_DIV_MAX;
+
+ /*
+ * Iterate over the acceptable divider values to find the best
+ * divider/multiplier pair (the one that generates the closest
+ * rate to the requested one).
+ */
+ for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
+ unsigned long remainder;
+ unsigned long tmprate;
+ unsigned long tmpmul;
+
+ /*
+ * Calculate the multiplier associated with the current
+ * divider that provide the closest rate to the requested one.
+ */
+ tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv);
+ tmprate = (parent_rate / tmpdiv) * tmpmul;
+ if (tmprate > rate)
+ remainder = tmprate - rate;
+ else
+ remainder = rate - tmprate;
+
+ /*
+ * Compare the remainder with the best remainder found until
+ * now and elect a new best multiplier/divider pair if the
+ * current remainder is smaller than the best one.
+ */
if (remainder < bestremainder) {
bestremainder = remainder;
- bestmul = rate / divrate;
bestdiv = tmpdiv;
+ bestmul = tmpmul;
+ bestrate = tmprate;
}
+ /*
+ * We've found a perfect match!
+ * Stop searching now and use this multiplier/divider pair.
+ */
if (!remainder)
break;
}
- rate = (parent_rate / bestdiv) * bestmul;
+ /* We haven't found any multiplier/divider pair => return -ERANGE */
+ if (bestrate < 0)
+ return bestrate;
+
+ /* Check if bestrate is a valid output rate */
+ for (i = 0; i < characteristics->num_output; i++) {
+ if (bestrate >= characteristics->output[i].min &&
+ bestrate <= characteristics->output[i].max)
+ break;
+ }
+
+ if (i >= characteristics->num_output)
+ return -ERANGE;
if (div)
*div = bestdiv;
if (mul)
- *mul = bestmul;
+ *mul = bestmul - 1;
if (index)
*index = i;
- return rate;
+ return bestrate;
}
static long clk_pll_round_rate(struct clk_hw *hw, unsigned long rate,
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
index 7d1d26a4bd04..24b5b020753a 100644
--- a/drivers/clk/at91/clk-usb.c
+++ b/drivers/clk/at91/clk-usb.c
@@ -238,16 +238,22 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
+ struct clk *parent = __clk_get_parent(hw->clk);
unsigned long bestrate = 0;
int bestdiff = -1;
unsigned long tmprate;
int tmpdiff;
int i = 0;
- for (i = 0; i < 4; i++) {
+ for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+ unsigned long tmp_parent_rate;
+
if (!usb->divisors[i])
continue;
- tmprate = *parent_rate / usb->divisors[i];
+
+ tmp_parent_rate = rate * usb->divisors[i];
+ tmp_parent_rate = __clk_round_rate(parent, tmp_parent_rate);
+ tmprate = tmp_parent_rate / usb->divisors[i];
if (tmprate < rate)
tmpdiff = rate - tmprate;
else
@@ -256,6 +262,7 @@ static long at91rm9200_clk_usb_round_rate(struct clk_hw *hw, unsigned long rate,
if (bestdiff < 0 || bestdiff > tmpdiff) {
bestrate = tmprate;
bestdiff = tmpdiff;
+ *parent_rate = tmp_parent_rate;
}
if (!bestdiff)
@@ -272,10 +279,13 @@ static int at91rm9200_clk_usb_set_rate(struct clk_hw *hw, unsigned long rate,
int i;
struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(hw);
struct at91_pmc *pmc = usb->pmc;
- unsigned long div = parent_rate / rate;
+ unsigned long div;
- if (parent_rate % rate)
+ if (!rate || parent_rate % rate)
return -EINVAL;
+
+ div = parent_rate / rate;
+
for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
if (usb->divisors[i] == div) {
tmp = pmc_read(pmc, AT91_CKGR_PLLBR) &
@@ -311,7 +321,7 @@ at91rm9200_clk_register_usb(struct at91_pmc *pmc, const char *name,
init.ops = &at91rm9200_usb_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
- init.flags = 0;
+ init.flags = CLK_SET_RATE_PARENT;
usb->hw.init = &init;
usb->pmc = pmc;
diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c
index 1127ee46b802..e619285c6def 100644
--- a/drivers/clk/clk-axi-clkgen.c
+++ b/drivers/clk/clk-axi-clkgen.c
@@ -544,7 +544,6 @@ static int axi_clkgen_remove(struct platform_device *pdev)
static struct platform_driver axi_clkgen_driver = {
.driver = {
.name = "adi-axi-clkgen",
- .owner = THIS_MODULE,
.of_match_table = axi_clkgen_ids,
},
.probe = axi_clkgen_probe,
diff --git a/drivers/clk/clk-fractional-divider.c b/drivers/clk/clk-fractional-divider.c
index ede685ca0d20..82a59d0086cc 100644
--- a/drivers/clk/clk-fractional-divider.c
+++ b/drivers/clk/clk-fractional-divider.c
@@ -36,7 +36,7 @@ static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
m = (val & fd->mmask) >> fd->mshift;
n = (val & fd->nmask) >> fd->nshift;
- ret = parent_rate * m;
+ ret = (u64)parent_rate * m;
do_div(ret, n);
return ret;
diff --git a/drivers/clk/clk-gate.c b/drivers/clk/clk-gate.c
index 4a58c55255bd..51fd87fb7ba6 100644
--- a/drivers/clk/clk-gate.c
+++ b/drivers/clk/clk-gate.c
@@ -45,7 +45,7 @@ static void clk_gate_endisable(struct clk_hw *hw, int enable)
{
struct clk_gate *gate = to_clk_gate(hw);
int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
- unsigned long flags = 0;
+ unsigned long uninitialized_var(flags);
u32 reg;
set ^= enable;
diff --git a/drivers/clk/clk-gpio-gate.c b/drivers/clk/clk-gpio-gate.c
new file mode 100644
index 000000000000..08e43224fd52
--- /dev/null
+++ b/drivers/clk/clk-gpio-gate.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2013 - 2014 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Jyri Sarha <jsarha@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.
+ *
+ * Gpio gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_gpio.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+/**
+ * DOC: basic gpio gated clock which can be enabled and disabled
+ * with gpio output
+ * Traits of this clock:
+ * prepare - clk_(un)prepare only ensures parent is (un)prepared
+ * enable - clk_enable and clk_disable are functional & control gpio
+ * rate - inherits rate from parent. No clk_set_rate support
+ * parent - fixed parent. No clk_set_parent support
+ */
+
+#define to_clk_gpio(_hw) container_of(_hw, struct clk_gpio, hw)
+
+static int clk_gpio_gate_enable(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value(clk->gpiod, 1);
+
+ return 0;
+}
+
+static void clk_gpio_gate_disable(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ gpiod_set_value(clk->gpiod, 0);
+}
+
+static int clk_gpio_gate_is_enabled(struct clk_hw *hw)
+{
+ struct clk_gpio *clk = to_clk_gpio(hw);
+
+ return gpiod_get_value(clk->gpiod);
+}
+
+const struct clk_ops clk_gpio_gate_ops = {
+ .enable = clk_gpio_gate_enable,
+ .disable = clk_gpio_gate_disable,
+ .is_enabled = clk_gpio_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(clk_gpio_gate_ops);
+
+/**
+ * clk_register_gpio - register a gpip clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @parent_name: name of this clock's parent
+ * @gpiod: gpio descriptor to gate this clock
+ */
+struct clk *clk_register_gpio_gate(struct device *dev, const char *name,
+ const char *parent_name, struct gpio_desc *gpiod,
+ unsigned long flags)
+{
+ struct clk_gpio *clk_gpio = NULL;
+ struct clk *clk = ERR_PTR(-EINVAL);
+ struct clk_init_data init = { NULL };
+ unsigned long gpio_flags;
+ int err;
+
+ if (gpiod_is_active_low(gpiod))
+ gpio_flags = GPIOF_OUT_INIT_HIGH;
+ else
+ gpio_flags = GPIOF_OUT_INIT_LOW;
+
+ if (dev)
+ err = devm_gpio_request_one(dev, desc_to_gpio(gpiod),
+ gpio_flags, name);
+ else
+ err = gpio_request_one(desc_to_gpio(gpiod), gpio_flags, name);
+
+ if (err) {
+ pr_err("%s: %s: Error requesting clock control gpio %u\n",
+ __func__, name, desc_to_gpio(gpiod));
+ return ERR_PTR(err);
+ }
+
+ if (dev)
+ clk_gpio = devm_kzalloc(dev, sizeof(struct clk_gpio),
+ GFP_KERNEL);
+ else
+ clk_gpio = kzalloc(sizeof(struct clk_gpio), GFP_KERNEL);
+
+ if (!clk_gpio) {
+ clk = ERR_PTR(-ENOMEM);
+ goto clk_register_gpio_gate_err;
+ }
+
+ init.name = name;
+ init.ops = &clk_gpio_gate_ops;
+ init.flags = flags | CLK_IS_BASIC;
+ init.parent_names = (parent_name ? &parent_name : NULL);
+ init.num_parents = (parent_name ? 1 : 0);