diff options
Diffstat (limited to 'drivers')
57 files changed, 2494 insertions, 632 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a47e6ee98b8c..a64caefdba12 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -63,6 +63,14 @@ config CLK_TWL6040 McPDM. McPDM module is using the external bit clock on the McPDM bus as functional clock. +config COMMON_CLK_AXI_CLKGEN + tristate "AXI clkgen driver" + depends on ARCH_ZYNQ || MICROBLAZE + help + ---help--- + Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx + FPGAs. It is commonly used in Analog Devices' reference designs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d4775d926..79e98e416724 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o +obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o @@ -23,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o @@ -31,6 +33,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_X86) += x86/ # Chip specific +obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o 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-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c new file mode 100644 index 000000000000..8137327847c3 --- /dev/null +++ b/drivers/clk/clk-axi-clkgen.c @@ -0,0 +1,331 @@ +/* + * AXI clkgen driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen <lars@metafoo.de> + * + * Licensed under the GPL-2. + * + */ + +#include <linux/platform_device.h> +#include <linux/clk-provider.h> +#include <linux/clk.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/module.h> +#include <linux/err.h> + +#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_REG_LOCK1 0x1c +#define AXI_CLKGEN_REG_LOCK2 0x20 +#define AXI_CLKGEN_REG_LOCK3 0x24 +#define AXI_CLKGEN_REG_FILTER1 0x28 +#define AXI_CLKGEN_REG_FILTER2 0x2c + +struct axi_clkgen { + void __iomem *base; + struct clk_hw clk_hw; +}; + +static uint32_t axi_clkgen_lookup_filter(unsigned int m) +{ + switch (m) { + case 0: + return 0x01001990; + case 1: + return 0x01001190; + case 2: + return 0x01009890; + case 3: + return 0x01001890; + case 4: + return 0x01008890; + case 5 ... 8: + return 0x01009090; + case 9 ... 11: + return 0x01000890; + case 12: + return 0x08009090; + case 13 ... 22: + return 0x01001090; + case 23 ... 36: + return 0x01008090; + case 37 ... 46: + return 0x08001090; + default: + return 0x08008090; + } +} + +static const uint32_t axi_clkgen_lock_table[] = { + 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, + 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, + 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, + 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271, + 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4, + 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190, + 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e, + 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c, + 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, +}; + +static uint32_t axi_clkgen_lookup_lock(unsigned int m) +{ + if (m < ARRAY_SIZE(axi_clkgen_lock_table)) + return axi_clkgen_lock_table[m]; + return 0x1f1f00fa; +} + +static const unsigned int fpfd_min = 10000; +static const unsigned int fpfd_max = 300000; +static const unsigned int fvco_min = 600000; +static const unsigned int fvco_max = 1200000; + +static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) +{ + unsigned long d, d_min, d_max, _d_min, _d_max; + unsigned long m, m_min, m_max; + unsigned long f, dout, best_f, fvco; + + fin /= 1000; + fout /= 1000; + + best_f = ULONG_MAX; + *best_d = 0; + *best_m = 0; + *best_dout = 0; + + d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); + d_max = min_t(unsigned long, fin / fpfd_min, 80); + + m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); + m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); + + for (m = m_min; m <= m_max; m++) { + _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); + _d_max = min(d_max, fin * m / fvco_min); + + for (d = _d_min; d <= _d_max; d++) { + fvco = fin * m / d; + + dout = DIV_ROUND_CLOSEST(fvco, fout); + dout = clamp_t(unsigned long, dout, 1, 128); + f = fvco / dout; + if (abs(f - fout) < abs(best_f - fout)) { + best_f = f; + *best_d = d; + *best_m = m; + *best_dout = dout; + if (best_f == fout) + return; + } + } + } +} + +static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, + unsigned int *high, unsigned int *edge, unsigned int *nocount) +{ + if (divider == 1) + *nocount = 1; + else + *nocount = 0; + + *high = divider / 2; + *edge = divider % 2; + *low = divider - *high; +} + +static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val) +{ + writel(val, axi_clkgen->base + reg); +} + +static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + *val = readl(axi_clkgen->base + reg); +} + +static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) +{ + return container_of(clk_hw, struct axi_clkgen, clk_hw); +} + +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, + unsigned long rate, unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int nocount; + unsigned int high; + unsigned int edge; + unsigned int low; + uint32_t filter; + uint32_t lock; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + filter = axi_clkgen_lookup_filter(m - 1); + lock = axi_clkgen_lookup_lock(m - 1); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); + + axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low); + + axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + + return 0; +} + +static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int d, m, dout; + + axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + return *parent_rate / d * m / dout; +} + +static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, + unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int reg; + unsigned long long tmp; + + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + d = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + + if (d == 0 || dout == 0) + return 0; + + tmp = (unsigned long long)(parent_rate / d) * m; + do_div(tmp, dout); + + if (tmp > ULONG_MAX) + return ULONG_MAX; + + return tmp; +} + +static const struct clk_ops axi_clkgen_ops = { + .recalc_rate = axi_clkgen_recalc_rate, + .round_rate = axi_clkgen_round_rate, + .set_rate = axi_clkgen_set_rate, +}; + +static int axi_clkgen_probe(struct platform_device *pdev) +{ + struct axi_clkgen *axi_clkgen; + struct clk_init_data init; + const char *parent_name; + const char *clk_name; + struct resource *mem; + struct clk *clk; + + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); + if (!axi_clkgen) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(axi_clkgen->base)) + return PTR_ERR(axi_clkgen->base); + + parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + if (!parent_name) + return -EINVAL; + + clk_name = pdev->dev.of_node->name; + of_property_read_string(pdev->dev.of_node, "clock-output-names", + &clk_name); + + init.name = clk_name; + init.ops = &axi_clkgen_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + axi_clkgen->clk_hw.init = &init; + clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + clk); +} + +static int axi_clkgen_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id axi_clkgen_ids[] = { + { .compatible = "adi,axi-clkgen-1.00.a" }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + +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, + .remove = axi_clkgen_remove, +}; +module_platform_driver(axi_clkgen_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); +MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator"); diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c new file mode 100644 index 000000000000..097dee4fd209 --- /dev/null +++ b/drivers/clk/clk-composite.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/slab.h> + +#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + +static u8 clk_composite_get_parent(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->get_parent(mux_hw); +} + +static int clk_composite_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->set_parent(mux_hw, index); +} + +static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->recalc_rate(div_hw, parent_rate); +} + +static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->round_rate(div_hw, rate, prate); +} + +static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->set_rate(div_hw, rate, parent_rate); +} + +static int clk_composite_is_enabled(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_composite_enable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->enable(gate_hw); +} + +static void clk_composite_disable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + gate_ops->disable(gate_hw); +} + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char **parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *div_hw, const struct clk_ops *div_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk *clk; + struct clk_init_data init; + struct clk_composite *composite; + struct clk_ops *clk_composite_ops; + + composite = kzalloc(sizeof(*composite), GFP_KERNEL); + if (!composite) { + pr_err("%s: could not allocate composite clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clk_composite_ops = &composite->ops; + + if (mux_hw && mux_ops) { + if (!mux_ops->get_parent || !mux_ops->set_parent) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->mux_hw = mux_hw; + composite->mux_ops = mux_ops; + clk_composite_ops->get_parent = clk_composite_get_parent; + clk_composite_ops->set_parent = clk_composite_set_parent; + } + + if (div_hw && div_ops) { + if (!div_ops->recalc_rate || !div_ops->round_rate || + !div_ops->set_rate) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->div_hw = div_hw; + composite->div_ops = div_ops; + clk_composite_ops->recalc_rate = clk_composite_recalc_rate; + clk_composite_ops->round_rate = clk_composite_round_rate; + clk_composite_ops->set_rate = clk_composite_set_rate; + } + + if (gate_hw && gate_ops) { + if (!gate_ops->is_enabled || !gate_ops->enable || + !gate_ops->disable) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; + clk_composite_ops->is_enabled = clk_composite_is_enabled; + clk_composite_ops->enable = clk_composite_enable; + clk_composite_ops->disable = clk_composite_disable; + } + + init.ops = clk_composite_ops; + composite->hw.init = &init; + + clk = clk_register(dev, &composite->hw); + if (IS_ERR(clk)) + goto err; + + if (composite->mux_hw) + composite->mux_hw->clk = clk; + + if (composite->div_hw) + composite->div_hw->clk = clk; + + if (composite->gate_hw) + composite->gate_hw->clk = clk; + + return clk; + +err: + kfree(composite); + return clk; +} diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 68b402101170..6d9674160430 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -109,8 +109,9 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, div = _get_div(divider, val); if (!div) { - WARN(1, "%s: Invalid divisor for clock %s\n", __func__, - __clk_get_name(hw->clk)); + WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), + "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + __clk_get_name(hw->clk)); return parent_rate; } diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 508c032edce4..25b1734560d0 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -32,6 +32,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) { struct clk_mux *mux = to_clk_mux(hw); + int num_parents = __clk_get_num_parents(hw->clk); u32 val; /* @@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) * val = 0x4 really means "bit 2, index starts at bit 0" */ val = readl(mux->reg) >> mux->shift; - val &= (1 << mux->width) - 1; + val &= mux->mask; + + if (mux->table) { + int i; + + for (i = 0; i < num_parents; i++) + if (mux->table[i] == val) + return i; + return -EINVAL; + } if (val && (mux->flags & CLK_MUX_INDEX_BIT)) val = ffs(val) - 1; @@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) if (val && (mux->flags & CLK_MUX_INDEX_ONE)) val--; - if (val >= __clk_get_num_parents(hw->clk)) + if (val >= num_parents) return -EINVAL; return val; @@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) u32 val; unsigned long flags = 0; - if (mux->flags & CLK_MUX_INDEX_BIT) - index = (1 << ffs(index)); + if (mux->table) + index = mux->table[index]; - if (mux->flags & CLK_MUX_INDEX_ONE) - index++; + else { + if (mux->flags & CLK_MUX_INDEX_BIT) + index = (1 << ffs(index)); + + if (mux->flags & CLK_MUX_INDEX_ONE) + index++; + } if (mux->lock) spin_lock_irqsave(mux->lock, flags); val = readl(mux->reg); - val &= ~(((1 << mux->width) - 1) << mux->shift); + val &= ~(mux->mask << mux->shift); val |= index << mux->shift; writel(val, mux->reg); @@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ops); -struct clk *clk_register_mux(struct device *dev, const char *name, +struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags, spinlock_t *lock) + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; struct clk *clk; @@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name, /* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; - mux->width = width; + mux->mask = mask; mux->flags = clk_mux_flags; mux->lock = lock; + mux->table = table; mux->hw.init = &init; clk = clk_register(dev, &mux->hw); @@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name, return clk; } + +struct clk *clk_register_mux(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock) +{ + u32 mask = BIT(width) - 1; + + return clk_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL, lock); +} diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index f8e9d0c27be2..643ca653fef0 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void) for (i = pll1; i < maxclk; i++) { prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); - BUG_ON(!prima2_clks[i]); + BUG_ON(IS_ERR(prima2_clks[i])); } clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); clk_register_clkdev(prima2_clks[io], NULL, "io"); diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c index b14a25f39255..32062977f453 100644 --- a/drivers/clk/clk-zynq.c +++ b/drivers/clk/clk-zynq.c @@ -20,6 +20,7 @@ #include <linux/slab.h> #include <linux/kernel.h> #include <linux/clk-provider.h> +#include <linux/clk/zynq.h> static void __iomem *slcr_base; diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ed87b2405806..0230c9d95975 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -19,14 +19,77 @@ #include <linux/of.h> #include <linux/device.h> #include <linux/init.h> +#include <linux/sched.h> static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); +static struct task_struct *prepare_owner; +static struct task_struct *enable_owner; + +static int prepare_refcnt; +static int enable_refcnt; + static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); +/*** locking ***/ +static void clk_prepare_lock(void) +{ + if (!mutex_trylock(&prepare_lock)) { + if (prepare_owner == current) { + prepare_refcnt++; + return; + } + mutex_lock(&prepare_lock); + } + WARN_ON_ONCE(prepare_owner != NULL); + WARN_ON_ONCE(prepare_refcnt != 0); + prepare_owner = current; + prepare_refcnt = 1; +} + +static void clk_prepare_unlock(void) +{ + WARN_ON_ONCE(prepare_owner != current); + WARN_ON_ONCE(prepare_refcnt == 0); + + if (--prepare_refcnt) + return; + prepare_owner = NULL; + mutex_unlock(&prepare_lock); +} + +static unsigned long clk_enable_lock(void) +{ + unsigned long flags; + + if (!spin_trylock_irqsave(&enable_lock, flags)) { + if (enable_owner == current) { + enable_refcnt++; + return flags; + } + spin_lock_irqsave(&enable_lock, flags); + } + WARN_ON_ONCE(enable_owner != NULL); + WARN_ON_ONCE(enable_refcnt != 0); + enable_owner = current; + enable_refcnt = 1; + return flags; +} + +static void clk_enable_unlock(unsigned long flags) +{ + WARN_ON_ONCE(enable_owner != current); + WARN_ON_ONCE(enable_refcnt == 0); + + if (--enable_refcnt) + return; + enable_owner = NULL; + spin_unlock_irqrestore(&enable_lock, flags); +} + /*** debugfs support ***/ #ifdef CONFIG_COMMON_CLK_DEBUG @@ -69,7 +132,7 @@ static int clk_summary_show(struct seq_file *s, void *data) seq_printf(s, " clock enable_cnt prepare_cnt rate\n"); seq_printf(s, "---------------------------------------------------------------------\n"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) clk_summary_show_subtree(s, c, 0); @@ -77,7 +140,7 @@ static int clk_summary_show(struct seq_file *s, void *data) hlist_for_each_entry(c, &clk_orphan_list, child_node) clk_summary_show_subtree(s, c, 0); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -130,7 +193,7 @@ static int clk_dump(struct seq_file *s, void *data) seq_printf(s, "{"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) { if (!first_node) @@ -144,7 +207,7 @@ static int clk_dump(struct seq_file *s, void *data) clk_dump_subtree(s, c, 0); } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); seq_printf(s, "}"); return 0; @@ -316,7 +379,7 @@ static int __init clk_debug_init(void) if (!orphandir) return -ENOMEM; - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_debug_create_subtree(clk, rootdir); @@ -326,7 +389,7 @@ static int __init clk_debug_init(void) inited = 1; - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -336,6 +399,31 @@ static inline int clk_debug_register(struct clk *clk) { return 0; } #endif /* caller must hold prepare_lock */ +static void clk_unprepare_unused_subtree(struct clk *clk) +{ + struct clk *child; + + if (!clk) + return; + + hlist_for_each_entry(child, &clk->children, child_node) + clk_unprepare_unused_subtree(child); + + if (clk->prepare_count) + return; + + if (clk->flags & CLK_IGNORE_UNUSED) + return; + + if (__clk_is_prepared(clk)) { + if (clk->ops->unprepare_unused) + clk->ops->unprepare_unused(clk->hw); + else if (clk->ops->unprepare) + clk->ops->unprepare(clk->hw); + } +} + +/* caller must hold prepare_lock */ static void clk_disable_unused_subtree(struct clk *clk) { struct clk *child; @@ -347,7 +435,7 @@ static void clk_disable_unused_subtree(struct clk *clk) hlist_for_each_entry(child, &clk->children, child_node) clk_disable_unused_subtree(child); - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) goto unlock_out; @@ -368,7 +456,7 @@ static void clk_disable_unused_subtree(struct clk *clk) } unlock_out: - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); out: return; @@ -378,7 +466,7 @@ static int clk_disable_unused(void) { struct clk *clk; - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_disable_unused_subtree(clk); @@ -386,7 +474,13 @@ static int clk_disable_unused(void) hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_disable_unused_subtree(clk); - mutex_unlock(&prepare_lock); + hlist_for_each_entry(clk, &clk_root_list, child_node) + clk_unprepare_unused_subtree(clk); + + hlist_for_each_entry(clk, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(clk); + + clk_prepare_unlock(); return 0; } @@ -451,6 +545,27 @@ unsigned long __clk_get_flags(struct clk *clk) return !clk ? 0 : clk->flags; } +bool __clk_is_prepared(struct clk *clk) +{ + int ret; + + if (!clk) + return false; + + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!clk->ops->is_prepared) { + ret = clk->prepare_count ? 1 : 0; + goto out; + } + + ret = clk->ops->is_prepared(clk->hw); +out: + return !!ret; +} + bool __clk_is_enabled(struct clk *clk) { int ret; @@ -548,9 +663,9 @@ void __clk_unprepare(struct clk *clk) */ void clk_unprepare(struct clk *clk) { - mutex_lock(&prepare_lock); + clk_prepare_lock(); __clk_unprepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); } EXPORT_SYMBOL_GPL(clk_unprepare); @@ -596,9 +711,9 @@ int clk_prepare(struct clk *clk) { int ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_prepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -640,9 +755,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); __clk_disable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); @@ -693,9 +808,9 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); ret = __clk_enable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); return ret; } @@ -740,9 +855,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate) { unsigned long ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_round_rate(clk, rate); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -837,13 +952,13 @@ unsigned long clk_get_rate(struct clk *clk) { unsigned long rate; - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) __clk_recalc_rates(clk, 0); rate = __clk_get_rate(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return rate; } @@ -974,7 +1089,7 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even int ret = NOTIFY_DONE; if (clk->rate == clk->new_rate) - return 0; + return NULL; if (clk->notifier_count) { ret = __clk_notify(clk, event, clk->rate, clk->new_rate); @@ -1048,7 +1163,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) int ret = 0; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* bail early if nothing to do */ if (rate == clk->rate) @@ -1080,7 +1195,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk_change_rate(top); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1096,9 +1211,9 @@ struct clk *clk_get_parent(struct clk *clk) { struct clk *parent; - mutex_lock(&prepare_lock); + clk_prepare_lock(); parent = __clk_get_parent(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return parent; } @@ -1242,19 +1357,19 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) __clk_prepare(parent); /* FIXME replace with clk_is_enabled(clk) someday */ - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) __clk_enable(parent); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); /* change clock input source */ ret = clk->ops->set_parent(clk->hw, i); /* clean up old prepare and enable */ - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) __clk_disable(old_parent); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); if (clk->prepare_count) __clk_unprepare(old_parent); @@ -1286,7 +1401,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) return -ENOSYS; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk->parent == parent) goto out; @@ -1315,7 +1430,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) __clk_reparent(clk, parent); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1338,7 +1453,7 @@ int __clk_init(struct device *dev, struct clk *clk) if (!clk) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* check to see if a clock with this name is already registered */ if (__clk_lookup(clk->name)) { @@ -1462,7 +1577,7 @@ int __clk_init(struct device *dev, struct clk *clk) clk_debug_register(clk); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1696,7 +1811,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* search the list of notifiers for this clk */ list_for_each_entry(cn, &clk_notifier_list, node) @@ -1720,7 +1835,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) clk->notifier_count++; out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1745,7 +1860,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) @@ -1766,7 +1881,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) ret = -ENOENT; } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 9dd2551a0a41..b0fbc0715491 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -16,7 +16,6 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/delay.h> -#include "clk-cpu.h" #define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 #define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC @@ -173,17 +172,5 @@ clks_out: kfree(cpuclk); } -static const __initconst struct of_device_id clk_cpu_match[] = { - { - .compatible = "marvell,armada-xp-cpu-clock", - .data = of_cpu_clk_setup, - }, - { - /* sentinel */ - }, -}; - -void __init mvebu_cpu_clk_init(void) -{ - of_clk_init(clk_cpu_match); -} +CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock", + of_cpu_clk_setup); diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h deleted file mode 100644 index 08e2affba4e6..000000000000 --- a/drivers/clk/mvebu/clk-cpu.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Marvell MVEBU CPU clock handling. - * - * Copyright (C) 2012 Marvell - * - * Gregory CLEMENT <gregory.clement@free-electrons.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __MVEBU_CLK_CPU_H -#define __MVEBU_CLK_CPU_H - -#ifdef CONFIG_MVEBU_CLK_CPU -void __init mvebu_cpu_clk_init(void); -#else -static inline void mvebu_cpu_clk_init(void) {} -#endif - -#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c index 855681b8a9dc..29f10fb3006c 100644 --- a/drivers/clk/mvebu/clk.c +++ b/drivers/clk/mvebu/clk.c @@ -10,18 +10,14 @@ * warranty of any kind, whether express or implied. */ #include <linux/kernel.h> -#include <linux/clk.h> #include <linux/clk-provider.h> -#include <linux/of_address.h> -#include <linux/clk/mvebu.h> #include <linux/of.h> #include "clk-core.h" -#include "clk-cpu.h" #include "clk-gating-ctrl.h" void __init mvebu_clocks_init(void) { mvebu_core_clk_init(); mvebu_gating_clk_init(); - mvebu_cpu_clk_init(); + of_clk_init(NULL); } diff --git a/drivers/clk/mxs/clk-imx23.c b/drivers/clk/mxs/clk-imx23.c index b5c06f9766f6..f6a74872f14e 100644 --- a/drivers/clk/mxs/clk-imx23.c +++ b/drivers/clk/mxs/clk-imx23.c @@ -15,12 +15,15 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/of.h> -#include <mach/common.h> -#include <mach/mx23.h> +#include <linux/of_address.h> #include "clk.h" -#define DIGCTRL MX23_IO_ADDRESS(MX23_DIGCTL_BASE_ADDR) -#define CLKCTRL MX23_IO_ADDRESS(MX23_CLKCTRL_BASE_ADDR) +static void __iomem *clkctrl; +static void __iomem *digctrl; + +#define CLKCTRL clkctrl +#define DIGCTRL digctrl + #define PLLCTRL0 (CLKCTRL + 0x0000) #define CPU (CLKCTRL + 0x0020) #define HBUS (CLKCTRL + 0x0030) @@ -48,10 +51,10 @@ static void __init clk_misc_init(void) u32 val; /* Gate off cpu clock in WFI for power saving */ - __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET); /* Clear BYPASS for SAIF */ - __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ); + writel_relaxed(1 << BP_CLKSEQ_BYPASS_SAIF, CLKSEQ + CLR); /* SAIF has to use frac div for functional operation */ val = readl_relaxed(SAIF); @@ -62,14 +65,14 @@ static void __init clk_misc_init(void) * Source ssp clock from ref_io than ref_xtal, * as ref_xtal only provides 24 MHz as maximum. */ - __mxs_clrl(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ); + writel_relaxed(1 << BP_CLKSEQ_BYPASS_SSP, CLKSEQ + CLR); /* * 480 MHz seems too high to be ssp clock source directly, * so set frac to get a 288 MHz ref_io. */ - __mxs_clrl(0x3f << BP_FRAC_IOFRAC, FRAC); - __mxs_setl(30 << BP_FRAC_IOFRAC, FRAC); + writel_relaxed(0x3f << BP_FRAC_IOFRAC, FRAC + CLR); + writel_relaxed(30 << BP_FRAC_IOFRAC, FRAC + SET); } static const char *sel_pll[] __initconst = { "pll", "ref_xtal", }; @@ -101,6 +104,14 @@ int __init mx23_clocks_init(void) struct device_node *np; u32 i; + np = of_find_compatible_node(NULL, NULL, "fsl,imx23-digctl"); + digctrl = of_iomap(np, 0); + WARN_ON(!digctrl); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl"); + clkctrl = of_iomap(np, 0); + WARN_ON(!clkctrl); + clk_misc_init(); clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); @@ -153,19 +164,12 @@ int __init mx23_clocks_init(void) return PTR_ERR(clks[i]); } - np = of_find_compatible_node(NULL, NULL, "fsl,imx23-clkctrl"); - if (np) { - clk_data.clks = clks; - clk_data.clk_num = ARRAY_SIZE(clks); - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - } - - clk_register_clkdev(clks[clk32k], NULL, "timrot"); + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clks[clks_init_on[i]]); - mxs_timer_init(); - return 0; } diff --git a/drivers/clk/mxs/clk-imx28.c b/drivers/clk/mxs/clk-imx28.c index 76ce6c6d1113..d0e5eed146de 100644 --- a/drivers/clk/mxs/clk-imx28.c +++ b/drivers/clk/mxs/clk-imx28.c @@ -15,11 +15,12 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/of.h> -#include <mach/common.h> -#include <mach/mx28.h> +#include <linux/of_address.h> #include "clk.h" -#define CLKCTRL MX28_IO_ADDRESS(MX28_CLKCTRL_BASE_ADDR) +static void __iomem *clkctrl; +#define CLKCTRL clkctrl + #define PLL0CTRL0 (CLKCTRL + 0x0000) #define PLL1CTRL0 (CLKCTRL + 0x0020) #define PLL2CTRL0 (CLKCTRL + 0x0040) @@ -53,7 +54,8 @@ #define BP_FRAC0_IO1FRAC 16 #define BP_FRAC0_IO0FRAC 24 -#define DIGCTRL MX28_IO_ADDRESS(MX28_DIGCTL_BASE_ADDR) +static void __iomem *digctrl; +#define DIGCTRL digctrl #define BP_SAIF_CLKMUX 10 /* @@ -72,8 +74,8 @@ int mxs_saif_clkmux_select(unsigned int clkmux) if (clkmux > 0x3) return -EINVAL; - __mxs_clrl(0x3 << BP_SAIF_CLKMUX, DIGCTRL); - __mxs_setl(clkmux << BP_SAIF_CLKMUX, DIGCTRL); + writel_relaxed(0x3 << BP_SAIF_CLKMUX, DIGCTRL + CLR); + writel_relaxed(clkmux << BP_SAIF_CLKMUX, DIGCTRL + SET); return 0; } @@ -83,13 +85,13 @@ static void __init clk_misc_init(void) u32 val; /* Gate off cpu clock in WFI for power saving */ - __mxs_setl(1 << BP_CPU_INTERRUPT_WAIT, CPU); + writel_relaxed(1 << BP_CPU_INTERRUPT_WAIT, CPU + SET); /* 0 is a bad default value for a divider */ - __mxs_setl(1 << BP_ENET_DIV_TIME, ENET); + writel_relaxed(1 << BP_ENET_DIV_TIME, ENET + SET); /* Clear BYPASS for SAIF */ - __mxs_clrl(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ); + writel_relaxed(0x3 << BP_CLKSEQ_BYPASS_SAIF0, CLKSEQ + CLR); /* SAIF has to use frac div for functional operation */ val = readl_relaxed(SAIF0); @@ -109,7 +111,7 @@ static void __init clk_misc_init(void) * Source ssp clock from ref_io than ref_xtal, * as ref_xtal only provides 24 MHz as maximum. */ - __mxs_clrl(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ); + writel_relaxed(0xf << BP_CLKSEQ_BYPASS_SSP0, CLKSEQ + CLR); /* * 480 MHz seems too high to be ssp clock source directly, @@ -156,6 +158,14 @@ int __init mx28_clocks_init(void) struct device_node *np; u32 i; + np = of_find_compatible_node(NULL, NULL, "fsl,imx28-digctl"); + digctrl = of_iomap(np, 0); + WARN_ON(!digctrl); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl"); + clkctrl = of_iomap(np, 0); + WARN_ON(!clkctrl); + clk_misc_init(); clks[ref_xtal] = mxs_clk_fixed("ref_xtal", 24000000); @@ -231,20 +241,14 @@ int __init mx28_clocks_init(void) return PTR_ERR(clks[i]); } - np = of_find_compatible_node(NULL, NULL, "fsl,imx28-clkctrl"); - if (np) { - clk_data.clks = clks; - clk_data.clk_num = ARRAY_SIZE(clks); - of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - } + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); - clk_register_clkdev(clks[xbus], NULL, "timrot"); clk_register_clkdev(clks[enet_out], NULL, "enet_out"); for (i = 0; i < ARRAY_SIZE(clks_init_on); i++) clk_prepare_enable(clks[clks_init_on[i]]); - mxs_timer_init(); - return 0; } diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c index b24d56067c80..5301bce8957b 100644 --- a/drivers/clk/mxs/clk.c +++ b/drivers/clk/mxs/clk.c @@ -13,6 +13,7 @@ #include <linux/io.h> #include <linux/jiffies.h> #include <linux/spinlock.h> +#include "clk.h" DEFINE_SPINLOCK(mxs_lock); diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 82abea366b78..35e7e2698e10 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -960,47 +960,47 @@ void __init spear1340_clk_init(void) SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0100000.spdif-in"); - clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0, + clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "acp_clk"); - clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0, + clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "e2800000.gpio"); - clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0, + clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_dec"); - clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0, + clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_enc"); - clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0, + clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "spear_vip"); - clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0, + clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0200000.cam0"); - clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0, + clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0300000.cam1"); - clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0, + clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0400000.cam2"); - clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0, + clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0500000.cam3"); diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile new file mode 100644 index 000000000000..b5bac917612c --- /dev/null +++ b/drivers/clk/sunxi/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c new file mode 100644 index 000000000000..88523f91d9b7 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 Emilio López <emilio@elopez.com.ar> + * + * 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. + * + * Adjustable factor-based clock implementation + */ + +#include <linux/clk-provider.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/err.h> +#include <linux/string.h> + +#include <linux/delay.h> + +#include "clk-factors.h" + +/* + * DOC: basic adjustable factor-based clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. + * clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1) + * parent - fixed parent. No clk_set_parent support + */ + +struct clk_factors { + struct clk_hw hw; + void __iomem *reg; + struct clk_factors_config *config; + void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); + spinlock_t *lock; +}; + +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + +#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos)) +#define CLRMASK(len, pos) (~(SETMASK(len, pos))) +#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) + +#define FACTOR_SET(bit, len, reg, val) \ + (((reg) & CLRMASK(len, bit)) | (val << (bit))) + +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u8 n = 1, k = 0, p = 0, m = 0; + u32 reg; + unsigned long rate; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Get each individual factor if applicable */ + if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE) + n = FACTOR_GET(config->nshift, config->nwidth, reg); + if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE) + k = FACTOR_GET(config->kshift, config->kwidth, reg); + if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE) + m = FACTOR_GET(config->mshift, config->mwidth, reg); + if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) + p = FACTOR_GET(config->pshift, config->pwidth, reg); + + /* Calculate the rate */ + rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + + return rate; +} + +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_factors *factors = to_clk_factors(hw); + factors->get_factors((u32 *)&rate, (u32)*parent_rate, + NULL, NULL, NULL, NULL); + + return rate; +} + +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u8 n, k, m, p; + u32 reg; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + unsigned long flags = 0; + + factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Set up the new factors - macros do not do anything if width is 0 */ + reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); + reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); + reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); + reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); + + /* Apply them now */ + writel(reg, factors->reg); + + /* delay 500us so pll stabilizes */ + __delay((rate >> 20) * 500 / 2); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); + + return 0; +} + +static const struct clk_ops clk_factors_ops = { + .recalc_rate = clk_factors_recalc_rate, + .round_rate = clk_factors_round_rate, + .set_rate = clk_factors_set_rate, +}; + +/** + * clk_register_factors - register a factors clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust factors + * @config: shift and width of factors n, k, m and p + * @get_factors: function to calculate the factors for a given frequency + * @lock: shared register lock for this clock + */ +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors)(u32 *rate, u32 parent, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock) +{ + struct clk_factors *factors; + struct clk *clk; + struct clk_init_data init; + + /* allocate the factors */ + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) { + pr_err("%s: could not allocate factors clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &clk_factors_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_factors assignments */ + factors->reg = reg; + factors->config = config; + factors->lock = lock; + factors->hw.init = &init; + factors->get_factors = get_factors; + + /* register the clock */ + clk = clk_register(dev, &factors->hw); + + if (IS_ERR(clk)) + kfree(factors); + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h new file mode 100644 index 000000000000..f49851cc4380 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.h @@ -0,0 +1,27 @@ +#ifndef __MACH_SUNXI_CLK_FACTORS_H +#define __MACH_SUNXI_CLK_FACTORS_H + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> + +#define SUNXI_FACTORS_NOT_APPLICABLE (0) + +struct clk_factors_config { + u8 nshift; + u8 nwidth; + u8 kshift; + u8 kwidth; + u8 mshift; + u8 mwidth; + u8 pshift; + u8 pwidth; +}; + +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors) (u32 *rate, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock); +#endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c new file mode 100644 index 000000000000..0bb0eb4ed217 --- /dev/null +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -0,0 +1,450 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López <emilio@elopez.com.ar> + * + * 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. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/sunxi.h> +#include <linux/of.h> +#include <linux/of_address.h> + +#include "clk-factors.h" + +static DEFINE_SPINLOCK(clk_lock); + +/** + * sunxi_osc_clk_setup() - Setup function for gatable oscillator + */ + +#define SUNXI_OSC24M_GATE 0 + +static void __init sunxi_osc_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_gate(NULL, clk_name, parent, 0, reg, + SUNXI_OSC24M_GATE, 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * PLL1 rate is calculated as follows + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + * parent_rate is always 24Mhz + */ + +static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* Normalize value to a 6M multiple */ + div = *freq / 6000000; + *freq = 6000000 * div; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + /* m is always zero for pll1 */ + *m = 0; + + /* k is 1 only on these cases */ + if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) + *k = 1; + else + *k = 0; + + /* p will be 3 for divs under 10 */ + if (div < 10) + *p = 3; + + /* p will be 2 for divs between 10 - 20 and odd divs under 32 */ + else if (div < 20 || (div < 32 && (div & 1))) + *p = 2; + + /* p will be 1 for even divs under 32, divs under 40 and odd pairs + * of divs between 40-62 */ + else if (div < 40 || (div < 64 && (div & 2))) + *p = 1; + + /* any other entries have p = 0 */ + else + *p = 0; + + /* calculate a suitable n based on k and p */ + div <<= *p; + div /= (*k + 1); + *n = div / 4; +} + + + +/** + * sunxi_get_apb1_factors() - calculates m, p factors for APB1 + * APB1 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 calcm, calcp; + + if (parent_rate < *freq) + *freq = parent_rate; + + parent_rate = (parent_rate + (*freq - 1)) / *freq; + + /* Invalid rate! */ + if (parent_rate > 32) + return; + + if (parent_rate <= 4) + calcp = 0; + else if (parent_rate <= 8) + calcp = 1; + else if (parent_rate <= 16) + calcp = 2; + else + calcp = 3; + + calcm = (parent_rate >> calcp) - 1; + + *freq = (parent_rate >> calcp) / (calcm + 1); + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm; + *p = calcp; +} + + + +/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +struct factors_data { + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); +}; + +static struct clk_factors_config pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, + .pshift = 16, + .pwidth = 2, +}; + +static struct clk_factors_config apb1_config = { + .mshift = 0, + .mwidth = 5, + .pshift = 16, + .pwidth = 2, +}; + +static const __initconst struct factors_data pll1_data = { + .table = &pll1_config, + .getter = sunxi_get_pll1_factors, +}; + +static const __initconst struct factors_data apb1_data = { + .table = &apb1_config, + .getter = sunxi_get_apb1_factors, +}; + +static void __init sunxi_factors_clk_setup(struct device_node *node, + struct factors_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_factors(NULL, clk_name, parent, 0, reg, + data->table, data->getter, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_mux_clk_setup() - Setup function for muxes + */ + +#define SUNXI_MUX_GATE_WIDTH 2 + +struct mux_data { + u8 shift; +}; + +static const __initconst struct mux_data cpu_data = { + .shift = 16, +}; + +static const __initconst struct mux_data apb1_mux_data = { + .shift = 24, +}; + +static void __init sunxi_mux_clk_setup(struct device_node *node, + struct mux_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parents[5]; + void *reg; + int i = 0; + + reg = of_iomap(node, 0); + + while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, + data->shift, SUNXI_MUX_GATE_WIDTH, + 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_divider_clk_setup() - Setup function for simple divider clocks + */ + +#define SUNXI_DIVISOR_WIDTH 2 + +struct div_data { + u8 shift; + u8 pow; +}; + +static const __initconst struct div_data axi_data = { + .shift = 0, + .pow = 0, +}; + +static const __initconst struct div_data ahb_data = { + .shift = 4, + .pow = 1, +}; + +static const __initconst struct div_data apb0_data = { + .shift = 8, + .pow = 1, +}; + +static void __init sunxi_divider_clk_setup(struct device_node *node, + struct div_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parent; + void *reg; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, + reg, data->shift, SUNXI_DIVISOR_WIDTH, + data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, + &clk_lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks + */ + +#define SUNXI_GATES_MAX_SIZE 64 + +struct gates_data { + DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); +}; + +static const __initconst struct gates_data axi_gates_data = { + .mask = {1}, +}; + +static const __initconst struct gates_data ahb_gates_data = { + .mask = {0x7F77FFF, 0x14FB3F}, +}; + +static const __initconst struct gates_data apb0_gates_data = { + .mask = {0x4EF}, +}; + +static const __initconst struct gates_data apb1_gates_data = { + .mask = {0xFF00F7}, +}; + +static void __init sunxi_gates_clk_setup(struct device_node *node, + struct gates_data *data) +{ + struct clk_onecell_data *clk_data; + const char *clk_parent; + const char *clk_name; + void *reg; + int qty; + int i = 0; + int j = 0; + int ignore; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + /* Worst-case size approximation and memory allocation */ + qty = find_last_bit(data->mask, SUNXI_GATES_MAX_SIZE); + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!clk_data) + return; + clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return; + } + + for_each_set_bit(i, data->mask, SUNXI_GATES_MAX_SIZE) { + of_property_read_string_index(node, "clock-output-names", + j, &clk_name); + + /* No driver claims this clock, but it should remain gated */ + ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0; + + clk_data->clks[i] = clk_register_gate(NULL, clk_name, + clk_parent, ignore, + reg + 4 * (i/32), i % 32, + 0, &clk_lock); + WARN_ON(IS_ERR(clk_data->clks[i])); + + j++; + } + + /* Adjust to the real max */ + clk_data->clk_num = i; + + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + +/* Matches for of_clk_init */ +static const __initconst struct of_device_id clk_match[] = { + {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, + {.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,}, + {} +}; + +/* Matches for factors clocks */ +static const __initconst struct of_device_id clk_factors_match[] = { + {.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,}, + {} +}; + +/* Matches for divider clocks */ +static const __initconst struct of_device_id clk_div_match[] = { + {.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,}, + {} +}; + +/* Matches for mux clocks */ +static const __initconst struct of_device_id clk_mux_match[] = { + {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, + {} +}; + +/* Matches for gate clocks */ +static const __initconst struct of_device_id clk_gates_match[] = { + {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,}, + {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,}, + {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,}, + {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,}, + {} +}; + +static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, + void *function) +{ + struct device_node *np; + const struct div_data *data; + const struct of_device_id *match; + void (*setup_function)(struct device_node *, const void *) = function; + + for_each_matching_node(np, clk_match) { + match = of_match_node(clk_match, np); + data = match->data; + setup_function(np, data); + } +} + +void __init sunxi_init_clocks(void) +{ + /* Register all the simple sunxi clocks on DT */ + of_clk_init(clk_match); + + /* Register factor clocks */ + of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); + + /* Register divider clocks */ + of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); + + /* Register mux clocks */ + of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); + + /* Register gate clocks */ + of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup); +} diff --git a/drivers/clk/tegra/clk-tegra20.c b/drivers/clk/tegra/clk-tegra20.c index f873dcefe0de..bf194009e20f 100644 --- a/drivers/clk/tegra/clk-tegra20.c +++ b/drivers/clk/tegra/clk-tegra20.c @@ -711,8 +711,8 @@ static void tegra20_pll_init(void) } static const char *cclk_parents[] = { "clk_m", "pll_c", "clk_32k", "pll_m", - "pll_p_cclk", "pll_p_out4_cclk", - "pll_p_out3_cclk", "clk_d", "pll_x" }; + "pll_p", "pll_p_out4", + "pll_p_out3", "clk_d", "pll_x" }; static const char *sclk_parents[] = { "clk_m", "pll_c_out1", "pll_p_out4", "pll_p_out3", "pll_p_out2", "clk_d", "clk_32k", "pll_m_out1" }; @@ -721,38 +721,6 @@ static void tegra20_super_clk_init(void) { struct clk *clk; - /* - * DIV_U71 dividers for CCLK, these dividers are used only - * if parent clock is fixed rate. - */ - - /* - * Clock input to cclk divided from pll_p using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_cclk", "pll_p", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_cclk", NULL); - - /* - * Clock input to cclk divided from pll_p_out3 using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_out3_cclk", "pll_p_out3", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_out3_cclk", NULL); - - /* - * Clock input to cclk divided from pll_p_out4 using - * U71 divider of cclk. - */ - clk = tegra_clk_register_divider("pll_p_out4_cclk", "pll_p_out4", - clk_base + SUPER_CCLK_DIVIDER, 0, - TEGRA_DIVIDER_INT, 16, 8, 1, NULL); - clk_register_clkdev(clk, "pll_p_out4_cclk", NULL); - /* CCLK */ clk = tegra_clk_register_super_mux("cclk", cclk_parents, ARRAY_SIZE(cclk_parents), CLK_SET_RATE_PARENT, diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 0744731c6229..a09d7dcaf183 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -355,15 +355,16 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name, struct tegra_clk_periph *periph, void __iomem *clk_base, u32 offset); -#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \ +#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \ _div_shift, _div_width, _div_frac_width, \ _div_flags, _clk_num, _enb_refcnt, _regs, \ - _gate_flags) \ + _gate_flags, _table) \ { \ .mux = { \ .flags = _mux_flags, \ .shift = _mux_shift, \ - .width = _mux_width, \ + .mask = _mux_mask, \ + .table = _table, \ }, \ .divider = { \ .flags = _div_flags, \ @@ -393,26 +394,36 @@ struct tegra_periph_init_data { const char *dev_id; }; -#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \ - _mux_shift, _mux_width, _mux_flags, _div_shift, \ +#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_mask, _mux_flags, _div_shift, \ _div_width, _div_frac_width, _div_flags, _regs, \ - _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table) \ { \ .name = _name, \ .clk_id = _clk_id, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ - .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \ + .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \ _mux_flags, _div_shift, \ _div_width, _div_frac_width, \ _div_flags, _clk_num, \ _enb_refcnt, _regs, \ - _gate_flags), \ + _gate_flags, _table), \ .offset = _offset, \ .con_id = _con_id, \ .dev_id = _dev_id, \ } +#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_width, _mux_flags, _div_shift, \ + _div_width, _div_frac_width, _div_flags, _regs, \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, BIT(_mux_width) - 1, _mux_flags, \ + _div_shift, _div_width, _div_frac_width, _div_flags, \ + _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\ + NULL) + /** * struct clk_super_mux - super clock * diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 74faa7e3cf59..293a28854417 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -20,15 +20,23 @@ struct clk_prcmu { struct clk_hw hw; u8 cg_sel; + int is_prepared; int is_enabled; + int opp_requested; }; /* PRCMU clock operations. */ static int clk_prcmu_prepare(struct clk_hw *hw) { + int ret; struct clk_prcmu *clk = to_clk_prcmu(hw); - return prcmu_request_clock(clk->cg_sel, true); + + ret = prcmu_request_clock(clk->cg_sel, true); + if (!ret) + clk->is_prepared = 1; + + return ret;; } static void clk_prcmu_unprepare(struct clk_hw *hw) @@ -36,7 +44,15 @@ static void clk_prcmu_unprepare(struct clk_hw *hw) struct clk_prcmu *clk = to_clk_prcmu(hw); if (prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); + else + clk->is_prepared = 0; +} + +static int clk_prcmu_is_prepared(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return clk->is_prepared; } static int clk_prcmu_enable(struct clk_hw *hw) @@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, return prcmu_set_clock_rate(clk->cg_sel, rate); } -static int request_ape_opp100(bool enable) -{ - static int reqs; - int err = 0; - - if (enable) { - if (!reqs) - err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, - "clock", 100); - if (!err) - reqs++; - } else { - reqs--; - if (!reqs) - prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, - "clock"); - } - return err; -} - static int clk_prcmu_opp_prepare(struct clk_hw *hw) { int err; struct clk_prcmu *clk = to_clk_prcmu(hw); - err = request_ape_opp100(true); - if (err) { - pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", - __func__, hw->init->name); - return err; + if (!clk->opp_requested) { + err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk), + 100); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", + __func__, __clk_get_name(hw->clk)); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) - request_ape_opp100(false); + if (err) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } static void clk_prcmu_opp_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 (request_ape_opp100(false)) - goto out_error; - return; - -out_error: - pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + __clk_get_name(hw->clk)); + return; + } + + if (clk->opp_requested) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) @@ -138,38 +148,49 @@ 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; + if (!clk->opp_requested) { + err = prcmu_request_ape_opp_100_voltage(true); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", + __func__, __clk_get_name(hw->clk)); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) + if (err) { prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } 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); + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + __clk_get_name(hw->clk)); + return; + } + + if (clk->opp_requested) { + prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static struct clk_ops clk_prcmu_scalable_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = { static struct clk_ops clk_prcmu_gate_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = { static struct clk_ops clk_prcmu_opp_gate_ops = { .prepare = clk_prcmu_opp_prepare, .unprepare = clk_prcmu_opp_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = { static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { .prepare = clk_prcmu_opp_volt_prepare, .unprepare = clk_prcmu_opp_volt_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name, } clk->cg_sel = cg_sel; + clk->is_prepared = 1; clk->is_enabled = 1; + clk->opp_requested = 0; /* "rate" can be used for changing the initial frequency */ if (rate) prcmu_set_clock_rate(cg_sel, rate); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index e507ab7df60b..9002185a0a1a 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -25,7 +25,7 @@ config DW_APB_TIMER_OF config ARMADA_370_XP_TIMER bool -config SUNXI_TIMER +config SUN4I_TIMER bool config VT8500_TIMER diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 4d8283aec5b5..98f220a7a92c 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -16,7 +16,8 @@ obj-$(CONFIG_CLKSRC_NOMADIK_MTU) += nomadik-mtu.o obj-$(CONFIG_CLKSRC_DBX500_PRCMU) += clksrc-dbx500-prcmu.o obj-$(CONFIG_ARMADA_370_XP_TIMER) += time-armada-370-xp.o obj-$(CONFIG_ARCH_BCM2835) += bcm2835_timer.o -obj-$(CONFIG_SUNXI_TIMER) += sunxi_timer.o +obj-$(CONFIG_ARCH_MXS) += mxs_timer.o +obj-$(CONFIG_SUN4I_TIMER) += sun4i_timer.o obj-$(CONFIG_ARCH_TEGRA) += tegra20_timer.o obj-$(CONFIG_VT8500_TIMER) += vt8500_timer.o diff --git a/drivers/clocksource/bcm2835_timer.c b/drivers/clocksource/bcm2835_timer.c index 50c68fef944b..766611d29945 100644 --- a/drivers/clocksource/bcm2835_timer.c +++ b/drivers/clocksource/bcm2835_timer.c @@ -95,23 +95,13 @@ static irqreturn_t bcm2835_time_interrupt(int irq, void *dev_id) } } -static struct of_device_id bcm2835_time_match[] __initconst = { - { .compatible = "brcm,bcm2835-system-timer" }, - {} -}; - -static void __init bcm2835_timer_init(void) +static void __init bcm2835_timer_init(struct device_node *node) { - struct device_node *node; void __iomem *base; u32 freq; int irq; struct bcm2835_timer *timer; - node = of_find_matching_node(NULL, bcm2835_time_match); - if (!node) - panic("No bcm2835 timer node"); - base = of_iomap(node, 0); if (!base) panic("Can't remap registers"); diff --git a/drivers/clocksource/clksrc-of.c b/drivers/clocksource/clksrc-of.c index bdabdaa8d00f..37f5325bec95 100644 --- a/drivers/clocksource/clksrc-of.c +++ b/drivers/clocksource/clksrc-of.c @@ -16,6 +16,7 @@ #include <linux/init.h> #include <linux/of.h> +#include <linux/clocksource.h> extern struct of_device_id __clksrc_of_table[]; @@ -26,10 +27,10 @@ void __init clocksource_of_init(void) { struct device_node *np; const struct of_device_id *match; - void (*init_func)(void); + clocksource_of_init_fn init_func; for_each_matching_node_and_match(np, __clksrc_of_table, &match) { init_func = match->data; - init_func(); + init_func(np); } } diff --git a/drivers/clocksource/mxs_timer.c b/drivers/clocksource/mxs_timer.c new file mode 100644 index 000000000000..02af4204af86 --- /dev/null +++ b/drivers/clocksource/mxs_timer.c @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2000-2001 Deep Blue Solutions + * Copyright (C) 2002 Shane Nay (shane@minirl.com) + * Copyright (C) 2006-2007 Pavel Pisa (ppisa@pikron.com) + * Copyright (C) 2008 Juergen Beisert (kernel@pengutronix.de) + * Copyright (C) 2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/clockchips.h> +#include <linux/clk.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/stmp_device.h> + +#include <asm/mach/time.h> +#include <asm/sched_clock.h> + +/* + * There are 2 versions of the timrot on Freescale MXS-based SoCs. + * The v1 on MX23 only gets 16 bits counter, while v2 on MX28 + * extends the counter to 32 bits. + * + * The implementation uses two timers, one for clock_event and + * another for clocksource. MX28 uses timrot 0 and 1, while MX23 + * uses 0 and 2. + */ + +#define MX23_TIMROT_VERSION_OFFSET 0x0a0 +#define MX28_TIMROT_VERSION_OFFSET 0x120 +#define BP_TIMROT_MAJOR_VERSION 24 +#define BV_TIMROT_VERSION_1 0x01 +#define BV_TIMROT_VERSION_2 0x02 +#define timrot_is_v1() (timrot_major_version == BV_TIMROT_VERSION_1) + +/* + * There are 4 registers for each timrotv2 instance, and 2 registers + * for each timrotv1. So address step 0x40 in macros below strides + * one instance of timrotv2 while two instances of timrotv1. + * + * As the result, HW_TIMROT_XXXn(1) defines the address of timrot1 + * on MX28 while timrot2 on MX23. + */ +/* common between v1 and v2 */ +#define HW_TIMROT_ROTCTRL 0x00 +#define HW_TIMROT_TIMCTRLn(n) (0x20 + (n) * 0x40) +/* v1 only */ +#define HW_TIMROT_TIMCOUNTn(n) (0x30 + (n) * 0x40) +/* v2 only */ +#define HW_TIMROT_RUNNING_COUNTn(n) (0x30 + (n) * 0x40) +#define HW_TIMROT_FIXED_COUNTn(n) (0x40 + (n) * 0x40) + +#define BM_TIMROT_TIMCTRLn_RELOAD (1 << 6) +#define BM_TIMROT_TIMCTRLn_UPDATE (1 << 7) +#define BM_TIMROT_TIMCTRLn_IRQ_EN (1 << 14) +#define BM_TIMROT_TIMCTRLn_IRQ (1 << 15) +#define BP_TIMROT_TIMCTRLn_SELECT 0 +#define BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL 0x8 +#define BV_TIMROTv2_TIMCTRLn_SELECT__32KHZ_XTAL 0xb +#define BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS 0xf + +static struct clock_event_device mxs_clockevent_device; +static enum clock_event_mode mxs_clockevent_mode = CLOCK_EVT_MODE_UNUSED; + +static void __iomem *mxs_timrot_base; +static u32 timrot_major_version; + +static inline void timrot_irq_disable(void) +{ + __raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base + + HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR); +} + +static inline void timrot_irq_enable(void) +{ + __raw_writel(BM_TIMROT_TIMCTRLn_IRQ_EN, mxs_timrot_base + + HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_SET); +} + +static void timrot_irq_acknowledge(void) +{ + __raw_writel(BM_TIMROT_TIMCTRLn_IRQ, mxs_timrot_base + + HW_TIMROT_TIMCTRLn(0) + STMP_OFFSET_REG_CLR); +} + +static cycle_t timrotv1_get_cycles(struct clocksource *cs) +{ + return ~((__raw_readl(mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)) + & 0xffff0000) >> 16); +} + +static int timrotv1_set_next_event(unsigned long evt, + struct clock_event_device *dev) +{ + /* timrot decrements the count */ + __raw_writel(evt, mxs_timrot_base + HW_TIMROT_TIMCOUNTn(0)); + + return 0; +} + +static int timrotv2_set_next_event(unsigned long evt, + struct clock_event_device *dev) +{ + /* timrot decrements the count */ + __raw_writel(evt, mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(0)); + + return 0; +} + +static irqreturn_t mxs_timer_interrupt(int irq, void *dev_id) +{ + struct clock_event_device *evt = dev_id; + + timrot_irq_acknowledge(); + evt->event_handler(evt); + + return IRQ_HANDLED; +} + +static struct irqaction mxs_timer_irq = { + .name = "MXS Timer Tick", + .dev_id = &mxs_clockevent_device, + .flags = IRQF_TIMER | IRQF_IRQPOLL, + .handler = mxs_timer_interrupt, +}; + +#ifdef DEBUG +static const char *clock_event_mode_label[] const = { + [CLOCK_EVT_MODE_PERIODIC] = "CLOCK_EVT_MODE_PERIODIC", + [CLOCK_EVT_MODE_ONESHOT] = "CLOCK_EVT_MODE_ONESHOT", + [CLOCK_EVT_MODE_SHUTDOWN] = "CLOCK_EVT_MODE_SHUTDOWN", + [CLOCK_EVT_MODE_UNUSED] = "CLOCK_EVT_MODE_UNUSED" +}; +#endif /* DEBUG */ + +static void mxs_set_mode(enum clock_event_mode mode, + struct clock_event_device *evt) +{ + /* Disable interrupt in timer module */ + timrot_irq_disable(); + + if (mode != mxs_clockevent_mode) { + /* Set event time into the furthest future */ + if (timrot_is_v1()) + __raw_writel(0xffff, + mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)); + else + __raw_writel(0xffffffff, + mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1)); + + /* Clear pending interrupt */ + timrot_irq_acknowledge(); + } + +#ifdef DEBUG + pr_info("%s: changing mode from %s to %s\n", __func__, + clock_event_mode_label[mxs_clockevent_mode], + clock_event_mode_label[mode]); +#endif /* DEBUG */ + + /* Remember timer mode */ + mxs_clockevent_mode = mode; + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + pr_err("%s: Periodic mode is not implemented\n", __func__); + break; + case CLOCK_EVT_MODE_ONESHOT: + timrot_irq_enable(); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_RESUME: + /* Left event sources disabled, no more interrupts appear */ + break; + } +} + +static struct clock_event_device mxs_clockevent_device = { + .name = "mxs_timrot", + .features = CLOCK_EVT_FEAT_ONESHOT, + .set_mode = mxs_set_mode, + .set_next_event = timrotv2_set_next_event, + .rating = 200, +}; + +static int __init mxs_clockevent_init(struct clk *timer_clk) +{ + if (timrot_is_v1()) + mxs_clockevent_device.set_next_event = timrotv1_set_next_event; + mxs_clockevent_device.cpumask = cpumask_of(0); + clockevents_config_and_register(&mxs_clockevent_device, + clk_get_rate(timer_clk), + timrot_is_v1() ? 0xf : 0x2, + timrot_is_v1() ? 0xfffe : 0xfffffffe); + + return 0; +} + +static struct clocksource clocksource_mxs = { + .name = "mxs_timer", + .rating = 200, + .read = timrotv1_get_cycles, + .mask = CLOCKSOURCE_MASK(16), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static u32 notrace mxs_read_sched_clock_v2(void) +{ + return ~readl_relaxed(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1)); +} + +static int __init mxs_clocksource_init(struct clk *timer_clk) +{ + unsigned int c = clk_get_rate(timer_clk); + + if (timrot_is_v1()) + clocksource_register_hz(&clocksource_mxs, c); + else { + clocksource_mmio_init(mxs_timrot_base + HW_TIMROT_RUNNING_COUNTn(1), + "mxs_timer", c, 200, 32, clocksource_mmio_readl_down); + setup_sched_clock(mxs_read_sched_clock_v2, 32, c); + } + + return 0; +} + +static void __init mxs_timer_init(struct device_node *np) +{ + struct clk *timer_clk; + int irq; + + mxs_timrot_base = of_iomap(np, 0); + WARN_ON(!mxs_timrot_base); + + timer_clk = of_clk_get(np, 0); + if (IS_ERR(timer_clk)) { + pr_err("%s: failed to get clk\n", __func__); + return; + } + + clk_prepare_enable(timer_clk); + + /* + * Initialize timers to a known state + */ + stmp_reset_block(mxs_timrot_base + HW_TIMROT_ROTCTRL); + + /* get timrot version */ + timrot_major_version = __raw_readl(mxs_timrot_base + + (of_device_is_compatible(np, "fsl,imx23-timrot") ? + MX23_TIMROT_VERSION_OFFSET : + MX28_TIMROT_VERSION_OFFSET)); + timrot_major_version >>= BP_TIMROT_MAJOR_VERSION; + + /* one for clock_event */ + __raw_writel((timrot_is_v1() ? + BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL : + BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) | + BM_TIMROT_TIMCTRLn_UPDATE | + BM_TIMROT_TIMCTRLn_IRQ_EN, + mxs_timrot_base + HW_TIMROT_TIMCTRLn(0)); + + /* another for clocksource */ + __raw_writel((timrot_is_v1() ? + BV_TIMROTv1_TIMCTRLn_SELECT__32KHZ_XTAL : + BV_TIMROTv2_TIMCTRLn_SELECT__TICK_ALWAYS) | + BM_TIMROT_TIMCTRLn_RELOAD, + mxs_timrot_base + HW_TIMROT_TIMCTRLn(1)); + + /* set clocksource timer fixed count to the maximum */ + if (timrot_is_v1()) + __raw_writel(0xffff, + mxs_timrot_base + HW_TIMROT_TIMCOUNTn(1)); + else + __raw_writel(0xffffffff, + mxs_timrot_base + HW_TIMROT_FIXED_COUNTn(1)); + + /* init and register the timer to the framework */ + mxs_clocksource_init(timer_clk); + mxs_clockevent_init(timer_clk); + + /* Make irqs happen */ + irq = irq_of_parse_and_map(np, 0); + setup_irq(irq, &mxs_timer_irq); +} +CLOCKSOURCE_OF_DECLARE(mxs, "fsl,timrot", mxs_timer_init); diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sun4i_timer.c index 4086b9167159..d4674e78ef35 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sun4i_timer.c @@ -22,66 +22,64 @@ #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> -#include <linux/sunxi_timer.h> -#include <linux/clk-provider.h> -#define TIMER_CTL_REG 0x00 -#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_IRQ_EN_REG 0x00 +#define TIMER_IRQ_EN(val) (1 << val) #define TIMER_IRQ_ST_REG 0x04 -#define TIMER0_CTL_REG 0x10 -#define TIMER0_CTL_ENABLE (1 << 0) -#define TIMER0_CTL_AUTORELOAD (1 << 1) -#define TIMER0_CTL_ONESHOT (1 << 7) -#define TIMER0_INTVAL_REG 0x14 -#define TIMER0_CNTVAL_REG 0x18 +#define TIMER_CTL_REG(val) (0x10 * val + 0x10) +#define TIMER_CTL_ENABLE (1 << 0) +#define TIMER_CTL_AUTORELOAD (1 << 1) +#define TIMER_CTL_ONESHOT (1 << 7) +#define TIMER_INTVAL_REG(val) (0x10 * val + 0x14) +#define TIMER_CNTVAL_REG(val) (0x10 * val + 0x18) #define TIMER_SCAL 16 static void __iomem *timer_base; -static void sunxi_clkevt_mode(enum clock_event_mode mode, +static void sun4i_clkevt_mode(enum clock_event_mode mode, struct clock_event_device *clk) { - u32 u = readl(timer_base + TIMER0_CTL_REG); + u32 u = readl(timer_base + TIMER_CTL_REG(0)); switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - u &= ~(TIMER0_CTL_ONESHOT); - writel(u | TIMER0_CTL_ENABLE, timer_base + TIMER0_CTL_REG); + u &= ~(TIMER_CTL_ONESHOT); + writel(u | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG(0)); break; case CLOCK_EVT_MODE_ONESHOT: - writel(u | TIMER0_CTL_ONESHOT, timer_base + TIMER0_CTL_REG); + writel(u | TIMER_CTL_ONESHOT, timer_base + TIMER_CTL_REG(0)); break; case CLOCK_EVT_MODE_UNUSED: case CLOCK_EVT_MODE_SHUTDOWN: default: - writel(u & ~(TIMER0_CTL_ENABLE), timer_base + TIMER0_CTL_REG); + writel(u & ~(TIMER_CTL_ENABLE), timer_base + TIMER_CTL_REG(0)); break; } } -static int sunxi_clkevt_next_event(unsigned long evt, +static int sun4i_clkevt_next_event(unsigned long evt, struct clock_event_device *unused) { - u32 u = readl(timer_base + TIMER0_CTL_REG); - writel(evt, timer_base + TIMER0_CNTVAL_REG); - writel(u | TIMER0_CTL_ENABLE | TIMER0_CTL_AUTORELOAD, - timer_base + TIMER0_CTL_REG); + u32 u = readl(timer_base + TIMER_CTL_REG(0)); + writel(evt, timer_base + TIMER_CNTVAL_REG(0)); + writel(u | TIMER_CTL_ENABLE | TIMER_CTL_AUTORELOAD, + timer_base + TIMER_CTL_REG(0)); return 0; } -static struct clock_event_device sunxi_clockevent = { - .name = "sunxi_tick", +static struct clock_event_device sun4i_clockevent = { + .name = "sun4i_tick", .rating = 300, .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .set_mode = sunxi_clkevt_mode, - .set_next_event = sunxi_clkevt_next_event, + .set_mode = sun4i_clkevt_mode, + .set_next_event = sun4i_clkevt_next_event, }; -static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) +static irqreturn_t sun4i_timer_interrupt(int irq, void *dev_id) { struct clock_event_device *evt = (struct clock_event_device *)dev_id; @@ -91,30 +89,20 @@ static irqreturn_t sunxi_timer_interrupt(int irq, void *dev_id) return IRQ_HANDLED; } -static struct irqaction sunxi_timer_irq = { - .name = "sunxi_timer0", +static struct irqaction sun4i_timer_irq = { + .name = "sun4i_timer0", .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = sunxi_timer_interrupt, - .dev_id = &sunxi_clockevent, -}; - -static struct of_device_id sunxi_timer_dt_ids[] = { - { .compatible = "allwinner,sunxi-timer" }, - { } + .handler = sun4i_timer_interrupt, + .dev_id = &sun4i_clockevent, }; -void __init sunxi_timer_init(void) +static void __init sun4i_timer_init(struct device_node *node) { - struct device_node *node; unsigned long rate = 0; struct clk *clk; int ret, irq; u32 val; - node = of_find_matching_node(NULL, sunxi_timer_dt_ids); - if (!node) - panic("No sunxi timer node"); - timer_base = of_iomap(node, 0); if (!timer_base) panic("Can't map registers"); @@ -123,8 +111,6 @@ void __init sunxi_timer_init(void) if (irq <= 0) panic("Can't parse IRQ"); - of_clk_init(NULL); - clk = of_clk_get(node, 0); if (IS_ERR(clk)) panic("Can't get timer clock"); @@ -132,29 +118,31 @@ void __init sunxi_timer_init(void) rate = clk_get_rate(clk); writel(rate / (TIMER_SCAL * HZ), - timer_base + TIMER0_INTVAL_REG); + timer_base + TIMER_INTVAL_REG(0)); /* set clock source to HOSC, 16 pre-division */ - val = readl(timer_base + TIMER0_CTL_REG); + val = readl(timer_base + TIMER_CTL_REG(0)); val &= ~(0x07 << 4); val &= ~(0x03 << 2); val |= (4 << 4) | (1 << 2); - writel(val, timer_base + TIMER0_CTL_REG); + writel(val, timer_base + TIMER_CTL_REG(0)); /* set mode to auto reload */ - val = readl(timer_base + TIMER0_CTL_REG); - writel(val | TIMER0_CTL_AUTORELOAD, timer_base + TIMER0_CTL_REG); + val = readl(timer_base + TIMER_CTL_REG(0)); + writel(val | TIMER_CTL_AUTORELOAD, timer_base + TIMER_CTL_REG(0)); - ret = setup_irq(irq, &sunxi_timer_irq); + ret = setup_irq(irq, &sun4i_timer_irq); if (ret) pr_warn("failed to setup irq %d\n", irq); /* Enable timer0 interrupt */ - val = readl(timer_base + TIMER_CTL_REG); - writel(val | TIMER_CTL_ENABLE, timer_base + TIMER_CTL_REG); + val = readl(timer_base + TIMER_IRQ_EN_REG); + writel(val | TIMER_IRQ_EN(0), timer_base + TIMER_IRQ_EN_REG); - sunxi_clockevent.cpumask = cpumask_of(0); + sun4i_clockevent.cpumask = cpumask_of(0); - clockevents_config_and_register(&sunxi_clockevent, rate / TIMER_SCAL, + clockevents_config_and_register(&sun4i_clockevent, rate / TIMER_SCAL, 0x1, 0xff); } +CLOCKSOURCE_OF_DECLARE(sun4i, "allwinner,sun4i-timer", + sun4i_timer_init); diff --git a/drivers/clocksource/tegra20_timer.c b/drivers/clocksource/tegra20_timer.c index 0bde03feb095..ae877b021b54 100644 --- a/drivers/clocksource/tegra20_timer.c +++ b/drivers/clocksource/tegra20_timer.c @@ -154,29 +154,12 @@ static struct irqaction tegra_timer_irq = { .dev_id = &tegra_clockevent, }; -static const struct of_device_id timer_match[] __initconst = { - { .compatible = "nvidia,tegra20-timer" }, - {} -}; - -static const struct of_device_id rtc_match[] __initconst = { - { .compatible = "nvidia,tegra20-rtc" }, - {} -}; - -static void __init tegra20_init_timer(void) +static void __init tegra20_init_timer(struct device_node *np) { - struct device_node *np; struct clk *clk; unsigned long rate; int ret; - np = of_find_matching_node(NULL, timer_match); - if (!np) { - pr_err("Failed to find timer DT node\n"); - BUG(); - } - timer_reg_base = of_iomap(np, 0); if (!timer_reg_base) { pr_err("Can't map timer registers\n"); @@ -189,7 +172,7 @@ static void __init tegra20_init_timer(void) BUG(); } - clk = clk_get_sys("timer", NULL); + clk = of_clk_get(np, 0); if (IS_ERR(clk)) { pr_warn("Unable to get timer clock. Assuming 12Mhz input clock.\n"); rate = 12000000; @@ -200,30 +183,6 @@ static void __init tegra20_init_timer(void) of_node_put(np); - np = of_find_matching_node(NULL, rtc_match); - if (!np) { - pr_err("Failed to find RTC DT node\n"); - BUG(); - } - - rtc_base = of_iomap(np, 0); - if (!rtc_base) { - pr_err("Can't map RTC registers"); - BUG(); - } - - /* - * rtc registers are used by read_persistent_clock, keep the rtc clock - * enabled - */ - clk = clk_get_sys("rtc-tegra", NULL); - if (IS_ERR(clk)) - pr_warn("Unable to get rtc-tegra clock\n"); - else - clk_prepare_enable(clk); - - of_node_put(np); - switch (rate) { case 12000000: timer_writel(0x000b, TIMERUS_USEC_CFG); @@ -259,12 +218,34 @@ static void __init tegra20_init_timer(void) tegra_clockevent.irq = tegra_timer_irq.irq; clockevents_config_and_register(&tegra_clockevent, 1000000, 0x1, 0x1fffffff); -#ifdef CONFIG_HAVE_ARM_TWD - twd_local_timer_of_register(); -#endif +} +CLOCKSOURCE_OF_DECLARE(tegra20_timer, "nvidia,tegra20-timer", tegra20_init_timer); + +static void __init tegra20_init_rtc(struct device_node *np) +{ + struct clk *clk; + + rtc_base = of_iomap(np, 0); + if (!rtc_base) { + pr_err("Can't map RTC registers"); + BUG(); + } + + /* + * rtc registers are used by read_persistent_clock, keep the rtc clock + * enabled + */ + clk = of_clk_get(np, 0); + if (IS_ERR(clk)) + pr_warn("Unable to get rtc-tegra clock\n"); + else + clk_prepare_enable(clk); + + of_node_put(np); + register_persistent_clock(NULL, tegra_read_persistent_clock); } -CLOCKSOURCE_OF_DECLARE(tegra20, "nvidia,tegra20-timer", tegra20_init_timer); +CLOCKSOURCE_OF_DECLARE(tegra20_rtc, "nvidia,tegra20-rtc", tegra20_init_rtc); #ifdef CONFIG_PM static u32 usec_config; diff --git a/drivers/clocksource/vt8500_timer.c b/drivers/clocksource/vt8500_timer.c index 8efc86b5b5dd..64f553f04fa4 100644 --- a/drivers/clocksource/vt8500_timer.c +++ b/drivers/clocksource/vt8500_timer.c @@ -129,22 +129,10 @@ static struct irqaction irq = { .dev_id = &clockevent, }; -static struct of_device_id vt8500_timer_ids[] = { - { .compatible = "via,vt8500-timer" }, - { } -}; - -static void __init vt8500_timer_init(void) +static void __init vt8500_timer_init(struct device_node *np) { - struct device_node *np; int timer_irq; - np = of_find_matching_node(NULL, vt8500_timer_ids); - if (!np) { - pr_err("%s: Timer description missing from Device Tree\n", - __func__); - return; - } regbase = of_iomap(np, 0); if (!regbase) { pr_err("%s: Missing iobase description in Device Tree\n", @@ -177,4 +165,4 @@ static void __init vt8500_timer_init(void) 4, 0xf0000000); } -CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init) +CLOCKSOURCE_OF_DECLARE(vt8500, "via,vt8500-timer", vt8500_timer_init); diff --git a/drivers/gpio/gpio-msm-v1.c b/drivers/gpio/gpio-msm-v1.c index 52a4d4286eba..c798585a3fe5 100644 --- a/drivers/gpio/gpio-msm-v1.c +++ b/drivers/gpio/gpio-msm-v1.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2007 Google, Inc. - * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. + * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -19,9 +19,10 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/module.h> -#include <mach/cpu.h> +#include <linux/device.h> +#include <linux/platform_device.h> + #include <mach/msm_gpiomux.h> -#include <mach/msm_iomap.h> /* see 80-VA736-2 Rev C pp 695-751 ** @@ -34,10 +35,10 @@ ** macros. */ -#define MSM_GPIO1_REG(off) (MSM_GPIO1_BASE + (off)) -#define MSM_GPIO2_REG(off) (MSM_GPIO2_BASE + 0x400 + (off)) -#define MSM_GPIO1_SHADOW_REG(off) (MSM_GPIO1_BASE + 0x800 + (off)) -#define MSM_GPIO2_SHADOW_REG(off) (MSM_GPIO2_BASE + 0xC00 + (off)) +#define MSM_GPIO1_REG(off) (off) +#define MSM_GPIO2_REG(off) (off) +#define MSM_GPIO1_SHADOW_REG(off) (off) +#define MSM_GPIO2_SHADOW_REG(off) (off) /* * MSM7X00 registers @@ -276,16 +277,14 @@ #define MSM_GPIO_BANK(soc, bank, first, last) \ { \ - .regs = { \ - .out = soc##_GPIO_OUT_##bank, \ - .in = soc##_GPIO_IN_##bank, \ - .int_status = soc##_GPIO_INT_STATUS_##bank, \ - .int_clear = soc##_GPIO_INT_CLEAR_##bank, \ - .int_en = soc##_GPIO_INT_EN_##bank, \ - .int_edge = soc##_GPIO_INT_EDGE_##bank, \ - .int_pos = soc##_GPIO_INT_POS_##bank, \ - .oe = soc##_GPIO_OE_##bank, \ - }, \ + .regs[MSM_GPIO_OUT] = soc##_GPIO_OUT_##bank, \ + .regs[MSM_GPIO_IN] = soc##_GPIO_IN_##bank, \ + .regs[MSM_GPIO_INT_STATUS] = soc##_GPIO_INT_STATUS_##bank, \ + .regs[MSM_GPIO_INT_CLEAR] = soc##_GPIO_INT_CLEAR_##bank, \ + .regs[MSM_GPIO_INT_EN] = soc##_GPIO_INT_EN_##bank, \ + .regs[MSM_GPIO_INT_EDGE] = soc##_GPIO_INT_EDGE_##bank, \ + .regs[MSM_GPIO_INT_POS] = soc##_GPIO_INT_POS_##bank, \ + .regs[MSM_GPIO_OE] = soc##_GPIO_OE_##bank, \ .chip = { \ .base = (first), \ .ngpio = (last) - (first) + 1, \ @@ -301,39 +300,57 @@ #define MSM_GPIO_BROKEN_INT_CLEAR 1 -struct msm_gpio_regs { - void __iomem *out; - void __iomem *in; - void __iomem *int_status; - void __iomem *int_clear; - void __iomem *int_en; - void __iomem *int_edge; - void __iomem *int_pos; - void __iomem *oe; +enum msm_gpio_reg { + MSM_GPIO_IN, + MSM_GPIO_OUT, + MSM_GPIO_INT_STATUS, + MSM_GPIO_INT_CLEAR, + MSM_GPIO_INT_EN, + MSM_GPIO_INT_EDGE, + MSM_GPIO_INT_POS, + MSM_GPIO_OE, + MSM_GPIO_REG_NR }; struct msm_gpio_chip { spinlock_t lock; struct gpio_chip chip; - struct msm_gpio_regs regs; + unsigned long regs[MSM_GPIO_REG_NR]; #if MSM_GPIO_BROKEN_INT_CLEAR unsigned int_status_copy; #endif unsigned int both_edge_detect; unsigned int int_enable[2]; /* 0: awake, 1: sleep */ + void __iomem *base; +}; + +struct msm_gpio_initdata { + struct msm_gpio_chip *chips; + int count; }; +static void msm_gpio_writel(struct msm_gpio_chip *chip, u32 val, + enum msm_gpio_reg reg) +{ + writel(val, chip->base + chip->regs[reg]); +} + +static u32 msm_gpio_readl(struct msm_gpio_chip *chip, enum msm_gpio_reg reg) +{ + return readl(chip->base + chip->regs[reg]); +} + static int msm_gpio_write(struct msm_gpio_chip *msm_chip, unsigned offset, unsigned on) { unsigned mask = BIT(offset); unsigned val; - val = readl(msm_chip->regs.out); + val = msm_gpio_readl(msm_chip, MSM_GPIO_OUT); if (on) - writel(val | mask, msm_chip->regs.out); + msm_gpio_writel(msm_chip, val | mask, MSM_GPIO_OUT); else - writel(val & ~mask, msm_chip->regs.out); + msm_gpio_writel(msm_chip, val & ~mask, MSM_GPIO_OUT); return 0; } @@ -342,13 +359,13 @@ static void msm_gpio_update_both_edge_detect(struct msm_gpio_chip *msm_chip) int loop_limit = 100; unsigned pol, val, val2, intstat; do { - val = readl(msm_chip->regs.in); - pol = readl(msm_chip->regs.int_pos); + val = msm_gpio_readl(msm_chip, MSM_GPIO_IN); + pol = msm_gpio_readl(msm_chip, MSM_GPIO_INT_POS); pol = (pol & ~msm_chip->both_edge_detect) | (~val & msm_chip->both_edge_detect); - writel(pol, msm_chip->regs.int_pos); - intstat = readl(msm_chip->regs.int_status); - val2 = readl(msm_chip->regs.in); + msm_gpio_writel(msm_chip, pol, MSM_GPIO_INT_POS); + intstat = msm_gpio_readl(msm_chip, MSM_GPIO_INT_STATUS); + val2 = msm_gpio_readl(msm_chip, MSM_GPIO_IN); if (((val ^ val2) & msm_chip->both_edge_detect & ~intstat) == 0) return; } while (loop_limit-- > 0); @@ -365,10 +382,11 @@ static int msm_gpio_clear_detect_status(struct msm_gpio_chip *msm_chip, /* Save interrupts that already triggered before we loose them. */ /* Any interrupt that triggers between the read of int_status */ /* and the write to int_clear will still be lost though. */ - msm_chip->int_status_copy |= readl(msm_chip->regs.int_status); + msm_chip->int_status_copy |= + msm_gpio_readl(msm_chip, MSM_GPIO_INT_STATUS); msm_chip->int_status_copy &= ~bit; #endif - writel(bit, msm_chip->regs.int_clear); + msm_gpio_writel(msm_chip, bit, MSM_GPIO_INT_CLEAR); msm_gpio_update_both_edge_detect(msm_chip); return 0; } @@ -377,10 +395,12 @@ static int msm_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { struct msm_gpio_chip *msm_chip; unsigned long irq_flags; + u32 val; msm_chip = container_of(chip, struct msm_gpio_chip, chip); spin_lock_irqsave(&msm_chip->lock, irq_flags); - writel(readl(msm_chip->regs.oe) & ~BIT(offset), msm_chip->regs.oe); + val = msm_gpio_readl(msm_chip, MSM_GPIO_OE) & ~BIT(offset); + msm_gpio_writel(msm_chip, val, MSM_GPIO_OE); spin_unlock_irqrestore(&msm_chip->lock, irq_flags); return 0; } @@ -390,11 +410,13 @@ msm_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { struct msm_gpio_chip *msm_chip; unsigned long irq_flags; + u32 val; msm_chip = container_of(chip, struct msm_gpio_chip, chip); spin_lock_irqsave(&msm_chip->lock, irq_flags); msm_gpio_write(msm_chip, offset, value); - writel(readl(msm_chip->regs.oe) | BIT(offset), msm_chip->regs.oe); + val = msm_gpio_readl(msm_chip, MSM_GPIO_OE) | BIT(offset); + msm_gpio_writel(msm_chip, val, MSM_GPIO_OE); spin_unlock_irqrestore(&msm_chip->lock, irq_flags); return 0; } @@ -404,7 +426,7 @@ static int msm_gpio_get(struct gpio_chip *chip, unsigned offset) struct msm_gpio_chip *msm_chip; msm_chip = container_of(chip, struct msm_gpio_chip, chip); - return (readl(msm_chip->regs.in) & (1U << offset)) ? 1 : 0; + return (msm_gpio_readl(msm_chip, MSM_GPIO_IN) & (1U << offset)) ? 1 : 0; } static void msm_gpio_set(struct gpio_chip *chip, unsigned offset, int value) @@ -450,6 +472,11 @@ static struct msm_gpio_chip msm_gpio_chips_msm7x01[] = { MSM_GPIO_BANK(MSM7X00, 5, 107, 121), }; +static struct msm_gpio_initdata msm_gpio_7x01_init = { + .chips = msm_gpio_chips_msm7x01, + .count = ARRAY_SIZE(msm_gpio_chips_msm7x01), +}; + static struct msm_gpio_chip msm_gpio_chips_msm7x30[] = { MSM_GPIO_BANK(MSM7X30, 0, 0, 15), MSM_GPIO_BANK(MSM7X30, 1, 16, 43), @@ -461,6 +488,11 @@ static struct msm_gpio_chip msm_gpio_chips_msm7x30[] = { MSM_GPIO_BANK(MSM7X30, 7, 151, 181), }; +static struct msm_gpio_initdata msm_gpio_7x30_init = { + .chips = msm_gpio_chips_msm7x30, + .count = ARRAY_SIZE(msm_gpio_chips_msm7x30), +}; + static struct msm_gpio_chip msm_gpio_chips_qsd8x50[] = { MSM_GPIO_BANK(QSD8X50, 0, 0, 15), MSM_GPIO_BANK(QSD8X50, 1, 16, 42), @@ -472,6 +504,11 @@ static struct msm_gpio_chip msm_gpio_chips_qsd8x50[] = { MSM_GPIO_BANK(QSD8X50, 7, 153, 164), }; +static struct msm_gpio_initdata msm_gpio_8x50_init = { + .chips = msm_gpio_chips_qsd8x50, + .count = ARRAY_SIZE(msm_gpio_chips_qsd8x50), +}; + static void msm_gpio_irq_ack(struct irq_data *d) { unsigned long irq_flags; @@ -490,10 +527,10 @@ static void msm_gpio_irq_mask(struct irq_data *d) spin_lock_irqsave(&msm_chip->lock, irq_flags); /* level triggered interrupts are also latched */ - if (!(readl(msm_chip->regs.int_edge) & BIT(offset))) + if (!(msm_gpio_readl(msm_chip, MSM_GPIO_INT_EDGE) & BIT(offset))) msm_gpio_clear_detect_status(msm_chip, offset); msm_chip->int_enable[0] &= ~BIT(offset); - writel(msm_chip->int_enable[0], msm_chip->regs.int_en); + msm_gpio_writel(msm_chip, msm_chip->int_enable[0], MSM_GPIO_INT_EN); spin_unlock_irqrestore(&msm_chip->lock, irq_flags); } @@ -505,10 +542,10 @@ static void msm_gpio_irq_unmask(struct irq_data *d) spin_lock_irqsave(&msm_chip->lock, irq_flags); /* level triggered interrupts are also latched */ - if (!(readl(msm_chip->regs.int_edge) & BIT(offset))) + if (!(msm_gpio_readl(msm_chip, MSM_GPIO_INT_EDGE) & BIT(offset))) msm_gpio_clear_detect_status(msm_chip, offset); msm_chip->int_enable[0] |= BIT(offset); - writel(msm_chip->int_enable[0], msm_chip->regs.int_en); + msm_gpio_writel(msm_chip, msm_chip->int_enable[0], MSM_GPIO_INT_EN); spin_unlock_irqrestore(&msm_chip->lock, irq_flags); } @@ -537,12 +574,12 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) unsigned val, mask = BIT(offset); spin_lock_irqsave(&msm_chip->lock, irq_flags); - val = readl(msm_chip->regs.int_edge); + val = msm_gpio_readl(msm_chip, MSM_GPIO_INT_EDGE); if (flow_type & IRQ_TYPE_EDGE_BOTH) { - writel(val | mask, msm_chip->regs.int_edge); + msm_gpio_writel(msm_chip, val | mask, MSM_GPIO_INT_EDGE); __irq_set_handler_locked(d->irq, handle_edge_irq); } else { - writel(val & ~mask, msm_chip->regs.int_edge); + msm_gpio_writel(msm_chip, val & ~mask, MSM_GPIO_INT_EDGE); __irq_set_handler_locked(d->irq, handle_level_irq); } if ((flow_type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { @@ -550,11 +587,12 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int flow_type) msm_gpio_update_both_edge_detect(msm_chip); } else { msm_chip->both_edge_detect &= ~mask; - val = readl(msm_chip->regs.int_pos); + val = msm_gpio_readl(msm_chip, MSM_GPIO_INT_POS); if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_HIGH)) - writel(val | mask, msm_chip->regs.int_pos); + val |= mask; else - writel(val & ~mask, msm_chip->regs.int_pos); + val &= ~mask; + msm_gpio_writel(msm_chip, val, MSM_GPIO_INT_POS); } spin_unlock_irqrestore(&msm_chip->lock, irq_flags); return 0; @@ -567,7 +605,7 @@ static void msm_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) for (i = 0; i < msm_gpio_count; i++) { struct msm_gpio_chip *msm_chip = &msm_gpio_chips[i]; - val = readl(msm_chip->regs.int_status); + val = msm_gpio_readl(msm_chip, MSM_GPIO_INT_STATUS); val &= msm_chip->int_enable[0]; while (val) { mask = val & -val; @@ -592,22 +630,36 @@ static struct irq_chip msm_gpio_irq_chip = { .irq_set_type = msm_gpio_irq_set_type, }; -static int __init msm_init_gpio(void) +static int __devinit gpio_msm_v1_probe(struct platform_device *pdev) { int i, j = 0; - - if (cpu_is_msm7x01()) { - msm_gpio_chips = msm_gpio_chips_msm7x01; - msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x01); - } else if (cpu_is_msm7x30()) { - msm_gpio_chips = msm_gpio_chips_msm7x30; - msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_msm7x30); - } else if (cpu_is_qsd8x50()) { - msm_gpio_chips = msm_gpio_chips_qsd8x50; - msm_gpio_count = ARRAY_SIZE(msm_gpio_chips_qsd8x50); - } else { - return 0; - } + const struct platform_device_id *dev_id = platform_get_device_id(pdev); + struct msm_gpio_initdata *data; + int irq1, irq2; + struct resource *res; + void __iomem *base1, __iomem *base2; + + data = (struct msm_gpio_initdata *)dev_id->driver_data; + msm_gpio_chips = data->chips; + msm_gpio_count = data->count; + + irq1 = platform_get_irq(pdev, 0); + if (irq1 < 0) + return irq1; + + irq2 = platform_get_irq(pdev, 1); + if (irq2 < 0) + return irq2; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base1 = devm_request_and_ioremap(&pdev->dev, res); + if (!base1) + return -EADDRNOTAVAIL; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + base2 = devm_request_and_ioremap(&pdev->dev, res); + if (!base2) + return -EADDRNOTAVAIL; for (i = FIRST_GPIO_IRQ; i < FIRST_GPIO_IRQ + NR_GPIO_IRQS; i++) { if (i - FIRST_GPIO_IRQ >= @@ -621,16 +673,42 @@ static int __init msm_init_gpio(void) } for (i = 0; i < msm_gpio_count; i++) { + if (i == 1) + msm_gpio_chips[i].base = base2; + else + msm_gpio_chips[i].base = base1; spin_lock_init(&msm_gpio_chips[i].lock); - writel(0, msm_gpio_chips[i].regs.int_en); + msm_gpio_writel(&msm_gpio_chips[i], 0, MSM_GPIO_INT_EN); gpiochip_add(&msm_gpio_chips[i].chip); } - irq_set_chained_handler(INT_GPIO_GROUP1, msm_gpio_irq_handler); - irq_set_chained_handler(INT_GPIO_GROUP2, msm_gpio_irq_handler); - irq_set_irq_wake(INT_GPIO_GROUP1, 1); - irq_set_irq_wake(INT_GPIO_GROUP2, 2); + irq_set_chained_handler(irq1, msm_gpio_irq_handler); + irq_set_chained_handler(irq2, msm_gpio_irq_handler); + irq_set_irq_wake(irq1, 1); + irq_set_irq_wake(irq2, 2); return 0; } -postcore_initcall(msm_init_gpio); +static struct platform_device_id gpio_msm_v1_device_ids[] = { + { "gpio-msm-7201", (unsigned long)&msm_gpio_7x01_init }, + { "gpio-msm-7x30", (unsigned long)&msm_gpio_7x30_init }, + { "gpio-msm-8x50", (unsigned long)&msm_gpio_8x50_init }, + { } +}; +MODULE_DEVICE_TABLE(platform, gpio_msm_v1_device_ids); + +static struct platform_driver gpio_msm_v1_driver = { + .driver = { + .name = "gpio-msm-v1", + .owner = THIS_MODULE, + }, + .probe = gpio_msm_v1_probe, + .id_table = gpio_msm_v1_device_ids, +}; + +static int __init gpio_msm_v1_init(void) +{ + return platform_driver_register(&gpio_msm_v1_driver); +} +postcore_initcall(gpio_msm_v1_init); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpio/gpio-msm-v2.c b/drivers/gpio/gpio-msm-v2.c index 55a7e7769af6..dd2eddeb1e0c 100644 --- a/drivers/gpio/gpio-msm-v2.c +++ b/drivers/gpio/gpio-msm-v2.c @@ -23,13 +23,12 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irq.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/spinlock.h> -#include <asm/mach/irq.h> - #include <mach/msm_gpiomux.h> #include <mach/msm_iomap.h> diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 7877335c4cc8..7176743915d3 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -24,6 +24,7 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/gpio.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -32,7 +33,6 @@ #include <linux/of_device.h> #include <linux/module.h> #include <asm-generic/bug.h> -#include <asm/mach/irq.h> enum mxc_gpio_hwtype { IMX1_GPIO, /* runs on i.mx1 */ diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 159f5c57eb45..a612ea1c53cb 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -25,11 +25,10 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/gpio.h> #include <linux/platform_data/gpio-omap.h> -#include <asm/mach/irq.h> - #define OFF_MODE 1 static LIST_HEAD(omap_gpio_list); diff --git a/drivers/gpio/gpio-pl061.c b/drivers/gpio/gpio-pl061.c index b820869ca93c..29763361d13c 100644 --- a/drivers/gpio/gpio-pl061.c +++ b/drivers/gpio/gpio-pl061.c @@ -15,6 +15,7 @@ #include <linux/io.h> #include <linux/ioport.h> #include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> #include <linux/bitops.h> #include <linux/workqueue.h> #include <linux/gpio.h> @@ -23,7 +24,6 @@ #include <linux/amba/pl061.h> #include <linux/slab.h> #include <linux/pm.h> -#include <asm/mach/irq.h> #define GPIODIR 0x400 #define GPIOIS 0x404 diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 9cc108d2b770..7523b6d108d0 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -19,6 +19,7 @@ #include <linux/init.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/io.h> #include <linux/of.h> #include <linux/of_device.h> @@ -26,8 +27,6 @@ #include <linux/syscore_ops.h> #include <linux/slab.h> -#include <asm/mach/irq.h> - #include <mach/irqs.h> /* diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 414ad912232f..8e2155548888 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -27,11 +27,10 @@ #include <linux/platform_device.h> #include <linux/module.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/pinctrl/consumer.h> #include <linux/pm.h> -#include <asm/mach/irq.h> - #define GPIO_BANK(x) ((x) >> 5) #define GPIO_PORT(x) (((x) >> 3) & 0x3) #define GPIO_BIT(x) ((x) & 0x7) diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 98e3b87bdf1b..d5e119ca9425 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -2,9 +2,10 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o +obj-$(CONFIG_ARCH_MXS) += irq-mxs.o obj-$(CONFIG_METAG) += irq-metag-ext.o obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o -obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o +obj-$(CONFIG_ARCH_SUNXI) += irq-sun4i.o obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o obj-$(CONFIG_ARM_GIC) += irq-gic.o obj-$(CONFIG_ARM_VIC) += irq-vic.o diff --git a/drivers/irqchip/exynos-combiner.c b/drivers/irqchip/exynos-combiner.c index 04d86a9803f4..6a5201351507 100644 --- a/drivers/irqchip/exynos-combiner.c +++ b/drivers/irqchip/exynos-combiner.c @@ -13,6 +13,7 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <asm/mach/irq.h> diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index a32e0d5aa45f..47aea33a0782 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -28,6 +28,7 @@ #include <linux/module.h> #include <linux/list.h> #include <linux/smp.h> +#include <linux/cpu.h> #include <linux/cpu_pm.h> #include <linux/cpumask.h> #include <linux/io.h> @@ -38,12 +39,12 @@ #include <linux/interrupt.h> #include <linux/percpu.h> #include <linux/slab.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irqchip/arm-gic.h> #include <asm/irq.h> #include <asm/exception.h> #include <asm/smp_plat.h> -#include <asm/mach/irq.h> #include "irqchip.h" @@ -127,7 +128,7 @@ static inline void gic_set_base_accessor(struct gic_chip_data *data, #else #define gic_data_dist_base(d) ((d)->dist_base.common_base) #define gic_data_cpu_base(d) ((d)->cpu_base.common_base) -#define gic_set_base_accessor(d,f) +#define gic_set_base_accessor(d, f) #endif static inline void __iomem *gic_dist_base(struct irq_data *d) @@ -323,7 +324,7 @@ static void gic_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) cascade_irq = irq_find_mapping(chip_data->domain, gic_irq); if (unlikely(gic_irq < 32 || gic_irq > 1020)) - do_bad_IRQ(cascade_irq, desc); + handle_bad_irq(cascade_irq, desc); else generic_handle_irq(cascade_irq); @@ -699,6 +700,25 @@ static int gic_irq_domain_xlate(struct irq_domain *d, return 0; } +#ifdef CONFIG_SMP +static int __cpuinit gic_secondary_init(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + if (action == CPU_STARTING) + gic_cpu_init(&gic_data[0]); + return NOTIFY_OK; +} + +/* + * Notifier for enabling the GIC CPU interface. Set an arbitrarily high + * priority because the GIC needs to be up before the ARM generic timers. + */ +static struct notifier_block __cpuinitdata gic_cpu_notifier = { + .notifier_call = gic_secondary_init, + .priority = 100, +}; +#endif + const struct irq_domain_ops gic_irq_domain_ops = { .map = gic_irq_domain_map, .xlate = gic_irq_domain_xlate, @@ -789,6 +809,7 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, #ifdef CONFIG_SMP set_smp_cross_call(gic_raise_softirq); + register_cpu_notifier(&gic_cpu_notifier); #endif set_handle_irq(gic_handle_irq); @@ -799,15 +820,8 @@ void __init gic_init_bases(unsigned int gic_nr, int irq_start, gic_pm_init(gic); } -void __cpuinit gic_secondary_init(unsigned int gic_nr) -{ - BUG_ON(gic_nr >= MAX_GIC_NR); - - gic_cpu_init(&gic_data[gic_nr]); -} - #ifdef CONFIG_OF -static int gic_cnt __initdata = 0; +static int gic_cnt __initdata; int __init gic_of_init(struct device_node *node, struct device_node *parent) { diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c new file mode 100644 index 000000000000..29889bbdcc6d --- /dev/null +++ b/drivers/irqchip/irq-mxs.c @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2009-2010 Freescale Semiconductor, Inc. All Rights Reserved. + * + * 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> +#include <linux/stmp_device.h> +#include <asm/exception.h> + +#include "irqchip.h" + +#define HW_ICOLL_VECTOR 0x0000 +#define HW_ICOLL_LEVELACK 0x0010 +#define HW_ICOLL_CTRL 0x0020 +#define HW_ICOLL_STAT_OFFSET 0x0070 +#define HW_ICOLL_INTERRUPTn_SET(n) (0x0124 + (n) * 0x10) +#define HW_ICOLL_INTERRUPTn_CLR(n) (0x0128 + (n) * 0x10) +#define BM_ICOLL_INTERRUPTn_ENABLE 0x00000004 +#define BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 0x1 + +#define ICOLL_NUM_IRQS 128 + +static void __iomem *icoll_base; +static struct irq_domain *icoll_domain; + +static void icoll_ack_irq(struct irq_data *d) +{ + /* + * The Interrupt Collector is able to prioritize irqs. + * Currently only level 0 is used. So acking can use + * BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0 unconditionally. + */ + __raw_writel(BV_ICOLL_LEVELACK_IRQLEVELACK__LEVEL0, + icoll_base + HW_ICOLL_LEVELACK); +} + +static void icoll_mask_irq(struct irq_data *d) +{ + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + icoll_base + HW_ICOLL_INTERRUPTn_CLR(d->hwirq)); +} + +static void icoll_unmask_irq(struct irq_data *d) +{ + __raw_writel(BM_ICOLL_INTERRUPTn_ENABLE, + icoll_base + HW_ICOLL_INTERRUPTn_SET(d->hwirq)); +} + +static struct irq_chip mxs_icoll_chip = { + .irq_ack = icoll_ack_irq, + .irq_mask = icoll_mask_irq, + .irq_unmask = icoll_unmask_irq, +}; + +asmlinkage void __exception_irq_entry icoll_handle_irq(struct pt_regs *regs) +{ + u32 irqnr; + + do { + irqnr = __raw_readl(icoll_base + HW_ICOLL_STAT_OFFSET); + if (irqnr != 0x7f) { + __raw_writel(irqnr, icoll_base + HW_ICOLL_VECTOR); + irqnr = irq_find_mapping(icoll_domain, irqnr); + handle_IRQ(irqnr, regs); + continue; + } + break; + } while (1); +} + +static int icoll_irq_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &mxs_icoll_chip, handle_level_irq); + set_irq_flags(virq, IRQF_VALID); + + return 0; +} + +static struct irq_domain_ops icoll_irq_domain_ops = { + .map = icoll_irq_domain_map, + .xlate = irq_domain_xlate_onecell, +}; + +static void __init icoll_of_init(struct device_node *np, + struct device_node *interrupt_parent) +{ + icoll_base = of_iomap(np, 0); + WARN_ON(!icoll_base); + + /* + * Interrupt Collector reset, which initializes the priority + * for each irq to level 0. + */ + stmp_reset_block(icoll_base + HW_ICOLL_CTRL); + + icoll_domain = irq_domain_add_linear(np, ICOLL_NUM_IRQS, + &icoll_irq_domain_ops, NULL); + WARN_ON(!icoll_domain); +} +IRQCHIP_DECLARE(mxs, "fsl,icoll", icoll_of_init); diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c new file mode 100644 index 000000000000..b66d4ae06898 --- /dev/null +++ b/drivers/irqchip/irq-sun4i.c @@ -0,0 +1,149 @@ +/* + * Allwinner A1X SoCs IRQ chip driver. + * + * Copyright (C) 2012 Maxime Ripard + * + * Maxime Ripard <maxime.ripard@free-electrons.com> + * + * Based on code from + * Allwinner Technology Co., Ltd. <www.allwinnertech.com> + * Benn Huang <benn@allwinnertech.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_irq.h> + +#include <asm/exception.h> +#include <asm/mach/irq.h> + +#include "irqchip.h" + +#define SUN4I_IRQ_VECTOR_REG 0x00 +#define SUN4I_IRQ_PROTECTION_REG 0x08 +#define SUN4I_IRQ_NMI_CTRL_REG 0x0c +#define SUN4I_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) +#define SUN4I_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) +#define SUN4I_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) +#define SUN4I_IRQ_MASK_REG(x) (0x50 + 0x4 * x) + +static void __iomem *sun4i_irq_base; +static struct irq_domain *sun4i_irq_domain; + +static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs); + +void sun4i_irq_ack(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); + writel(val | (1 << irq_off), + sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg)); +} + +static void sun4i_irq_mask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + writel(val & ~(1 << irq_off), + sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); +} + +static void sun4i_irq_unmask(struct irq_data *irqd) +{ + unsigned int irq = irqd_to_hwirq(irqd); + unsigned int irq_off = irq % 32; + int reg = irq / 32; + u32 val; + + val = readl(sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); + writel(val | (1 << irq_off), + sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(reg)); +} + +static struct irq_chip sun4i_irq_chip = { + .name = "sun4i_irq", + .irq_ack = sun4i_irq_ack, + .irq_mask = sun4i_irq_mask, + .irq_unmask = sun4i_irq_unmask, +}; + +static int sun4i_irq_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, &sun4i_irq_chip, + handle_level_irq); + set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); + + return 0; +} + +static struct irq_domain_ops sun4i_irq_ops = { + .map = sun4i_irq_map, + .xlate = irq_domain_xlate_onecell, +}; + +static int __init sun4i_of_init(struct device_node *node, + struct device_node *parent) +{ + sun4i_irq_base = of_iomap(node, 0); + if (!sun4i_irq_base) + panic("%s: unable to map IC registers\n", + node->full_name); + + /* Disable all interrupts */ + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(0)); + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1)); + writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2)); + + /* Mask all the interrupts */ + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0)); + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1)); + writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2)); + + /* Clear all the pending interrupts */ + writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)); + writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(1)); + writel(0xffffffff, sun4i_irq_base + SUN4I_IRQ_PENDING_REG(2)); + + /* Enable protection mode */ + writel(0x01, sun4i_irq_base + SUN4I_IRQ_PROTECTION_REG); + + /* Configure the external interrupt source type */ + writel(0x00, sun4i_irq_base + SUN4I_IRQ_NMI_CTRL_REG); + + sun4i_irq_domain = irq_domain_add_linear(node, 3 * 32, + &sun4i_irq_ops, NULL); + if (!sun4i_irq_domain) + panic("%s: unable to create IRQ domain\n", node->full_name); + + set_handle_irq(sun4i_handle_irq); + + return 0; +} +IRQCHIP_DECLARE(allwinner_sun4i_ic, "allwinner,sun4i-ic", sun4i_of_init); + +static asmlinkage void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs) +{ + u32 irq, hwirq; + + hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; + while (hwirq != 0) { + irq = irq_find_mapping(sun4i_irq_domain, hwirq); + handle_IRQ(irq, regs); + hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2; + } +} diff --git a/drivers/irqchip/irq-sunxi.c b/drivers/irqchip/irq-sunxi.c deleted file mode 100644 index 10974fa42653..000000000000 --- a/drivers/irqchip/irq-sunxi.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Allwinner A1X SoCs IRQ chip driver. - * - * Copyright (C) 2012 Maxime Ripard - * - * Maxime Ripard <maxime.ripard@free-electrons.com> - * - * Based on code from - * Allwinner Technology Co., Ltd. <www.allwinnertech.com> - * Benn Huang <benn@allwinnertech.com> - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#include <linux/io.h> -#include <linux/irq.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_irq.h> - -#include <linux/irqchip/sunxi.h> - -#define SUNXI_IRQ_VECTOR_REG 0x00 -#define SUNXI_IRQ_PROTECTION_REG 0x08 -#define SUNXI_IRQ_NMI_CTRL_REG 0x0c -#define SUNXI_IRQ_PENDING_REG(x) (0x10 + 0x4 * x) -#define SUNXI_IRQ_FIQ_PENDING_REG(x) (0x20 + 0x4 * x) -#define SUNXI_IRQ_ENABLE_REG(x) (0x40 + 0x4 * x) -#define SUNXI_IRQ_MASK_REG(x) (0x50 + 0x4 * x) - -static void __iomem *sunxi_irq_base; -static struct irq_domain *sunxi_irq_domain; - -void sunxi_irq_ack(struct irq_data *irqd) -{ - unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; - - val = readl(sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); - writel(val | (1 << irq_off), - sunxi_irq_base + SUNXI_IRQ_PENDING_REG(reg)); -} - -static void sunxi_irq_mask(struct irq_data *irqd) -{ - unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; - - val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); - writel(val & ~(1 << irq_off), - sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); -} - -static void sunxi_irq_unmask(struct irq_data *irqd) -{ - unsigned int irq = irqd_to_hwirq(irqd); - unsigned int irq_off = irq % 32; - int reg = irq / 32; - u32 val; - - val = readl(sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); - writel(val | (1 << irq_off), - sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(reg)); -} - -static struct irq_chip sunxi_irq_chip = { - .name = "sunxi_irq", - .irq_ack = sunxi_irq_ack, - .irq_mask = sunxi_irq_mask, - .irq_unmask = sunxi_irq_unmask, -}; - -static int sunxi_irq_map(struct irq_domain *d, unsigned int virq, - irq_hw_number_t hw) -{ - irq_set_chip_and_handler(virq, &sunxi_irq_chip, - handle_level_irq); - set_irq_flags(virq, IRQF_VALID | IRQF_PROBE); - - return 0; -} - -static struct irq_domain_ops sunxi_irq_ops = { - .map = sunxi_irq_map, - .xlate = irq_domain_xlate_onecell, -}; - -static int __init sunxi_of_init(struct device_node *node, - struct device_node *parent) -{ - sunxi_irq_base = of_iomap(node, 0); - if (!sunxi_irq_base) - panic("%s: unable to map IC registers\n", - node->full_name); - - /* Disable all interrupts */ - writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(0)); - writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(1)); - writel(0, sunxi_irq_base + SUNXI_IRQ_ENABLE_REG(2)); - - /* Mask all the interrupts */ - writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(0)); - writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(1)); - writel(0, sunxi_irq_base + SUNXI_IRQ_MASK_REG(2)); - - /* Clear all the pending interrupts */ - writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(0)); - writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(1)); - writel(0xffffffff, sunxi_irq_base + SUNXI_IRQ_PENDING_REG(2)); - - /* Enable protection mode */ - writel(0x01, sunxi_irq_base + SUNXI_IRQ_PROTECTION_REG); - - /* Configure the external interrupt source type */ - writel(0x00, sunxi_irq_base + SUNXI_IRQ_NMI_CTRL_REG); - - sunxi_irq_domain = irq_domain_add_linear(node, 3 * 32, - &sunxi_irq_ops, NULL); - if (!sunxi_irq_domain) - panic("%s: unable to create IRQ domain\n", node->full_name); - - return 0; -} - -static struct of_device_id sunxi_irq_dt_ids[] __initconst = { - { .compatible = "allwinner,sunxi-ic", .data = sunxi_of_init }, - { } -}; - -void __init sunxi_init_irq(void) -{ - of_irq_init(sunxi_irq_dt_ids); -} - -asmlinkage void __exception_irq_entry sunxi_handle_irq(struct pt_regs *regs) -{ - u32 irq, hwirq; - - hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; - while (hwirq != 0) { - irq = irq_find_mapping(sunxi_irq_domain, hwirq); - handle_IRQ(irq, regs); - hwirq = readl(sunxi_irq_base + SUNXI_IRQ_VECTOR_REG) >> 2; - } -} diff --git a/drivers/irqchip/irq-vic.c b/drivers/irqchip/irq-vic.c index 3cf97aaebe40..884d11c7355f 100644 --- a/drivers/irqchip/irq-vic.c +++ b/drivers/irqchip/irq-vic.c @@ -23,6 +23,7 @@ #include <linux/init.h> #include <linux/list.h> #include <linux/io.h> +#include <linux/irq.h> #include <linux/irqdomain.h> #include <linux/of.h> #include <linux/of_address.h> @@ -33,7 +34,7 @@ #include <linux/irqchip/arm-vic.h> #include <asm/exception.h> -#include <asm/mach/irq.h> +#include <asm/irq.h> #include "irqchip.h" diff --git a/drivers/memory/tegra30-mc.c b/drivers/memory/tegra30-mc.c index 0b975986777d..f4ae074badc3 100644 --- a/drivers/memory/tegra30-mc.c +++ b/drivers/memory/tegra30-mc.c @@ -268,6 +268,7 @@ static const u32 tegra30_mc_ctx[] = { MC_INTMASK, }; +#ifdef CONFIG_PM static int tegra30_mc_suspend(struct device *dev) { int i; @@ -291,6 +292,7 @@ static int tegra30_mc_resume(struct device *dev) mc_readl(mc, MC_TIMING_CONTROL); return 0; } +#endif static UNIVERSAL_DEV_PM_OPS(tegra30_mc_pm, tegra30_mc_suspend, diff --git a/drivers/mmc/host/msm_sdcc.c b/drivers/mmc/host/msm_sdcc.c index 7c0af0e80047..0ee4a57fe6b2 100644 --- a/drivers/mmc/host/msm_sdcc.c +++ b/drivers/mmc/host/msm_sdcc.c @@ -43,7 +43,6 @@ #include <asm/sizes.h> #include <linux/platform_data/mmc-msm_sdcc.h> -#include <mach/msm_iomap.h> #include <mach/dma.h> #include <mach/clk.h> diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 63fb265e0da6..8d6794cdf899 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c @@ -25,14 +25,93 @@ #include <mach/dma.h> -#include <mach/regs-sdi.h> - #include <linux/platform_data/mmc-s3cmci.h> #include "s3cmci.h" #define DRIVER_NAME "s3c-mci" +#define S3C2410_SDICON (0x00) +#define S3C2410_SDIPRE (0x04) +#define S3C2410_SDICMDARG (0x08) +#define S3C2410_SDICMDCON (0x0C) +#define S3C2410_SDICMDSTAT (0x10) +#define S3C2410_SDIRSP0 (0x14) +#define S3C2410_SDIRSP1 (0x18) +#define S3C2410_SDIRSP2 (0x1C) +#define S3C2410_SDIRSP3 (0x20) +#define S3C2410_SDITIMER (0x24) +#define S3C2410_SDIBSIZE (0x28) +#define S3C2410_SDIDCON (0x2C) +#define S3C2410_SDIDCNT (0x30) +#define S3C2410_SDIDSTA (0x34) +#define S3C2410_SDIFSTA (0x38) + +#define S3C2410_SDIDATA (0x3C) +#define S3C2410_SDIIMSK (0x40) + +#define S3C2440_SDIDATA (0x40) +#define S3C2440_SDIIMSK (0x3C) + +#define S3C2440_SDICON_SDRESET (1 << 8) +#define S3C2410_SDICON_SDIOIRQ (1 << 3) +#define S3C2410_SDICON_FIFORESET (1 << 1) +#define S3C2410_SDICON_CLOCKTYPE (1 << 0) + +#define S3C2410_SDICMDCON_LONGRSP (1 << 10) +#define S3C2410_SDICMDCON_WAITRSP (1 << 9) +#define S3C2410_SDICMDCON_CMDSTART (1 << 8) +#define S3C2410_SDICMDCON_SENDERHOST (1 << 6) +#define S3C2410_SDICMDCON_INDEX (0x3f) + +#define S3C2410_SDICMDSTAT_CRCFAIL (1 << 12) +#define S3C2410_SDICMDSTAT_CMDSENT (1 << 11) +#define S3C2410_SDICMDSTAT_CMDTIMEOUT (1 << 10) +#define S3C2410_SDICMDSTAT_RSPFIN (1 << 9) + +#define S3C2440_SDIDCON_DS_WORD (2 << 22) +#define S3C2410_SDIDCON_TXAFTERRESP (1 << 20) +#define S3C2410_SDIDCON_RXAFTERCMD (1 << 19) +#define S3C2410_SDIDCON_BLOCKMODE (1 << 17) +#define S3C2410_SDIDCON_WIDEBUS (1 << 16) +#define S3C2410_SDIDCON_DMAEN (1 << 15) +#define S3C2410_SDIDCON_STOP (1 << 14) +#define S3C2440_SDIDCON_DATSTART (1 << 14) + +#define S3C2410_SDIDCON_XFER_RXSTART (2 << 12) +#define S3C2410_SDIDCON_XFER_TXSTART (3 << 12) + +#define S3C2410_SDIDCON_BLKNUM_MASK (0xFFF) + +#define S3C2410_SDIDSTA_SDIOIRQDETECT (1 << 9) +#define S3C2410_SDIDSTA_FIFOFAIL (1 << 8) +#define S3C2410_SDIDSTA_CRCFAIL (1 << 7) +#define S3C2410_SDIDSTA_RXCRCFAIL (1 << 6) +#define S3C2410_SDIDSTA_DATATIMEOUT (1 << 5) +#define S3C2410_SDIDSTA_XFERFINISH (1 << 4) +#define S3C2410_SDIDSTA_TXDATAON (1 << 1) +#define S3C2410_SDIDSTA_RXDATAON (1 << 0) + +#define S3C2440_SDIFSTA_FIFORESET (1 << 16) +#define S3C2440_SDIFSTA_FIFOFAIL (3 << 14) +#define S3C2410_SDIFSTA_TFDET (1 << 13) +#define S3C2410_SDIFSTA_RFDET (1 << 12) +#define S3C2410_SDIFSTA_COUNTMASK (0x7f) + +#define S3C2410_SDIIMSK_RESPONSECRC (1 << 17) +#define S3C2410_SDIIMSK_CMDSENT (1 << 16) +#define S3C2410_SDIIMSK_CMDTIMEOUT (1 << 15) +#define S3C2410_SDIIMSK_RESPONSEND (1 << 14) +#define S3C2410_SDIIMSK_SDIOIRQ (1 << 12) +#define S3C2410_SDIIMSK_FIFOFAIL (1 << 11) +#define S3C2410_SDIIMSK_CRCSTATUS (1 << 10) +#define S3C2410_SDIIMSK_DATACRC (1 << 9) +#define S3C2410_SDIIMSK_DATATIMEOUT (1 << 8) +#define S3C2410_SDIIMSK_DATAFINISH (1 << 7) +#define S3C2410_SDIIMSK_TXFIFOHALF (1 << 4) +#define S3C2410_SDIIMSK_RXFIFOLAST (1 << 2) +#define S3C2410_SDIIMSK_RXFIFOHALF (1 << 0) + enum dbg_channels { dbg_err = (1 << 0), dbg_debug = (1 << 1), diff --git a/drivers/pinctrl/pinctrl-at91.c b/drivers/pinctrl/pinctrl-at91.c index efb7f10e902a..b141a28473b5 100644 --- a/drivers/pinctrl/pinctrl-at91.c +++ b/drivers/pinctrl/pinctrl-at91.c @@ -18,6 +18,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/io.h> #include <linux/gpio.h> #include <linux/pinctrl/machine.h> @@ -27,8 +28,6 @@ /* Since we request GPIOs from ourself */ #include <linux/pinctrl/consumer.h> -#include <asm/mach/irq.h> - #include <mach/hardware.h> #include <mach/at91_pio.h> diff --git a/drivers/pinctrl/pinctrl-exynos.c b/drivers/pinctrl/pinctrl-exynos.c index 538b9ddaadf7..7265e551dddb 100644 --- a/drivers/pinctrl/pinctrl-exynos.c +++ b/drivers/pinctrl/pinctrl-exynos.c @@ -23,13 +23,12 @@ #include <linux/interrupt.h> #include <linux/irqdomain.h> #include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> #include <linux/of_irq.h> #include <linux/io.h> #include <linux/slab.h> #include <linux/err.h> -#include <asm/mach/irq.h> - #include "pinctrl-samsung.h" #include "pinctrl-exynos.h" diff --git a/drivers/pinctrl/pinctrl-nomadik.c b/drivers/pinctrl/pinctrl-nomadik.c index 36d20293de5c..93eba9715e62 100644 --- a/drivers/pinctrl/pinctrl-nomadik.c +++ b/drivers/pinctrl/pinctrl-nomadik.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/slab.h> #include <linux/of_device.h> #include <linux/of_address.h> @@ -33,7 +34,6 @@ /* Since we request GPIOs from ourself */ #include <linux/pinctrl/consumer.h> #include <linux/platform_data/pinctrl-nomadik.h> -#include <asm/mach/irq.h> #include "pinctrl-nomadik.h" #include "core.h" diff --git a/drivers/pinctrl/pinctrl-sirf.c b/drivers/pinctrl/pinctrl-sirf.c index d02498b30c6e..ab26b4b669d5 100644 --- a/drivers/pinctrl/pinctrl-sirf.c +++ b/drivers/pinctrl/pinctrl-sirf.c @@ -14,6 +14,7 @@ #include <linux/slab.h> #include <linux/err.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> #include <linux/pinctrl/consumer.h> @@ -25,7 +26,6 @@ #include <linux/bitops.h> #include <linux/gpio.h> #include <linux/of_gpio.h> -#include <asm/mach/irq.h> #define DRIVER_NAME "pinmux-sirf" diff --git a/drivers/pinctrl/spear/pinctrl-plgpio.c b/drivers/pinctrl/spear/pinctrl-plgpio.c index 295b349a05cf..a4908ecd74fb 100644 --- a/drivers/pinctrl/spear/pinctrl-plgpio.c +++ b/drivers/pinctrl/spear/pinctrl-plgpio.c @@ -15,12 +15,12 @@ #include <linux/io.h> #include <linux/irq.h> #include <linux/irqdomain.h> +#include <linux/irqchip/chained_irq.h> #include <linux/module.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/spinlock.h> -#include <asm/mach/irq.h> #define MAX_GPIO_PER_REG 32 #define PIN_OFFSET(pin) (pin % MAX_GPIO_PER_REG) diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index 98f0d3c30738..67d26128bc85 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -30,8 +30,6 @@ #include <linux/stmp_device.h> #include <linux/stmp3xxx_rtc_wdt.h> -#include <mach/common.h> - #define STMP3XXX_RTC_CTRL 0x0 #define STMP3XXX_RTC_CTRL_SET 0x4 #define STMP3XXX_RTC_CTRL_CLR 0x8 @@ -271,7 +269,7 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, rtc_data); - mxs_reset_block(rtc_data->io); + stmp_reset_block(rtc_data->io); writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, @@ -319,7 +317,7 @@ static int stmp3xxx_rtc_resume(struct platform_device *dev) { struct stmp3xxx_rtc_data *rtc_data = platform_get_drvdata(dev); - mxs_reset_block(rtc_data->io); + stmp_reset_block(rtc_data->io); writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, diff --git a/drivers/staging/iio/adc/mxs-lradc.c b/drivers/staging/iio/adc/mxs-lradc.c index 55a459b61907..0eb5b4d759e5 100644 --- a/drivers/staging/iio/adc/mxs-lradc.c +++ b/drivers/staging/iio/adc/mxs-lradc.c @@ -36,9 +36,6 @@ #include <linux/delay.h> #include <linux/input.h> -#include <mach/mxs.h> -#include <mach/common.h> - #include <linux/iio/iio.h> #include <linux/iio/buffer.h> #include <linux/iio/trigger.h> diff --git a/drivers/staging/imx-drm/ipu-v3/ipu-common.c b/drivers/staging/imx-drm/ipu-v3/ipu-common.c index 366f259e3756..6efe4e1b499f 100644 --- a/drivers/staging/imx-drm/ipu-v3/ipu-common.c +++ b/drivers/staging/imx-drm/ipu-v3/ipu-common.c @@ -25,8 +25,8 @@ #include <linux/clk.h> #include <linux/list.h> #include <linux/irq.h> +#include <linux/irqchip/chained_irq.h> #include <linux/of_device.h> -#include <asm/mach/irq.h> #include "imx-ipu-v3.h" #include "ipu-prv.h" diff --git a/drivers/usb/host/ohci-exynos.c b/drivers/usb/host/ohci-exynos.c index e3b7e85120e4..0bd6f47dfdc4 100644 --- a/drivers/usb/host/ohci-exynos.c +++ b/drivers/usb/host/ohci-exynos.c @@ -14,9 +14,10 @@ #include <linux/clk.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/platform_data/usb-exynos.h> +#include <linux/platform_data/usb-ohci-exynos.h> #include <linux/usb/phy.h> #include <linux/usb/samsung_usb_phy.h> + #include <plat/usb-phy.h> struct exynos_ohci_hcd { |