diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 11:34:35 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-06-26 11:34:35 -0700 |
| commit | 4aa705b18bf17c4ff33ff7bbcd3f0c596443fa81 (patch) | |
| tree | 3b166bff290d123ccaa88598ad2d45be67f5b358 /drivers/clk/imx/clk-fixup-div.c | |
| parent | c11d716218910c3aa2bac1bb641e6086ad649555 (diff) | |
| parent | 2879e43f09122f8b3ef5456e3d7e48716b086e60 (diff) | |
Merge tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC platform support updates from Kevin Hilman:
"Our SoC branch usually contains expanded support for new SoCs and
other core platform code. Some highlights from this round:
- sunxi: SMP support for A23 SoC
- socpga: big-endian support
- pxa: conversion to common clock framework
- bcm: SMP support for BCM63138
- imx: support new I.MX7D SoC
- zte: basic support for ZX296702 SoC"
* tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (134 commits)
ARM: zx: Add basic defconfig support for ZX296702
ARM: dts: zx: add an initial zx296702 dts and doc
clk: zx: add clock support to zx296702
dt-bindings: Add #defines for ZTE ZX296702 clocks
ARM: socfpga: fix build error due to secondary_startup
MAINTAINERS: ARM64: EXYNOS: Extend entry for ARM64 DTS
ARM: ep93xx: simone: support for SPI-based MMC/SD cards
MAINTAINERS: update Shawn's email to use kernel.org one
ARM: socfpga: support suspend to ram
ARM: socfpga: add CPU_METHOD_OF_DECLARE for Arria 10
ARM: socfpga: use CPU_METHOD_OF_DECLARE for socfpga_cyclone5
ARM: EXYNOS: register power domain driver from core_initcall
ARM: EXYNOS: use PS_HOLD based poweroff for all supported SoCs
ARM: SAMSUNG: Constify platform_device_id
ARM: EXYNOS: Constify irq_domain_ops
ARM: EXYNOS: add coupled cpuidle support for Exynos3250
ARM: EXYNOS: add exynos_get_boot_addr() helper
ARM: EXYNOS: add exynos_set_boot_addr() helper
ARM: EXYNOS: make exynos_core_restart() less verbose
ARM: EXYNOS: fix exynos_boot_secondary() return value on timeout
...
Diffstat (limited to 'drivers/clk/imx/clk-fixup-div.c')
| -rw-r--r-- | drivers/clk/imx/clk-fixup-div.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/drivers/clk/imx/clk-fixup-div.c b/drivers/clk/imx/clk-fixup-div.c new file mode 100644 index 000000000000..21db020b1f2d --- /dev/null +++ b/drivers/clk/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; +} |
