summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-03 03:14:07 +0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-03 03:14:07 +0400
commit0a58471541cc823ef8056d23945c39fec154481c (patch)
tree04a8499be0659ac16f82f3b0d0d8d2c2ccafe4dd /drivers
parentff933a0817f95efbeb97bec5ca609a13f8aed686 (diff)
parent08d38bebb4dcd6414944f8277ea5ea30010664fe (diff)
downloadlinux-0a58471541cc823ef8056d23945c39fec154481c.tar.xz
Merge tag 'cleanup-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc into next
Pull ARM SoC cleanups from Olof Johansson: "Cleanups for 3.16. Among these are: - a bunch of misc cleanups for Broadcom platforms, mostly housekeeping - enabling Common Clock Framework on the older s3c24xx Samsung chipsets - cleanup of the Versatile Express system controller code, moving it to syscon - power management cleanups for OMAP platforms plus a handful of other cleanups across the place" * tag 'cleanup-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (87 commits) ARM: kconfig: allow PCI support to be selected with ARCH_MULTIPLATFORM clk: samsung: fix build error ARM: vexpress: refine dependencies for new code clk: samsung: clk-s3c2410-dlck: do not use PNAME macro as it declares __initdata cpufreq: exynos: Fix the compile error ARM: S3C24XX: move debug-macro.S into the common space ARM: S3C24XX: use generic DEBUG_UART_PHY/_VIRT in debug macro ARM: S3C24XX: trim down debug uart handling ARM: compressed/head.S: remove s3c24xx special case ARM: EXYNOS: Remove unnecessary inclusion of cpu.h ARM: EXYNOS: Migrate Exynos specific macros from plat to mach ARM: EXYNOS: Remove exynos_subsys registration ARM: EXYNOS: Remove duplicate lines in Makefile ARM: EXYNOS: use v7_exit_coherency_flush macro for cache disabling ARM: OMAP4: PRCM: remove references to cm-regbits-44xx.h from PRCM core files ARM: OMAP3/4: PRM: add support of late_init call to prm_ll_ops ARM: OMAP3/OMAP4: PRM: add prm_features flags and add IO wakeup under it ARM: OMAP3/4: PRM: provide io chain reconfig function through irq setup ARM: OMAP2+: PRM: remove unnecessary cpu_is_XXX calls from prm_init / exit ARM: OMAP2+: PRCM: cleanup some header includes ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/bus/Kconfig10
-rw-r--r--drivers/bus/Makefile2
-rw-r--r--drivers/bus/vexpress-config.c202
-rw-r--r--drivers/clk/Kconfig9
-rw-r--r--drivers/clk/samsung/Makefile4
-rw-r--r--drivers/clk/samsung/clk-pll.c266
-rw-r--r--drivers/clk/samsung/clk-pll.h6
-rw-r--r--drivers/clk/samsung/clk-s3c2410-dclk.c440
-rw-r--r--drivers/clk/samsung/clk-s3c2410.c477
-rw-r--r--drivers/clk/samsung/clk-s3c2412.c269
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c462
-rw-r--r--drivers/clk/versatile/Kconfig26
-rw-r--r--drivers/clk/versatile/Makefile5
-rw-r--r--drivers/clk/versatile/clk-vexpress-osc.c96
-rw-r--r--drivers/clocksource/Kconfig11
-rw-r--r--drivers/clocksource/Makefile1
-rw-r--r--drivers/clocksource/timer-marco.c8
-rw-r--r--drivers/clocksource/timer-prima2.c47
-rw-r--r--drivers/clocksource/versatile.c40
-rw-r--r--drivers/cpufreq/exynos-cpufreq.c19
-rw-r--r--drivers/cpufreq/exynos-cpufreq.h8
-rw-r--r--drivers/cpufreq/exynos4x12-cpufreq.c11
-rw-r--r--drivers/cpufreq/s3c24xx-cpufreq.c1
-rw-r--r--drivers/hwmon/vexpress.c17
-rw-r--r--drivers/irqchip/irq-sirfsoc.c3
-rw-r--r--drivers/leds/Kconfig8
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/leds-versatile.c110
-rw-r--r--drivers/mfd/Kconfig15
-rw-r--r--drivers/mfd/Makefile2
-rw-r--r--drivers/mfd/syscon.c4
-rw-r--r--drivers/mfd/vexpress-config.c287
-rw-r--r--drivers/mfd/vexpress-sysreg.c554
-rw-r--r--drivers/misc/Kconfig9
-rw-r--r--drivers/misc/Makefile1
-rw-r--r--drivers/misc/vexpress-syscfg.c324
-rw-r--r--drivers/of/platform.c74
-rw-r--r--drivers/power/reset/vexpress-poweroff.c16
-rw-r--r--drivers/regulator/vexpress.c50
39 files changed, 3036 insertions, 859 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig
index 552373c4e362..286342778884 100644
--- a/drivers/bus/Kconfig
+++ b/drivers/bus/Kconfig
@@ -41,4 +41,14 @@ config ARM_CCI
help
Driver supporting the CCI cache coherent interconnect for ARM
platforms.
+
+config VEXPRESS_CONFIG
+ bool "Versatile Express configuration bus"
+ default y if ARCH_VEXPRESS
+ depends on ARM || ARM64
+ depends on OF
+ select REGMAP
+ help
+ Platform configuration infrastructure for the ARM Ltd.
+ Versatile Express.
endmenu
diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile
index 8947bdd0de8b..f095aa771de9 100644
--- a/drivers/bus/Makefile
+++ b/drivers/bus/Makefile
@@ -10,3 +10,5 @@ obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o
obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o
# CCI cache coherent interconnect for ARM platforms
obj-$(CONFIG_ARM_CCI) += arm-cci.o
+
+obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o
diff --git a/drivers/bus/vexpress-config.c b/drivers/bus/vexpress-config.c
new file mode 100644
index 000000000000..a64763b6b5fd
--- /dev/null
+++ b/drivers/bus/vexpress-config.c
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/vexpress.h>
+
+
+struct vexpress_config_bridge {
+ struct vexpress_config_bridge_ops *ops;
+ void *context;
+};
+
+
+static DEFINE_MUTEX(vexpress_config_mutex);
+static struct class *vexpress_config_class;
+static u32 vexpress_config_site_master = VEXPRESS_SITE_MASTER;
+
+
+void vexpress_config_set_master(u32 site)
+{
+ vexpress_config_site_master = site;
+}
+
+u32 vexpress_config_get_master(void)
+{
+ return vexpress_config_site_master;
+}
+
+void vexpress_config_lock(void *arg)
+{
+ mutex_lock(&vexpress_config_mutex);
+}
+
+void vexpress_config_unlock(void *arg)
+{
+ mutex_unlock(&vexpress_config_mutex);
+}
+
+
+static void vexpress_config_find_prop(struct device_node *node,
+ const char *name, u32 *val)
+{
+ /* Default value */
+ *val = 0;
+
+ of_node_get(node);
+ while (node) {
+ if (of_property_read_u32(node, name, val) == 0) {
+ of_node_put(node);
+ return;
+ }
+ node = of_get_next_parent(node);
+ }
+}
+
+int vexpress_config_get_topo(struct device_node *node, u32 *site,
+ u32 *position, u32 *dcc)
+{
+ vexpress_config_find_prop(node, "arm,vexpress,site", site);
+ if (*site == VEXPRESS_SITE_MASTER)
+ *site = vexpress_config_site_master;
+ if (WARN_ON(vexpress_config_site_master == VEXPRESS_SITE_MASTER))
+ return -EINVAL;
+ vexpress_config_find_prop(node, "arm,vexpress,position", position);
+ vexpress_config_find_prop(node, "arm,vexpress,dcc", dcc);
+
+ return 0;
+}
+
+
+static void vexpress_config_devres_release(struct device *dev, void *res)
+{
+ struct vexpress_config_bridge *bridge = dev_get_drvdata(dev->parent);
+ struct regmap *regmap = res;
+
+ bridge->ops->regmap_exit(regmap, bridge->context);
+}
+
+struct regmap *devm_regmap_init_vexpress_config(struct device *dev)
+{
+ struct vexpress_config_bridge *bridge;
+ struct regmap *regmap;
+ struct regmap **res;
+
+ if (WARN_ON(dev->parent->class != vexpress_config_class))
+ return ERR_PTR(-ENODEV);
+
+ bridge = dev_get_drvdata(dev->parent);
+ if (WARN_ON(!bridge))
+ return ERR_PTR(-EINVAL);
+
+ res = devres_alloc(vexpress_config_devres_release, sizeof(*res),
+ GFP_KERNEL);
+ if (!res)
+ return ERR_PTR(-ENOMEM);
+
+ regmap = bridge->ops->regmap_init(dev, bridge->context);
+ if (IS_ERR(regmap)) {
+ devres_free(res);
+ return regmap;
+ }
+
+ *res = regmap;
+ devres_add(dev, res);
+
+ return regmap;
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_vexpress_config);
+
+struct device *vexpress_config_bridge_register(struct device *parent,
+ struct vexpress_config_bridge_ops *ops, void *context)
+{
+ struct device *dev;
+ struct vexpress_config_bridge *bridge;
+
+ if (!vexpress_config_class) {
+ vexpress_config_class = class_create(THIS_MODULE,
+ "vexpress-config");
+ if (IS_ERR(vexpress_config_class))
+ return (void *)vexpress_config_class;
+ }
+
+ dev = device_create(vexpress_config_class, parent, 0,
+ NULL, "%s.bridge", dev_name(parent));
+
+ if (IS_ERR(dev))
+ return dev;
+
+ bridge = devm_kmalloc(dev, sizeof(*bridge), GFP_KERNEL);
+ if (!bridge) {
+ put_device(dev);
+ device_unregister(dev);
+ return ERR_PTR(-ENOMEM);
+ }
+ bridge->ops = ops;
+ bridge->context = context;
+
+ dev_set_drvdata(dev, bridge);
+
+ dev_dbg(parent, "Registered bridge '%s', parent node %p\n",
+ dev_name(dev), parent->of_node);
+
+ return dev;
+}
+
+
+static int vexpress_config_node_match(struct device *dev, const void *data)
+{
+ const struct device_node *node = data;
+
+ dev_dbg(dev, "Parent node %p, looking for %p\n",
+ dev->parent->of_node, node);
+
+ return dev->parent->of_node == node;
+}
+
+static int vexpress_config_populate(struct device_node *node)
+{
+ struct device_node *bridge;
+ struct device *parent;
+
+ bridge = of_parse_phandle(node, "arm,vexpress,config-bridge", 0);
+ if (!bridge)
+ return -EINVAL;
+
+ parent = class_find_device(vexpress_config_class, NULL, bridge,
+ vexpress_config_node_match);
+ if (WARN_ON(!parent))
+ return -ENODEV;
+
+ return of_platform_populate(node, NULL, NULL, parent);
+}
+
+static int __init vexpress_config_init(void)
+{
+ int err = 0;
+ struct device_node *node;
+
+ /* Need the config devices early, before the "normal" devices... */
+ for_each_compatible_node(node, NULL, "arm,vexpress,config-bus") {
+ err = vexpress_config_populate(node);
+ if (err)
+ break;
+ }
+
+ return err;
+}
+postcore_initcall(vexpress_config_init);
+
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 6f56d3a4f010..4fdfd6c70bd3 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -30,14 +30,7 @@ config COMMON_CLK_WM831X
Supports the clocking subsystem of the WM831x/2x series of
PMICs from Wolfson Microlectronics.
-config COMMON_CLK_VERSATILE
- bool "Clock driver for ARM Reference designs"
- depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64
- ---help---
- Supports clocking on ARM Reference designs:
- - Integrator/AP and Integrator/CP
- - RealView PB1176, EB, PB11MP and PBX
- - Versatile Express
+source "drivers/clk/versatile/Kconfig"
config COMMON_CLK_MAX77686
tristate "Clock driver for Maxim 77686 MFD"
diff --git a/drivers/clk/samsung/Makefile b/drivers/clk/samsung/Makefile
index 8eb4799237f0..2cb62f87e068 100644
--- a/drivers/clk/samsung/Makefile
+++ b/drivers/clk/samsung/Makefile
@@ -8,4 +8,8 @@ obj-$(CONFIG_SOC_EXYNOS5250) += clk-exynos5250.o
obj-$(CONFIG_SOC_EXYNOS5420) += clk-exynos5420.o
obj-$(CONFIG_SOC_EXYNOS5440) += clk-exynos5440.o
obj-$(CONFIG_ARCH_EXYNOS) += clk-exynos-audss.o
+obj-$(CONFIG_S3C2410_COMMON_CLK)+= clk-s3c2410.o
+obj-$(CONFIG_S3C2410_COMMON_DCLK)+= clk-s3c2410-dclk.o
+obj-$(CONFIG_S3C2412_COMMON_CLK)+= clk-s3c2412.o
+obj-$(CONFIG_S3C2443_COMMON_CLK)+= clk-s3c2443.o
obj-$(CONFIG_ARCH_S3C64XX) += clk-s3c64xx.o
diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 81e6d2f49aa0..7fb0a28e65d5 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -11,6 +11,7 @@
#include <linux/errno.h>
#include <linux/hrtimer.h>
+#include <linux/delay.h>
#include "clk.h"
#include "clk-pll.h"
@@ -59,6 +60,72 @@ static long samsung_pll_round_rate(struct clk_hw *hw,
}
/*
+ * PLL2126 Clock Type
+ */
+
+#define PLL2126_MDIV_MASK (0xff)
+#define PLL2126_PDIV_MASK (0x3f)
+#define PLL2126_SDIV_MASK (0x3)
+#define PLL2126_MDIV_SHIFT (16)
+#define PLL2126_PDIV_SHIFT (8)
+#define PLL2126_SDIV_SHIFT (0)
+
+static unsigned long samsung_pll2126_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u32 pll_con, mdiv, pdiv, sdiv;
+ u64 fvco = parent_rate;
+
+ pll_con = __raw_readl(pll->con_reg);
+ mdiv = (pll_con >> PLL2126_MDIV_SHIFT) & PLL2126_MDIV_MASK;
+ pdiv = (pll_con >> PLL2126_PDIV_SHIFT) & PLL2126_PDIV_MASK;
+ sdiv = (pll_con >> PLL2126_SDIV_SHIFT) & PLL2126_SDIV_MASK;
+
+ fvco *= (mdiv + 8);
+ do_div(fvco, (pdiv + 2) << sdiv);
+
+ return (unsigned long)fvco;
+}
+
+static const struct clk_ops samsung_pll2126_clk_ops = {
+ .recalc_rate = samsung_pll2126_recalc_rate,
+};
+
+/*
+ * PLL3000 Clock Type
+ */
+
+#define PLL3000_MDIV_MASK (0xff)
+#define PLL3000_PDIV_MASK (0x3)
+#define PLL3000_SDIV_MASK (0x3)
+#define PLL3000_MDIV_SHIFT (16)
+#define PLL3000_PDIV_SHIFT (8)
+#define PLL3000_SDIV_SHIFT (0)
+
+static unsigned long samsung_pll3000_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u32 pll_con, mdiv, pdiv, sdiv;
+ u64 fvco = parent_rate;
+
+ pll_con = __raw_readl(pll->con_reg);
+ mdiv = (pll_con >> PLL3000_MDIV_SHIFT) & PLL3000_MDIV_MASK;
+ pdiv = (pll_con >> PLL3000_PDIV_SHIFT) & PLL3000_PDIV_MASK;
+ sdiv = (pll_con >> PLL3000_SDIV_SHIFT) & PLL3000_SDIV_MASK;
+
+ fvco *= (2 * (mdiv + 8));
+ do_div(fvco, pdiv << sdiv);
+
+ return (unsigned long)fvco;
+}
+
+static const struct clk_ops samsung_pll3000_clk_ops = {
+ .recalc_rate = samsung_pll3000_recalc_rate,
+};
+
+/*
* PLL35xx Clock Type
*/
/* Maximum lock time can be 270 * PDIV cycles */
@@ -564,7 +631,9 @@ static const struct clk_ops samsung_pll46xx_clk_min_ops = {
#define PLL6552_PDIV_MASK 0x3f
#define PLL6552_SDIV_MASK 0x7
#define PLL6552_MDIV_SHIFT 16
+#define PLL6552_MDIV_SHIFT_2416 14
#define PLL6552_PDIV_SHIFT 8
+#define PLL6552_PDIV_SHIFT_2416 5
#define PLL6552_SDIV_SHIFT 0
static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw,
@@ -575,8 +644,13 @@ static unsigned long samsung_pll6552_recalc_rate(struct clk_hw *hw,
u64 fvco = parent_rate;
pll_con = __raw_readl(pll->con_reg);
- mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK;
- pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK;
+ if (pll->type == pll_6552_s3c2416) {
+ mdiv = (pll_con >> PLL6552_MDIV_SHIFT_2416) & PLL6552_MDIV_MASK;
+ pdiv = (pll_con >> PLL6552_PDIV_SHIFT_2416) & PLL6552_PDIV_MASK;
+ } else {
+ mdiv = (pll_con >> PLL6552_MDIV_SHIFT) & PLL6552_MDIV_MASK;
+ pdiv = (pll_con >> PLL6552_PDIV_SHIFT) & PLL6552_PDIV_MASK;
+ }
sdiv = (pll_con >> PLL6552_SDIV_SHIFT) & PLL6552_SDIV_MASK;
fvco *= mdiv;
@@ -628,6 +702,169 @@ static const struct clk_ops samsung_pll6553_clk_ops = {
};
/*
+ * PLL Clock Type of S3C24XX before S3C2443
+ */
+
+#define PLLS3C2410_MDIV_MASK (0xff)
+#define PLLS3C2410_PDIV_MASK (0x1f)
+#define PLLS3C2410_SDIV_MASK (0x3)
+#define PLLS3C2410_MDIV_SHIFT (12)
+#define PLLS3C2410_PDIV_SHIFT (4)
+#define PLLS3C2410_SDIV_SHIFT (0)
+
+#define PLLS3C2410_ENABLE_REG_OFFSET 0x10
+
+static unsigned long samsung_s3c2410_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u32 pll_con, mdiv, pdiv, sdiv;
+ u64 fvco = parent_rate;
+
+ pll_con = __raw_readl(pll->con_reg);
+ mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK;
+ pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK;
+ sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK;
+
+ fvco *= (mdiv + 8);
+ do_div(fvco, (pdiv + 2) << sdiv);
+
+ return (unsigned int)fvco;
+}
+
+static unsigned long samsung_s3c2440_mpll_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u32 pll_con, mdiv, pdiv, sdiv;
+ u64 fvco = parent_rate;
+
+ pll_con = __raw_readl(pll->con_reg);
+ mdiv = (pll_con >> PLLS3C2410_MDIV_SHIFT) & PLLS3C2410_MDIV_MASK;
+ pdiv = (pll_con >> PLLS3C2410_PDIV_SHIFT) & PLLS3C2410_PDIV_MASK;
+ sdiv = (pll_con >> PLLS3C2410_SDIV_SHIFT) & PLLS3C2410_SDIV_MASK;
+
+ fvco *= (2 * (mdiv + 8));
+ do_div(fvco, (pdiv + 2) << sdiv);
+
+ return (unsigned int)fvco;
+}
+
+static int samsung_s3c2410_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ const struct samsung_pll_rate_table *rate;
+ u32 tmp;
+
+ /* Get required rate settings from table */
+ rate = samsung_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, __clk_get_name(hw->clk));
+ return -EINVAL;
+ }
+
+ tmp = __raw_readl(pll->con_reg);
+
+ /* Change PLL PMS values */
+ tmp &= ~((PLLS3C2410_MDIV_MASK << PLLS3C2410_MDIV_SHIFT) |
+ (PLLS3C2410_PDIV_MASK << PLLS3C2410_PDIV_SHIFT) |
+ (PLLS3C2410_SDIV_MASK << PLLS3C2410_SDIV_SHIFT));
+ tmp |= (rate->mdiv << PLLS3C2410_MDIV_SHIFT) |
+ (rate->pdiv << PLLS3C2410_PDIV_SHIFT) |
+ (rate->sdiv << PLLS3C2410_SDIV_SHIFT);
+ __raw_writel(tmp, pll->con_reg);
+
+ /* Time to settle according to the manual */
+ udelay(300);
+
+ return 0;
+}
+
+static int samsung_s3c2410_pll_enable(struct clk_hw *hw, int bit, bool enable)
+{
+ struct samsung_clk_pll *pll = to_clk_pll(hw);
+ u32 pll_en = __raw_readl(pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET);
+ u32 pll_en_orig = pll_en;
+
+ if (enable)
+ pll_en &= ~BIT(bit);
+ else
+ pll_en |= BIT(bit);
+
+ __raw_writel(pll_en, pll->lock_reg + PLLS3C2410_ENABLE_REG_OFFSET);
+
+ /* if we started the UPLL, then allow to settle */
+ if (enable && (pll_en_orig & BIT(bit)))
+ udelay(300);
+
+ return 0;
+}
+
+static int samsung_s3c2410_mpll_enable(struct clk_hw *hw)
+{
+ return samsung_s3c2410_pll_enable(hw, 5, true);
+}
+
+static void samsung_s3c2410_mpll_disable(struct clk_hw *hw)
+{
+ samsung_s3c2410_pll_enable(hw, 5, false);
+}
+
+static int samsung_s3c2410_upll_enable(struct clk_hw *hw)
+{
+ return samsung_s3c2410_pll_enable(hw, 7, true);
+}
+
+static void samsung_s3c2410_upll_disable(struct clk_hw *hw)
+{
+ samsung_s3c2410_pll_enable(hw, 7, false);
+}
+
+static const struct clk_ops samsung_s3c2410_mpll_clk_min_ops = {
+ .recalc_rate = samsung_s3c2410_pll_recalc_rate,
+ .enable = samsung_s3c2410_mpll_enable,
+ .disable = samsung_s3c2410_mpll_disable,
+};
+
+static const struct clk_ops samsung_s3c2410_upll_clk_min_ops = {
+ .recalc_rate = samsung_s3c2410_pll_recalc_rate,
+ .enable = samsung_s3c2410_upll_enable,
+ .disable = samsung_s3c2410_upll_disable,
+};
+
+static const struct clk_ops samsung_s3c2440_mpll_clk_min_ops = {
+ .recalc_rate = samsung_s3c2440_mpll_recalc_rate,
+ .enable = samsung_s3c2410_mpll_enable,
+ .disable = samsung_s3c2410_mpll_disable,
+};
+
+static const struct clk_ops samsung_s3c2410_mpll_clk_ops = {
+ .recalc_rate = samsung_s3c2410_pll_recalc_rate,
+ .enable = samsung_s3c2410_mpll_enable,
+ .disable = samsung_s3c2410_mpll_disable,
+ .round_rate = samsung_pll_round_rate,
+ .set_rate = samsung_s3c2410_pll_set_rate,
+};
+
+static const struct clk_ops samsung_s3c2410_upll_clk_ops = {
+ .recalc_rate = samsung_s3c2410_pll_recalc_rate,
+ .enable = samsung_s3c2410_upll_enable,
+ .disable = samsung_s3c2410_upll_disable,
+ .round_rate = samsung_pll_round_rate,
+ .set_rate = samsung_s3c2410_pll_set_rate,
+};
+
+static const struct clk_ops samsung_s3c2440_mpll_clk_ops = {
+ .recalc_rate = samsung_s3c2440_mpll_recalc_rate,
+ .enable = samsung_s3c2410_mpll_enable,
+ .disable = samsung_s3c2410_mpll_disable,
+ .round_rate = samsung_pll_round_rate,
+ .set_rate = samsung_s3c2410_pll_set_rate,
+};
+
+/*
* PLL2550x Clock Type
*/
@@ -746,6 +983,12 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk,
}
switch (pll_clk->type) {
+ case pll_2126:
+ init.ops = &samsung_pll2126_clk_ops;
+ break;
+ case pll_3000:
+ init.ops = &samsung_pll3000_clk_ops;
+ break;
/* clk_ops for 35xx and 2550 are similar */
case pll_35xx:
case pll_2550:
@@ -773,6 +1016,7 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk,
init.ops = &samsung_pll36xx_clk_ops;
break;
case pll_6552:
+ case pll_6552_s3c2416:
init.ops = &samsung_pll6552_clk_ops;
break;
case pll_6553:
@@ -786,6 +1030,24 @@ static void __init _samsung_clk_register_pll(struct samsung_pll_clock *pll_clk,
else
init.ops = &samsung_pll46xx_clk_ops;
break;
+ case pll_s3c2410_mpll:
+ if (!pll->rate_table)
+ init.ops = &samsung_s3c2410_mpll_clk_min_ops;
+ else
+ init.ops = &samsung_s3c2410_mpll_clk_ops;
+ break;
+ case pll_s3c2410_upll:
+ if (!pll->rate_table)
+ init.ops = &samsung_s3c2410_upll_clk_min_ops;
+ else
+ init.ops = &samsung_s3c2410_upll_clk_ops;
+ break;
+ case pll_s3c2440_mpll:
+ if (!pll->rate_table)
+ init.ops = &samsung_s3c2440_mpll_clk_min_ops;
+ else
+ init.ops = &samsung_s3c2440_mpll_clk_ops;
+ break;
default:
pr_warn("%s: Unknown pll type for pll clk %s\n",
__func__, pll_clk->name);
diff --git a/drivers/clk/samsung/clk-pll.h b/drivers/clk/samsung/clk-pll.h
index 6c39030080fb..6428bcc6df6f 100644
--- a/drivers/clk/samsung/clk-pll.h
+++ b/drivers/clk/samsung/clk-pll.h
@@ -13,6 +13,8 @@
#define __SAMSUNG_CLK_PLL_H
enum samsung_pll_type {
+ pll_2126,
+ pll_3000,
pll_35xx,
pll_36xx,
pll_2550,
@@ -24,7 +26,11 @@ enum samsung_pll_type {
pll_4650,
pll_4650c,
pll_6552,
+ pll_6552_s3c2416,
pll_6553,
+ pll_s3c2410_mpll,
+ pll_s3c2410_upll,
+ pll_s3c2440_mpll,
};
#define PLL_35XX_RATE(_rate, _m, _p, _s) \
diff --git a/drivers/clk/samsung/clk-s3c2410-dclk.c b/drivers/clk/samsung/clk-s3c2410-dclk.c
new file mode 100644
index 000000000000..0449cc0458ed
--- /dev/null
+++ b/drivers/clk/samsung/clk-s3c2410-dclk.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
+ *
+ * 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.
+ *
+ * Common Clock Framework support for s3c24xx external clock output.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include "clk.h"
+
+/* legacy access to misccr, until dt conversion is finished */
+#include <mach/hardware.h>
+#include <mach/regs-gpio.h>
+
+#define MUX_DCLK0 0
+#define MUX_DCLK1 1
+#define DIV_DCLK0 2
+#define DIV_DCLK1 3
+#define GATE_DCLK0 4
+#define GATE_DCLK1 5
+#define MUX_CLKOUT0 6
+#define MUX_CLKOUT1 7
+#define DCLK_MAX_CLKS (MUX_CLKOUT1 + 1)
+
+enum supported_socs {
+ S3C2410,
+ S3C2412,
+ S3C2440,
+ S3C2443,
+};
+
+struct s3c24xx_dclk_drv_data {
+ const char **clkout0_parent_names;
+ int clkout0_num_parents;
+ const char **clkout1_parent_names;
+ int clkout1_num_parents;
+ const char **mux_parent_names;
+ int mux_num_parents;
+};
+
+/*
+ * Clock for output-parent selection in misccr
+ */
+
+struct s3c24xx_clkout {
+ struct clk_hw hw;
+ u32 mask;
+ u8 shift;
+};
+
+#define to_s3c24xx_clkout(_hw) container_of(_hw, struct s3c24xx_clkout, hw)
+
+static u8 s3c24xx_clkout_get_parent(struct clk_hw *hw)
+{
+ struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw);
+ int num_parents = __clk_get_num_parents(hw->clk);
+ u32 val;
+
+ val = readl_relaxed(S3C24XX_MISCCR) >> clkout->shift;
+ val >>= clkout->shift;
+ val &= clkout->mask;
+
+ if (val >= num_parents)
+ return -EINVAL;
+
+ return val;
+}
+
+static int s3c24xx_clkout_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct s3c24xx_clkout *clkout = to_s3c24xx_clkout(hw);
+ int ret = 0;
+
+ s3c2410_modify_misccr((clkout->mask << clkout->shift),
+ (index << clkout->shift));
+
+ return ret;
+}
+
+const struct clk_ops s3c24xx_clkout_ops = {
+ .get_parent = s3c24xx_clkout_get_parent,
+ .set_parent = s3c24xx_clkout_set_parent,
+ .determine_rate = __clk_mux_determine_rate,
+};
+
+struct clk *s3c24xx_register_clkout(struct device *dev, const char *name,
+ const char **parent_names, u8 num_parents,
+ u8 shift, u32 mask)
+{
+ struct s3c24xx_clkout *clkout;
+ struct clk *clk;
+ struct clk_init_data init;
+
+ /* allocate the clkout */
+ clkout = kzalloc(sizeof(*clkout), GFP_KERNEL);
+ if (!clkout)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &s3c24xx_clkout_ops;
+ init.flags = CLK_IS_BASIC;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+
+ clkout->shift = shift;
+ clkout->mask = mask;
+ clkout->hw.init = &init;
+
+ clk = clk_register(dev, &clkout->hw);
+
+ return clk;
+}
+
+/*
+ * dclk and clkout init
+ */
+
+struct s3c24xx_dclk {
+ struct device *dev;
+ void __iomem *base;
+ struct clk_onecell_data clk_data;
+ struct notifier_block dclk0_div_change_nb;
+ struct notifier_block dclk1_div_change_nb;
+ spinlock_t dclk_lock;
+ unsigned long reg_save;
+};
+
+#define to_s3c24xx_dclk0(x) \
+ container_of(x, struct s3c24xx_dclk, dclk0_div_change_nb)
+
+#define to_s3c24xx_dclk1(x) \
+ container_of(x, struct s3c24xx_dclk, dclk1_div_change_nb)
+
+static const char *dclk_s3c2410_p[] = { "pclk", "uclk" };
+static const char *clkout0_s3c2410_p[] = { "mpll", "upll", "fclk", "hclk", "pclk",
+ "gate_dclk0" };
+static const char *clkout1_s3c2410_p[] = { "mpll", "upll", "fclk", "hclk", "pclk",
+ "gate_dclk1" };
+
+static const char *clkout0_s3c2412_p[] = { "mpll", "upll", "rtc_clkout",
+ "hclk", "pclk", "gate_dclk0" };
+static const char *clkout1_s3c2412_p[] = { "xti", "upll", "fclk", "hclk", "pclk",
+ "gate_dclk1" };
+
+static const char *clkout0_s3c2440_p[] = { "xti", "upll", "fclk", "hclk", "pclk",
+ "gate_dclk0" };
+static const char *clkout1_s3c2440_p[] = { "mpll", "upll", "rtc_clkout",
+ "hclk", "pclk", "gate_dclk1" };
+
+static const char *dclk_s3c2443_p[] = { "pclk", "epll" };
+static const char *clkout0_s3c2443_p[] = { "xti", "epll", "armclk", "hclk", "pclk",
+ "gate_dclk0" };
+static const char *clkout1_s3c2443_p[] = { "dummy", "epll", "rtc_clkout",
+ "hclk", "pclk", "gate_dclk1" };
+
+#define DCLKCON_DCLK_DIV_MASK 0xf
+#define DCLKCON_DCLK0_DIV_SHIFT 4
+#define DCLKCON_DCLK0_CMP_SHIFT 8
+#define DCLKCON_DCLK1_DIV_SHIFT 20
+#define DCLKCON_DCLK1_CMP_SHIFT 24
+
+static void s3c24xx_dclk_update_cmp(struct s3c24xx_dclk *s3c24xx_dclk,
+ int div_shift, int cmp_shift)
+{
+ unsigned long flags = 0;
+ u32 dclk_con, div, cmp;
+
+ spin_lock_irqsave(&s3c24xx_dclk->dclk_lock, flags);
+
+ dclk_con = readl_relaxed(s3c24xx_dclk->base);
+
+ div = ((dclk_con >> div_shift) & DCLKCON_DCLK_DIV_MASK) + 1;
+ cmp = ((div + 1) / 2) - 1;
+
+ dclk_con &= ~(DCLKCON_DCLK_DIV_MASK << cmp_shift);
+ dclk_con |= (cmp << cmp_shift);
+
+ writel_relaxed(dclk_con, s3c24xx_dclk->base);
+
+ spin_unlock_irqrestore(&s3c24xx_dclk->dclk_lock, flags);
+}
+
+static int s3c24xx_dclk0_div_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk0(nb);
+
+ if (event == POST_RATE_CHANGE) {
+ s3c24xx_dclk_update_cmp(s3c24xx_dclk,
+ DCLKCON_DCLK0_DIV_SHIFT, DCLKCON_DCLK0_CMP_SHIFT);
+ }
+
+ return NOTIFY_DONE;
+}
+
+static int s3c24xx_dclk1_div_notify(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct s3c24xx_dclk *s3c24xx_dclk = to_s3c24xx_dclk1(nb);
+
+ if (event == POST_RATE_CHANGE) {
+ s3c24xx_dclk_update_cmp(s3c24xx_dclk,
+ DCLKCON_DCLK1_DIV_SHIFT, DCLKCON_DCLK1_CMP_SHIFT);
+ }
+
+ return NOTIFY_DONE;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int s3c24xx_dclk_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev);
+
+ s3c24xx_dclk->reg_save = readl_relaxed(s3c24xx_dclk->base);
+ return 0;
+}
+
+static int s3c24xx_dclk_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev);
+
+ writel_relaxed(s3c24xx_dclk->reg_save, s3c24xx_dclk->base);
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(s3c24xx_dclk_pm_ops,
+ s3c24xx_dclk_suspend, s3c24xx_dclk_resume);
+
+static int s3c24xx_dclk_probe(struct platform_device *pdev)
+{
+ struct s3c24xx_dclk *s3c24xx_dclk;
+ struct resource *mem;
+ struct clk **clk_table;
+ struct s3c24xx_dclk_drv_data *dclk_variant;
+ int ret, i;
+
+ s3c24xx_dclk = devm_kzalloc(&pdev->dev, sizeof(*s3c24xx_dclk),
+ GFP_KERNEL);
+ if (!s3c24xx_dclk)
+ return -ENOMEM;
+
+ s3c24xx_dclk->dev = &pdev->dev;
+ platform_set_drvdata(pdev, s3c24xx_dclk);
+ spin_lock_init(&s3c24xx_dclk->dclk_lock);
+
+ clk_table = devm_kzalloc(&pdev->dev,
+ sizeof(struct clk *) * DCLK_MAX_CLKS,
+ GFP_KERNEL);
+ if (!clk_table)
+ return -ENOMEM;
+
+ s3c24xx_dclk->clk_data.clks = clk_table;
+ s3c24xx_dclk->clk_data.clk_num = DCLK_MAX_CLKS;
+
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ s3c24xx_dclk->base = devm_ioremap_resource(&pdev->dev, mem);
+ if (IS_ERR(s3c24xx_dclk->base))
+ return PTR_ERR(s3c24xx_dclk->base);
+
+ dclk_variant = (struct s3c24xx_dclk_drv_data *)
+ platform_get_device_id(pdev)->driver_data;
+
+
+ clk_table[MUX_DCLK0] = clk_register_mux(&pdev->dev, "mux_dclk0",
+ dclk_variant->mux_parent_names,
+ dclk_variant->mux_num_parents, 0,
+ s3c24xx_dclk->base, 1, 1, 0,
+ &s3c24xx_dclk->dclk_lock);
+ clk_table[MUX_DCLK1] = clk_register_mux(&pdev->dev, "mux_dclk1",
+ dclk_variant->mux_parent_names,
+ dclk_variant->mux_num_parents, 0,
+ s3c24xx_dclk->base, 17, 1, 0,
+ &s3c24xx_dclk->dclk_lock);
+
+ clk_table[DIV_DCLK0] = clk_register_divider(&pdev->dev, "div_dclk0",
+ "mux_dclk0", 0, s3c24xx_dclk->base,
+ 4, 4, 0, &s3c24xx_dclk->dclk_lock);
+ clk_table[DIV_DCLK1] = clk_register_divider(&pdev->dev, "div_dclk1",
+ "mux_dclk1", 0, s3c24xx_dclk->base,
+ 20, 4, 0, &s3c24xx_dclk->dclk_lock);
+
+ clk_table[GATE_DCLK0] = clk_register_gate(&pdev->dev, "gate_dclk0",
+ "div_dclk0", CLK_SET_RATE_PARENT,
+ s3c24xx_dclk->base, 0, 0,
+ &s3c24xx_dclk->dclk_lock);
+ clk_table[GATE_DCLK1] = clk_register_gate(&pdev->dev, "gate_dclk1",
+ "div_dclk1", CLK_SET_RATE_PARENT,
+ s3c24xx_dclk->base, 16, 0,
+ &s3c24xx_dclk->dclk_lock);
+
+ clk_table[MUX_CLKOUT0] = s3c24xx_register_clkout(&pdev->dev,
+ "clkout0", dclk_variant->clkout0_parent_names,
+ dclk_variant->clkout0_num_parents, 4, 7);
+ clk_table[MUX_CLKOUT1] = s3c24xx_register_clkout(&pdev->dev,
+ "clkout1", dclk_variant->clkout1_parent_names,
+ dclk_variant->clkout1_num_parents, 8, 7);
+
+ for (i = 0; i < DCLK_MAX_CLKS; i++)
+ if (IS_ERR(clk_table[i])) {
+ dev_err(&pdev->dev, "clock %d failed to register\n", i);
+ ret = PTR_ERR(clk_table[i]);
+ goto err_clk_register;
+ }
+
+ ret = clk_register_clkdev(clk_table[MUX_DCLK0], "dclk0", NULL);
+ if (!ret)
+ ret = clk_register_clkdev(clk_table[MUX_DCLK1], "dclk1", NULL);
+ if (!ret)
+ ret = clk_register_clkdev(clk_table[MUX_CLKOUT0],
+ "clkout0", NULL);
+ if (!ret)
+ ret = clk_register_clkdev(clk_table[MUX_CLKOUT1],
+ "clkout1", NULL);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register aliases, %d\n", ret);
+ goto err_clk_register;
+ }
+
+ s3c24xx_dclk->dclk0_div_change_nb.notifier_call =
+ s3c24xx_dclk0_div_notify;
+
+ s3c24xx_dclk->dclk1_div_change_nb.notifier_call =
+ s3c24xx_dclk1_div_notify;
+
+ ret = clk_notifier_register(clk_table[DIV_DCLK0],
+ &s3c24xx_dclk->dclk0_div_change_nb);
+ if (ret)
+ goto err_clk_register;
+
+ ret = clk_notifier_register(clk_table[DIV_DCLK1],
+ &s3c24xx_dclk->dclk1_div_change_nb);
+ if (ret)
+ goto err_dclk_notify;
+
+ return 0;
+
+err_dclk_notify:
+ clk_notifier_unregister(clk_table[DIV_DCLK0],
+ &s3c24xx_dclk->dclk0_div_change_nb);
+err_clk_register:
+ for (i = 0; i < DCLK_MAX_CLKS; i++)
+ if (clk_table[i] && !IS_ERR(clk_table[i]))
+ clk_unregister(clk_table[i]);
+
+ return ret;
+}
+
+static int s3c24xx_dclk_remove(struct platform_device *pdev)
+{
+ struct s3c24xx_dclk *s3c24xx_dclk = platform_get_drvdata(pdev);
+ struct clk **clk_table = s3c24xx_dclk->clk_data.clks;
+ int i;
+
+ clk_notifier_unregister(clk_table[DIV_DCLK1],
+ &s3c24xx_dclk->dclk1_div_change_nb);
+ clk_notifier_unregister(clk_table[DIV_DCLK0],
+ &s3c24xx_dclk->dclk0_div_change_nb);
+
+ for (i = 0; i < DCLK_MAX_CLKS; i++)
+ clk_unregister(clk_table[i]);
+
+ return 0;
+}
+
+static struct s3c24xx_dclk_drv_data dclk_variants[] = {
+ [S3C2410] = {
+ .clkout0_parent_names = clkout0_s3c2410_p,
+ .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2410_p),
+ .clkout1_parent_names = clkout1_s3c2410_p,
+ .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2410_p),
+ .mux_parent_names = dclk_s3c2410_p,
+ .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p),
+ },
+ [S3C2412] = {
+ .clkout0_parent_names = clkout0_s3c2412_p,
+ .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2412_p),
+ .clkout1_parent_names = clkout1_s3c2412_p,
+ .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2412_p),
+ .mux_parent_names = dclk_s3c2410_p,
+ .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p),
+ },
+ [S3C2440] = {
+ .clkout0_parent_names = clkout0_s3c2440_p,
+ .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2440_p),
+ .clkout1_parent_names = clkout1_s3c2440_p,
+ .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2440_p),
+ .mux_parent_names = dclk_s3c2410_p,
+ .mux_num_parents = ARRAY_SIZE(dclk_s3c2410_p),
+ },
+ [S3C2443] = {
+ .clkout0_parent_names = clkout0_s3c2443_p,
+ .clkout0_num_parents = ARRAY_SIZE(clkout0_s3c2443_p),
+ .clkout1_parent_names = clkout1_s3c2443_p,
+ .clkout1_num_parents = ARRAY_SIZE(clkout1_s3c2443_p),
+ .mux_parent_names = dclk_s3c2443_p,
+ .mux_num_parents = ARRAY_SIZE(dclk_s3c2443_p),
+ },
+};
+
+static struct platform_device_id s3c24xx_dclk_driver_ids[] = {
+ {
+ .name = "s3c2410-dclk",
+ .driver_data = (kernel_ulong_t)&dclk_variants[S3C2410],
+ }, {
+ .name = "s3c2412-dclk",
+ .driver_data = (kernel_ulong_t)&dclk_variants[S3C2412],
+ }, {
+ .name = "s3c2440-dclk",
+ .driver_data = (kernel_ulong_t)&dclk_variants[S3C2440],
+ }, {
+ .name = "s3c2443-dclk",
+ .driver_data = (kernel_ulong_t)&dclk_variants[S3C2443],
+ },
+ { }
+};
+
+MODULE_DEVICE_TABLE(platform, s3c24xx_dclk_driver_ids);
+
+static struct platform_driver s3c24xx_dclk_driver = {
+ .driver = {
+ .name = "s3c24xx-dclk",
+ .owner = THIS_MODULE,
+ .pm = &s3c24xx_dclk_pm_ops,
+ },
+ .probe = s3c24xx_dclk_probe,
+ .remove = s3c24xx_dclk_remove,
+ .id_table = s3c24xx_dclk_driver_ids,
+};
+module_platform_driver(s3c24xx_dclk_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");
+MODULE_DESCRIPTION("Driver for the S3C24XX external clock outputs");
diff --git a/drivers/clk/samsung/clk-s3c2410.c b/drivers/clk/samsung/clk-s3c2410.c
new file mode 100644
index 000000000000..7b4182186a30
--- /dev/null
+++ b/drivers/clk/samsung/clk-s3c2410.c
@@ -0,0 +1,477 @@
+/*
+ * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
+ *
+ * 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.
+ *
+ * Common Clock Framework support for S3C2410 and following SoCs.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+
+#include <dt-bindings/clock/s3c2410.h>
+
+#include "clk.h"
+#include "clk-pll.h"
+
+#define LOCKTIME 0x00
+#define MPLLCON 0x04
+#define UPLLCON 0x08
+#define CLKCON 0x0c
+#define CLKSLOW 0x10
+#define CLKDIVN 0x14
+#define CAMDIVN 0x18
+
+/* the soc types */
+enum supported_socs {
+ S3C2410,
+ S3C2440,
+ S3C2442,
+};
+
+/* list of PLLs to be registered */
+enum s3c2410_plls {
+ mpll, upll,
+};
+
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *s3c2410_save;
+
+/*
+ * list of controller registers to be saved and restored during a
+ * suspend/resume cycle.
+ */
+static unsigned long s3c2410_clk_regs[] __initdata = {
+ LOCKTIME,
+ MPLLCON,
+ UPLLCON,
+ CLKCON,
+ CLKSLOW,
+ CLKDIVN,
+ CAMDIVN,
+};
+
+static int s3c2410_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, s3c2410_save,
+ ARRAY_SIZE(s3c2410_clk_regs));
+
+ return 0;
+}
+
+static void s3c2410_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, s3c2410_save,
+ ARRAY_SIZE(s3c2410_clk_regs));
+}
+
+static struct syscore_ops s3c2410_clk_syscore_ops = {
+ .suspend = s3c2410_clk_suspend,
+ .resume = s3c2410_clk_resume,
+};
+
+static void s3c2410_clk_sleep_init(void)
+{
+ s3c2410_save = samsung_clk_alloc_reg_dump(s3c2410_clk_regs,
+ ARRAY_SIZE(s3c2410_clk_regs));
+ if (!s3c2410_save) {
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+ return;
+ }
+
+ register_syscore_ops(&s3c2410_clk_syscore_ops);
+ return;
+}
+#else
+static void s3c2410_clk_sleep_init(void) {}
+#endif
+
+PNAME(fclk_p) = { "mpll", "div_slow" };
+
+struct samsung_mux_clock s3c2410_common_muxes[] __initdata = {
+ MUX(FCLK, "fclk", fclk_p, CLKSLOW, 4, 1),
+};
+
+static struct clk_div_table divslow_d[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 6 },
+ { .val = 4, .div = 8 },
+ { .val = 5, .div = 10 },
+ { .val = 6, .div = 12 },
+ { .val = 7, .div = 14 },
+ { /* sentinel */ },
+};
+
+struct samsung_div_clock s3c2410_common_dividers[] __initdata = {
+ DIV_T(0, "div_slow", "xti", CLKSLOW, 0, 3, divslow_d),
+ DIV(PCLK, "pclk", "hclk", CLKDIVN, 0, 1),
+};
+
+struct samsung_gate_clock s3c2410_common_gates[] __initdata = {
+ GATE(PCLK_SPI, "spi", "pclk", CLKCON, 18, 0, 0),
+ GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 17, 0, 0),
+ GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 16, 0, 0),
+ GATE(PCLK_ADC, "adc", "pclk", CLKCON, 15, 0, 0),
+ GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 14, 0, 0),
+ GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 13, CLK_IGNORE_UNUSED, 0),
+ GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 12, 0, 0),
+ GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 11, 0, 0),
+ GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 10, 0, 0),
+ GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 9, 0, 0),
+ GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 8, 0, 0),
+ GATE(HCLK_USBD, "usb-device", "hclk", CLKCON, 7, 0, 0),
+ GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0),
+ GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0),
+ GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0),
+};
+
+/* should be added _after_ the soc-specific clocks are created */
+struct samsung_clock_alias s3c2410_common_aliases[] __initdata = {
+ ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"),
+ ALIAS(PCLK_ADC, NULL, "adc"),
+ ALIAS(PCLK_RTC, NULL, "rtc"),
+ ALIAS(PCLK_PWM, NULL, "timers"),
+ ALIAS(HCLK_LCD, NULL, "lcd"),
+ ALIAS(HCLK_USBD, NULL, "usb-device"),
+ ALIAS(HCLK_USBH, NULL, "usb-host"),
+ ALIAS(UCLK, NULL, "usb-bus-host"),
+ ALIAS(UCLK, NULL, "usb-bus-gadget"),
+ ALIAS(ARMCLK, NULL, "armclk"),
+ ALIAS(UCLK, NULL, "uclk"),
+ ALIAS(HCLK, NULL, "hclk"),
+ ALIAS(MPLL, NULL, "mpll"),
+ ALIAS(FCLK, NULL, "fclk"),
+};
+
+/* S3C2410 specific clocks */
+
+static struct samsung_pll_rate_table pll_s3c2410_12mhz_tbl[] __initdata = {
+ /* sorted in descending order */
+ /* 2410A extras */
+ PLL_35XX_RATE(270000000, 127, 1, 1),
+ PLL_35XX_RATE(268000000, 126, 1, 1),
+ PLL_35XX_RATE(266000000, 125, 1, 1),
+ PLL_35XX_RATE(226000000, 105, 1, 1),
+ PLL_35XX_RATE(210000000, 132, 2, 1),
+ /* 2410 common */
+ PLL_35XX_RATE(203000000, 161, 3, 1),
+ PLL_35XX_RATE(192000000, 88, 1, 1),
+ PLL_35XX_RATE(186000000, 85, 1, 1),
+ PLL_35XX_RATE(180000000, 82, 1, 1),
+ PLL_35XX_RATE(170000000, 77, 1, 1),
+ PLL_35XX_RATE(158000000, 71, 1, 1),
+ PLL_35XX_RATE(152000000, 68, 1, 1),
+ PLL_35XX_RATE(147000000, 90, 2, 1),
+ PLL_35XX_RATE(135000000, 82, 2, 1),
+ PLL_35XX_RATE(124000000, 116, 1, 2),
+ PLL_35XX_RATE(118000000, 150, 2, 2),
+ PLL_35XX_RATE(113000000, 105, 1, 2),
+ PLL_35XX_RATE(101000000, 127, 2, 2),
+ PLL_35XX_RATE(90000000, 112, 2, 2),
+ PLL_35XX_RATE(85000000, 105, 2, 2),
+ PLL_35XX_RATE(79000000, 71, 1, 2),
+ PLL_35XX_RATE(68000000, 82, 2, 2),
+ PLL_35XX_RATE(56000000, 142, 2, 3),
+ PLL_35XX_RATE(48000000, 120, 2, 3),
+ PLL_35XX_RATE(51000000, 161, 3, 3),
+ PLL_35XX_RATE(45000000, 82, 1, 3),
+ PLL_35XX_RATE(34000000, 82, 2, 3),
+ { /* sentinel */ },
+};
+
+static struct samsung_pll_clock s3c2410_plls[] __initdata = {
+ [mpll] = PLL(pll_s3c2410_mpll, MPLL, "mpll", "xti",
+ LOCKTIME, MPLLCON, NULL),
+ [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti",
+ LOCKTIME, UPLLCON, NULL),
+};
+
+struct samsung_div_clock s3c2410_dividers[] __initdata = {
+ DIV(HCLK, "hclk", "mpll", CLKDIVN, 1, 1),
+};
+
+struct samsung_fixed_factor_clock s3c2410_ffactor[] __initdata = {
+ /*
+ * armclk is directly supplied by the fclk, without
+ * switching possibility like on the s3c244x below.
+ */
+ FFACTOR(ARMCLK, "armclk", "fclk", 1, 1, 0),
+
+ /* uclk is fed from the unmodified upll */
+ FFACTOR(UCLK, "uclk", "upll", 1, 1, 0),
+};
+
+struct samsung_clock_alias s3c2410_aliases[] __initdata = {
+ ALIAS(PCLK_UART0, "s3c2410-uart.0", "uart"),
+ ALIAS(PCLK_UART1, "s3c2410-uart.1", "uart"),
+ ALIAS(PCLK_UART2, "s3c2410-uart.2", "uart"),
+ ALIAS(PCLK_UART0, "s3c2410-uart.0", "clk_uart_baud0"),
+ ALIAS(PCLK_UART1, "s3c2410-uart.1", "clk_uart_baud0"),
+ ALIAS(PCLK_UART2, "s3c2410-uart.2", "clk_uart_baud0"),
+ ALIAS(UCLK, NULL, "clk_uart_baud1"),
+};
+
+/* S3C244x specific clocks */
+
+static struct samsung_pll_rate_table pll_s3c244x_12mhz_tbl[] __initdata = {
+ /* sorted in descending order */
+ PLL_35XX_RATE(400000000, 0x5c, 1, 1),
+ PLL_35XX_RATE(390000000, 0x7a, 2, 1),
+ PLL_35XX_RATE(380000000, 0x57, 1, 1),
+ PLL_35XX_RATE(370000000, 0xb1, 4, 1),
+ PLL_35XX_RATE(360000000, 0x70, 2, 1),
+ PLL_35XX_RATE(350000000, 0xa7, 4, 1),
+ PLL_35XX_RATE(340000000, 0x4d, 1, 1),
+ PLL_35XX_RATE(330000000, 0x66, 2, 1),
+ PLL_35XX_RATE(320000000, 0x98, 4, 1),
+ PLL_35XX_RATE(310000000, 0x93, 4, 1),
+ PLL_35XX_RATE(300000000, 0x75, 3, 1),
+ PLL_35XX_RATE(240000000, 0x70, 1, 2),
+ PLL_35XX_RATE(230000000, 0x6b, 1, 2),
+ PLL_35XX_RATE(220000000, 0x66, 1, 2),
+ PLL_35XX_RATE(210000000, 0x84, 2, 2),
+ PLL_35XX_RATE(200000000, 0x5c, 1, 2),
+ PLL_35XX_RATE(190000000, 0x57, 1, 2),
+ PLL_35XX_RATE(180000000, 0x70, 2, 2),
+ PLL_35XX_RATE(170000000, 0x4d, 1, 2),
+ PLL_35XX_RATE(160000000, 0x98, 4, 2),
+ PLL_35XX_RATE(150000000, 0x75, 3, 2),
+ PLL_35XX_RATE(120000000, 0x70, 1, 3),
+ PLL_35XX_RATE(110000000, 0x66, 1, 3),
+ PLL_35XX_RATE(100000000, 0x5c, 1, 3),
+ PLL_35XX_RATE(90000000, 0x70, 2, 3),
+ PLL_35XX_RATE(80000000, 0x98, 4, 3),
+ PLL_35XX_RATE(75000000, 0x75, 3, 3),
+ { /* sentinel */ },
+};
+
+static struct samsung_pll_clock s3c244x_common_plls[] __initdata = {
+ [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti",
+ LOCKTIME, MPLLCON, NULL),
+ [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "xti",
+ LOCKTIME, UPLLCON, NULL),
+};
+
+PNAME(hclk_p) = { "fclk", "div_hclk_2", "div_hclk_4", "div_hclk_3" };
+PNAME(armclk_p) = { "fclk", "hclk" };
+
+struct samsung_mux_clock s3c244x_common_muxes[] __initdata = {
+ MUX(HCLK, "hclk", hclk_p, CLKDIVN, 1, 2),
+ MUX(ARMCLK, "armclk", armclk_p, CAMDIVN, 12, 1),
+};
+
+struct samsung_fixed_factor_clock s3c244x_common_ffactor[] __initdata = {
+ FFACTOR(0, "div_hclk_2", "fclk", 1, 2, 0),
+ FFACTOR(0, "ff_cam", "div_cam", 2, 1, CLK_SET_RATE_PARENT),
+};
+
+static struct clk_div_table div_hclk_4_d[] = {
+ { .val = 0, .div = 4 },
+ { .val = 1, .div = 8 },
+ { /* sentinel */ },
+};
+
+static struct clk_div_table div_hclk_3_d[] = {
+ { .val = 0, .div = 3 },
+ { .val = 1, .div = 6 },
+ { /* sentinel */ },
+};
+
+struct samsung_div_clock s3c244x_common_dividers[] __initdata = {
+ DIV(UCLK, "uclk", "upll", CLKDIVN, 3, 1),
+ DIV(0, "div_hclk", "fclk", CLKDIVN, 1, 1),
+ DIV_T(0, "div_hclk_4", "fclk", CAMDIVN, 9, 1, div_hclk_4_d),
+ DIV_T(0, "div_hclk_3", "fclk", CAMDIVN, 8, 1, div_hclk_3_d),
+ DIV(0, "div_cam", "upll", CAMDIVN, 0, 3),
+};
+
+struct samsung_gate_clock s3c244x_common_gates[] __initdata = {
+ GATE(HCLK_CAM, "cam", "hclk", CLKCON, 19, 0, 0),
+};
+
+struct samsung_clock_alias s3c244x_common_aliases[] __initdata = {
+ ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"),
+ ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"),
+ ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"),
+ ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"),
+ ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"),
+ ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"),
+ ALIAS(HCLK_CAM, NULL, "camif"),
+ ALIAS(CAMIF, NULL, "camif-upll"),
+};
+
+/* S3C2440 specific clocks */
+
+PNAME(s3c2440_camif_p) = { "upll", "ff_cam" };
+
+struct samsung_mux_clock s3c2440_muxes[] __initdata = {
+ MUX(CAMIF, "camif", s3c2440_camif_p, CAMDIVN, 4, 1),
+};
+
+struct samsung_gate_clock s3c2440_gates[] __initdata = {
+ GATE(PCLK_AC97, "ac97", "pclk", CLKCON, 20, 0, 0),
+};
+
+/* S3C2442 specific clocks */
+
+struct samsung_fixed_factor_clock s3c2442_ffactor[] __initdata = {
+ FFACTOR(0, "upll_3", "upll", 1, 3, 0),
+};
+
+PNAME(s3c2442_camif_p) = { "upll", "ff_cam", "upll", "upll_3" };
+
+struct samsung_mux_clock s3c2442_muxes[] __initdata = {
+ MUX(CAMIF, "camif", s3c2442_camif_p, CAMDIVN, 4, 2),
+};
+
+/*
+ * fixed rate clocks generated outside the soc
+ * Only necessary until the devicetree-move is complete
+ */
+#define XTI 1
+struct samsung_fixed_rate_clock s3c2410_common_frate_clks[] __initdata = {
+ FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0),
+};
+
+static void __init s3c2410_common_clk_register_fixed_ext(unsigned long xti_f)
+{
+ struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal");
+
+ s3c2410_common_frate_clks[0].fixed_rate = xti_f;
+ samsung_clk_register_fixed_rate(s3c2410_common_frate_clks,
+ ARRAY_SIZE(s3c2410_common_frate_clks));
+
+ samsung_clk_register_alias(&xti_alias, 1);
+}
+
+void __init s3c2410_common_clk_init(struct device_node *np, unsigned long xti_f,
+ int current_soc,
+ void __iomem *base)
+{
+ reg_base = base;
+
+ if (np) {
+ reg_base = of_iomap(np, 0);
+ if (!reg_base)
+ panic("%s: failed to map registers\n", __func__);
+ }
+
+ samsung_clk_init(np, reg_base, NR_CLKS);
+
+ /* Register external clocks only in non-dt cases */
+ if (!np)
+ s3c2410_common_clk_register_fixed_ext(xti_f);
+
+ if (current_soc == 2410) {
+ if (_get_rate("xti") == 12 * MHZ) {
+ s3c2410_plls[mpll].rate_table = pll_s3c2410_12mhz_tbl;
+ s3c2410_plls[upll].rate_table = pll_s3c2410_12mhz_tbl;
+ }
+
+ /* Register PLLs. */
+ samsung_clk_register_pll(s3c2410_plls,
+ ARRAY_SIZE(s3c2410_plls), reg_base);
+
+ } else { /* S3C2440, S3C2442 */
+ if (_get_rate("xti") == 12 * MHZ) {
+ /*
+ * plls follow different calculation schemes, with the
+ * upll following the same scheme as the s3c2410 plls
+ */
+ s3c244x_common_plls[mpll].rate_table =
+ pll_s3c244x_12mhz_tbl;
+ s3c244x_common_plls[upll].rate_table =
+ pll_s3c2410_12mhz_tbl;
+ }
+
+ /* Register PLLs. */
+ samsung_clk_register_pll(s3c244x_common_plls,
+ ARRAY_SIZE(s3c244x_common_plls), reg_base);
+ }
+
+ /* Register common internal clocks. */
+ samsung_clk_register_mux(s3c2410_common_muxes,
+ ARRAY_SIZE(s3c2410_common_muxes));
+ samsung_clk_register_div(s3c2410_common_dividers,
+ ARRAY_SIZE(s3c2410_common_dividers));
+ samsung_clk_register_gate(s3c2410_common_gates,
+ ARRAY_SIZE(s3c2410_common_gates));
+
+ if (current_soc == S3C2440 || current_soc == S3C2442) {
+ samsung_clk_register_div(s3c244x_common_dividers,
+ ARRAY_SIZE(s3c244x_common_dividers));
+ samsung_clk_register_gate(s3c244x_common_gates,
+ ARRAY_SIZE(s3c244x_common_gates));
+ samsung_clk_register_mux(s3c244x_common_muxes,
+ ARRAY_SIZE(s3c244x_common_muxes));
+ samsung_clk_register_fixed_factor(s3c244x_common_ffactor,
+ ARRAY_SIZE(s3c244x_common_ffactor));
+ }
+
+ /* Register SoC-specific clocks. */
+ switch (current_soc) {
+ case S3C2410:
+ samsung_clk_register_div(s3c2410_dividers,
+ ARRAY_SIZE(s3c2410_dividers));
+ samsung_clk_register_fixed_factor(s3c2410_ffactor,
+ ARRAY_SIZE(s3c2410_ffactor));
+ samsung_clk_register_alias(s3c2410_aliases,
+ ARRAY_SIZE(s3c2410_common_aliases));
+ break;
+ case S3C2440:
+ samsung_clk_register_mux(s3c2440_muxes,
+ ARRAY_SIZE(s3c2440_muxes));
+ samsung_clk_register_gate(s3c2440_gates,
+ ARRAY_SIZE(s3c2440_gates));
+ break;
+ case S3C2442:
+ samsung_clk_register_mux(s3c2442_muxes,
+ ARRAY_SIZE(s3c2442_muxes));
+ samsung_clk_register_fixed_factor(s3c2442_ffactor,
+ ARRAY_SIZE(s3c2442_ffactor));
+ break;
+ }
+
+ /*
+ * Register common aliases at the end, as some of the aliased clocks
+ * are SoC specific.
+ */
+ samsung_clk_register_alias(s3c2410_common_aliases,
+ ARRAY_SIZE(s3c2410_common_aliases));
+
+ if (current_soc == S3C2440 || current_soc == S3C2442) {
+ samsung_clk_register_alias(s3c244x_common_aliases,
+ ARRAY_SIZE(s3c244x_common_aliases));
+ }
+
+ s3c2410_clk_sleep_init();
+}
+
+static void __init s3c2410_clk_init(struct device_node *np)
+{
+ s3c2410_common_clk_init(np, 0, S3C2410, 0);
+}
+CLK_OF_DECLARE(s3c2410_clk, "samsung,s3c2410-clock", s3c2410_clk_init);
+
+static void __init s3c2440_clk_init(struct device_node *np)
+{
+ s3c2410_common_clk_init(np, 0, S3C2440, 0);
+}
+CLK_OF_DECLARE(s3c2440_clk, "samsung,s3c2440-clock", s3c2440_clk_init);
+
+static void __init s3c2442_clk_init(struct device_node *np)
+{
+ s3c2410_common_clk_init(np, 0, S3C2442, 0);
+}
+CLK_OF_DECLARE(s3c2442_clk, "samsung,s3c2442-clock", s3c2442_clk_init);
diff --git a/drivers/clk/samsung/clk-s3c2412.c b/drivers/clk/samsung/clk-s3c2412.c
new file mode 100644
index 000000000000..0f11a07d5c01
--- /dev/null
+++ b/drivers/clk/samsung/clk-s3c2412.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
+ *
+ * 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.
+ *
+ * Common Clock Framework support for S3C2412 and S3C2413.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+
+#include <dt-bindings/clock/s3c2412.h>
+
+#include "clk.h"
+#include "clk-pll.h"
+
+#define LOCKTIME 0x00
+#define MPLLCON 0x04
+#define UPLLCON 0x08
+#define CLKCON 0x0c
+#define CLKDIVN 0x14
+#define CLKSRC 0x1c
+
+/* list of PLLs to be registered */
+enum s3c2412_plls {
+ mpll, upll,
+};
+
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *s3c2412_save;
+
+/*
+ * list of controller registers to be saved and restored during a
+ * suspend/resume cycle.
+ */
+static unsigned long s3c2412_clk_regs[] __initdata = {
+ LOCKTIME,
+ MPLLCON,
+ UPLLCON,
+ CLKCON,
+ CLKDIVN,
+ CLKSRC,
+};
+
+static int s3c2412_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, s3c2412_save,
+ ARRAY_SIZE(s3c2412_clk_regs));
+
+ return 0;
+}
+
+static void s3c2412_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, s3c2412_save,
+ ARRAY_SIZE(s3c2412_clk_regs));
+}
+
+static struct syscore_ops s3c2412_clk_syscore_ops = {
+ .suspend = s3c2412_clk_suspend,
+ .resume = s3c2412_clk_resume,
+};
+
+static void s3c2412_clk_sleep_init(void)
+{
+ s3c2412_save = samsung_clk_alloc_reg_dump(s3c2412_clk_regs,
+ ARRAY_SIZE(s3c2412_clk_regs));
+ if (!s3c2412_save) {
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+ return;
+ }
+
+ register_syscore_ops(&s3c2412_clk_syscore_ops);
+ return;
+}
+#else
+static void s3c2412_clk_sleep_init(void) {}
+#endif
+
+static struct clk_div_table divxti_d[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 4 },
+ { .val = 3, .div = 6 },
+ { .val = 4, .div = 8 },
+ { .val = 5, .div = 10 },
+ { .val = 6, .div = 12 },
+ { .val = 7, .div = 14 },
+ { /* sentinel */ },
+};
+
+struct samsung_div_clock s3c2412_dividers[] __initdata = {
+ DIV_T(0, "div_xti", "xti", CLKSRC, 0, 3, divxti_d),
+ DIV(0, "div_cam", "mux_cam", CLKDIVN, 16, 4),
+ DIV(0, "div_i2s", "mux_i2s", CLKDIVN, 12, 4),
+ DIV(0, "div_uart", "mux_uart", CLKDIVN, 8, 4),
+ DIV(0, "div_usb", "mux_usb", CLKDIVN, 6, 1),
+ DIV(0, "div_hclk_half", "hclk", CLKDIVN, 5, 1),
+ DIV(ARMDIV, "armdiv", "msysclk", CLKDIVN, 3, 1),
+ DIV(PCLK, "pclk", "hclk", CLKDIVN, 2, 1),
+ DIV(HCLK, "hclk", "armdiv", CLKDIVN, 0, 2),
+};
+
+struct samsung_fixed_factor_clock s3c2412_ffactor[] __initdata = {
+ FFACTOR(0, "ff_hclk", "hclk", 2, 1, CLK_SET_RATE_PARENT),
+};
+
+/*
+ * The first two use the OM[4] setting, which is not readable from
+ * software, so assume it is set to xti.
+ */
+PNAME(erefclk_p) = { "xti", "xti", "xti", "ext" };
+PNAME(urefclk_p) = { "xti", "xti", "xti", "ext" };
+
+PNAME(camclk_p) = { "usysclk", "hclk" };
+PNAME(usbclk_p) = { "usysclk", "hclk" };
+PNAME(i2sclk_p) = { "erefclk", "mpll" };
+PNAME(uartclk_p) = { "erefclk", "mpll" };
+PNAME(usysclk_p) = { "urefclk", "upll" };
+PNAME(msysclk_p) = { "mdivclk", "mpll" };
+PNAME(mdivclk_p) = { "xti", "div_xti" };
+PNAME(armclk_p) = { "armdiv", "hclk" };
+
+struct samsung_mux_clock s3c2412_muxes[] __initdata = {
+ MUX(0, "erefclk", erefclk_p, CLKSRC, 14, 2),
+ MUX(0, "urefclk", urefclk_p, CLKSRC, 12, 2),
+ MUX(0, "mux_cam", camclk_p, CLKSRC, 11, 1),
+ MUX(0, "mux_usb", usbclk_p, CLKSRC, 10, 1),
+ MUX(0, "mux_i2s", i2sclk_p, CLKSRC, 9, 1),
+ MUX(0, "mux_uart", uartclk_p, CLKSRC, 8, 1),
+ MUX(USYSCLK, "usysclk", usysclk_p, CLKSRC, 5, 1),
+ MUX(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1),
+ MUX(MDIVCLK, "mdivclk", mdivclk_p, CLKSRC, 3, 1),
+ MUX(ARMCLK, "armclk", armclk_p, CLKDIVN, 4, 1),
+};
+
+static struct samsung_pll_clock s3c2412_plls[] __initdata = {
+ [mpll] = PLL(pll_s3c2440_mpll, MPLL, "mpll", "xti",
+ LOCKTIME, MPLLCON, NULL),
+ [upll] = PLL(pll_s3c2410_upll, UPLL, "upll", "urefclk",
+ LOCKTIME, UPLLCON, NULL),
+};
+
+struct samsung_gate_clock s3c2412_gates[] __initdata = {
+ GATE(PCLK_WDT, "wdt", "pclk", CLKCON, 28, 0, 0),
+ GATE(PCLK_SPI, "spi", "pclk", CLKCON, 27, 0, 0),
+ GATE(PCLK_I2S, "i2s", "pclk", CLKCON, 26, 0, 0),
+ GATE(PCLK_I2C, "i2c", "pclk", CLKCON, 25, 0, 0),
+ GATE(PCLK_ADC, "adc", "pclk", CLKCON, 24, 0, 0),
+ GATE(PCLK_RTC, "rtc", "pclk", CLKCON, 23, 0, 0),
+ GATE(PCLK_GPIO, "gpio", "pclk", CLKCON, 22, CLK_IGNORE_UNUSED, 0),
+ GATE(PCLK_UART2, "uart2", "pclk", CLKCON, 21, 0, 0),
+ GATE(PCLK_UART1, "uart1", "pclk", CLKCON, 20, 0, 0),
+ GATE(PCLK_UART0, "uart0", "pclk", CLKCON, 19, 0, 0),
+ GATE(PCLK_SDI, "sdi", "pclk", CLKCON, 18, 0, 0),
+ GATE(PCLK_PWM, "pwm", "pclk", CLKCON, 17, 0, 0),
+ GATE(PCLK_USBD, "usb-device", "pclk", CLKCON, 16, 0, 0),
+ GATE(SCLK_CAM, "sclk_cam", "div_cam", CLKCON, 15, 0, 0),
+ GATE(SCLK_UART, "sclk_uart", "div_uart", CLKCON, 14, 0, 0),
+ GATE(SCLK_I2S, "sclk_i2s", "div_i2s", CLKCON, 13, 0, 0),
+ GATE(SCLK_USBH, "sclk_usbh", "div_usb", CLKCON, 12, 0, 0),
+ GATE(SCLK_USBD, "sclk_usbd", "div_usb", CLKCON, 11, 0, 0),
+ GATE(HCLK_HALF, "hclk_half", "div_hclk_half", CLKCON, 10, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_X2, "hclkx2", "ff_hclk", CLKCON, 9, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_SDRAM, "sdram", "hclk", CLKCON, 8, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_USBH, "usb-host", "hclk", CLKCON, 6, 0, 0),
+ GATE(HCLK_LCD, "lcd", "hclk", CLKCON, 5, 0, 0),
+ GATE(HCLK_NAND, "nand", "hclk", CLKCON, 4, 0, 0),
+ GATE(HCLK_DMA3, "dma3", "hclk", CLKCON, 3, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA2, "dma2", "hclk", CLKCON, 2, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA1, "dma1", "hclk", CLKCON, 1, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA0, "dma0", "hclk", CLKCON, 0, CLK_IGNORE_UNUSED, 0),
+};
+
+struct samsung_clock_alias s3c2412_aliases[] __initdata = {
+ ALIAS(PCLK_UART0, "s3c2412-uart.0", "uart"),
+ ALIAS(PCLK_UART1, "s3c2412-uart.1", "uart"),
+ ALIAS(PCLK_UART2, "s3c2412-uart.2", "uart"),
+ ALIAS(PCLK_UART0, "s3c2412-uart.0", "clk_uart_baud2"),
+ ALIAS(PCLK_UART1, "s3c2412-uart.1", "clk_uart_baud2"),
+ ALIAS(PCLK_UART2, "s3c2412-uart.2", "clk_uart_baud2"),
+ ALIAS(SCLK_UART, NULL, "clk_uart_baud3"),
+ ALIAS(PCLK_I2C, "s3c2410-i2c.0", "i2c"),
+ ALIAS(PCLK_ADC, NULL, "adc"),
+ ALIAS(PCLK_RTC, NULL, "rtc"),
+ ALIAS(PCLK_PWM, NULL, "timers"),
+ ALIAS(HCLK_LCD, NULL, "lcd"),
+ ALIAS(PCLK_USBD, NULL, "usb-device"),
+ ALIAS(SCLK_USBD, NULL, "usb-bus-gadget"),
+ ALIAS(HCLK_USBH, NULL, "usb-host"),
+ ALIAS(SCLK_USBH, NULL, "usb-bus-host"),
+ ALIAS(ARMCLK, NULL, "armclk"),
+ ALIAS(HCLK, NULL, "hclk"),
+ ALIAS(MPLL, NULL, "mpll"),
+ ALIAS(MSYSCLK, NULL, "fclk"),
+};
+
+/*
+ * fixed rate clocks generated outside the soc
+ * Only necessary until the devicetree-move is complete
+ */
+#define XTI 1
+struct samsung_fixed_rate_clock s3c2412_common_frate_clks[] __initdata = {
+ FRATE(XTI, "xti", NULL, CLK_IS_ROOT, 0),
+ FRATE(0, "ext", NULL, CLK_IS_ROOT, 0),
+};
+
+static void __init s3c2412_common_clk_register_fixed_ext(unsigned long xti_f,
+ unsigned long ext_f)
+{
+ /* xtal alias is necessary for the current cpufreq driver */
+ struct samsung_clock_alias xti_alias = ALIAS(XTI, NULL, "xtal");
+
+ s3c2412_common_frate_clks[0].fixed_rate = xti_f;
+ s3c2412_common_frate_clks[1].fixed_rate = ext_f;
+ samsung_clk_register_fixed_rate(s3c2412_common_frate_clks,
+ ARRAY_SIZE(s3c2412_common_frate_clks));
+
+ samsung_clk_register_alias(&xti_alias, 1);
+}
+
+void __init s3c2412_common_clk_init(struct device_node *np, unsigned long xti_f,
+ unsigned long ext_f, void __iomem *base)
+{
+ reg_base = base;
+
+ if (np) {
+ reg_base = of_iomap(np, 0);
+ if (!reg_base)
+ panic("%s: failed to map registers\n", __func__);
+ }
+
+ samsung_clk_init(np, reg_base, NR_CLKS);
+
+ /* Register external clocks only in non-dt cases */
+ if (!np)
+ s3c2412_common_clk_register_fixed_ext(xti_f, ext_f);
+
+ /* Register PLLs. */
+ samsung_clk_register_pll(s3c2412_plls, ARRAY_SIZE(s3c2412_plls),
+ reg_base);
+
+ /* Register common internal clocks. */
+ samsung_clk_register_mux(s3c2412_muxes, ARRAY_SIZE(s3c2412_muxes));
+ samsung_clk_register_div(s3c2412_dividers,
+ ARRAY_SIZE(s3c2412_dividers));
+ samsung_clk_register_gate(s3c2412_gates, ARRAY_SIZE(s3c2412_gates));
+ samsung_clk_register_fixed_factor(s3c2412_ffactor,
+ ARRAY_SIZE(s3c2412_ffactor));
+ samsung_clk_register_alias(s3c2412_aliases,
+ ARRAY_SIZE(s3c2412_aliases));
+
+ s3c2412_clk_sleep_init();
+}
+
+static void __init s3c2412_clk_init(struct device_node *np)
+{
+ s3c2412_common_clk_init(np, 0, 0, 0);
+}
+CLK_OF_DECLARE(s3c2412_clk, "samsung,s3c2412-clock", s3c2412_clk_init);
diff --git a/drivers/clk/samsung/clk-s3c2443.c b/drivers/clk/samsung/clk-s3c2443.c
new file mode 100644
index 000000000000..8e4f4517e95c
--- /dev/null
+++ b/drivers/clk/samsung/clk-s3c2443.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
+ *
+ * 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.
+ *
+ * Common Clock Framework support for S3C2443 and following SoCs.
+ */
+
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/syscore_ops.h>
+
+#include <dt-bindings/clock/s3c2443.h>
+
+#include "clk.h"
+#include "clk-pll.h"
+
+/* S3C2416 clock controller register offsets */
+#define LOCKCON0 0x00
+#define LOCKCON1 0x04
+#define MPLLCON 0x10
+#define EPLLCON 0x18
+#define EPLLCON_K 0x1C
+#define CLKSRC 0x20
+#define CLKDIV0 0x24
+#define CLKDIV1 0x28
+#define CLKDIV2 0x2C
+#define HCLKCON 0x30
+#define PCLKCON 0x34
+#define SCLKCON 0x38
+
+/* the soc types */
+enum supported_socs {
+ S3C2416,
+ S3C2443,
+ S3C2450,
+};
+
+/* list of PLLs to be registered */
+enum s3c2443_plls {
+ mpll, epll,
+};
+
+static void __iomem *reg_base;
+
+#ifdef CONFIG_PM_SLEEP
+static struct samsung_clk_reg_dump *s3c2443_save;
+
+/*
+ * list of controller registers to be saved and restored during a
+ * suspend/resume cycle.
+ */
+static unsigned long s3c2443_clk_regs[] __initdata = {
+ LOCKCON0,
+ LOCKCON1,
+ MPLLCON,
+ EPLLCON,
+ EPLLCON_K,
+ CLKSRC,
+ CLKDIV0,
+ CLKDIV1,
+ CLKDIV2,
+ PCLKCON,
+ HCLKCON,
+ SCLKCON,
+};
+
+static int s3c2443_clk_suspend(void)
+{
+ samsung_clk_save(reg_base, s3c2443_save,
+ ARRAY_SIZE(s3c2443_clk_regs));
+
+ return 0;
+}
+
+static void s3c2443_clk_resume(void)
+{
+ samsung_clk_restore(reg_base, s3c2443_save,
+ ARRAY_SIZE(s3c2443_clk_regs));
+}
+
+static struct syscore_ops s3c2443_clk_syscore_ops = {
+ .suspend = s3c2443_clk_suspend,
+ .resume = s3c2443_clk_resume,
+};
+
+static void s3c2443_clk_sleep_init(void)
+{
+ s3c2443_save = samsung_clk_alloc_reg_dump(s3c2443_clk_regs,
+ ARRAY_SIZE(s3c2443_clk_regs));
+ if (!s3c2443_save) {
+ pr_warn("%s: failed to allocate sleep save data, no sleep support!\n",
+ __func__);
+ return;
+ }
+
+ register_syscore_ops(&s3c2443_clk_syscore_ops);
+ return;
+}
+#else
+static void s3c2443_clk_sleep_init(void) {}
+#endif
+
+PNAME(epllref_p) = { "mpllref", "mpllref", "xti", "ext" };
+PNAME(esysclk_p) = { "epllref", "epll" };
+PNAME(mpllref_p) = { "xti", "mdivclk" };
+PNAME(msysclk_p) = { "mpllref", "mpll" };
+PNAME(armclk_p) = { "armdiv" , "hclk" };
+PNAME(i2s0_p) = { "div_i2s0", "ext_i2s", "epllref", "epllref" };
+
+struct samsung_mux_clock s3c2443_common_muxes[] __initdata = {
+ MUX(0, "epllref", epllref_p, CLKSRC, 7, 2),
+ MUX(ESYSCLK, "esysclk", esysclk_p, CLKSRC, 6, 1),
+ MUX(0, "mpllref", mpllref_p, CLKSRC, 3, 1),
+ MUX_A(MSYSCLK, "msysclk", msysclk_p, CLKSRC, 4, 1, "msysclk"),
+ MUX_A(ARMCLK, "armclk", armclk_p, CLKDIV0, 13, 1, "armclk"),
+ MUX(0, "mux_i2s0", i2s0_p, CLKSRC, 14, 2),
+};
+
+static struct clk_div_table hclk_d[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 3, .div = 4 },
+ { /* sentinel */ },
+};
+
+static struct clk_div_table mdivclk_d[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 3 },
+ { .val = 2, .div = 5 },
+ { .val = 3, .div = 7 },
+ { .val = 4, .div = 9 },
+ { .val = 5, .div = 11 },
+ { .val = 6, .div = 13 },
+ { .val = 7, .div = 15 },
+ { /* sentinel */ },
+};
+
+struct samsung_div_clock s3c2443_common_dividers[] __initdata = {
+ DIV_T(0, "mdivclk", "xti", CLKDIV0, 6, 3, mdivclk_d),
+ DIV(0, "prediv", "msysclk", CLKDIV0, 4, 2),
+ DIV_T(HCLK, "hclk", "prediv", CLKDIV0, 0, 2, hclk_d),
+ DIV(PCLK, "pclk", "hclk", CLKDIV0, 2, 1),
+ DIV(0, "div_hsspi0_epll", "esysclk", CLKDIV1, 24, 2),
+ DIV(0, "div_fimd", "esysclk", CLKDIV1, 16, 8),
+ DIV(0, "div_i2s0", "esysclk", CLKDIV1, 12, 4),
+ DIV(0, "div_uart", "esysclk", CLKDIV1, 8, 4),
+ DIV(0, "div_hsmmc1", "esysclk", CLKDIV1, 6, 2),
+ DIV(0, "div_usbhost", "esysclk", CLKDIV1, 4, 2),
+};
+
+struct samsung_gate_clock s3c2443_common_gates[] __initdata = {
+ GATE(SCLK_HSMMC_EXT, "sclk_hsmmcext", "ext", SCLKCON, 13, 0, 0),
+ GATE(SCLK_HSMMC1, "sclk_hsmmc1", "div_hsmmc1", SCLKCON, 12, 0, 0),
+ GATE(SCLK_FIMD, "sclk_fimd", "div_fimd", SCLKCON, 10, 0, 0),
+ GATE(SCLK_I2S0, "sclk_i2s0", "mux_i2s0", SCLKCON, 9, 0, 0),
+ GATE(SCLK_UART, "sclk_uart", "div_uart", SCLKCON, 8, 0, 0),
+ GATE(SCLK_USBH, "sclk_usbhost", "div_usbhost", SCLKCON, 1, 0, 0),
+ GATE(HCLK_DRAM, "dram", "hclk", HCLKCON, 19, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_SSMC, "ssmc", "hclk", HCLKCON, 18, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_HSMMC1, "hsmmc1", "hclk", HCLKCON, 16, 0, 0),
+ GATE(HCLK_USBD, "usb-device", "hclk", HCLKCON, 12, 0, 0),
+ GATE(HCLK_USBH, "usb-host", "hclk", HCLKCON, 11, 0, 0),
+ GATE(HCLK_LCD, "lcd", "hclk", HCLKCON, 9, 0, 0),
+ GATE(HCLK_DMA5, "dma5", "hclk", HCLKCON, 5, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA4, "dma4", "hclk", HCLKCON, 4, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA3, "dma3", "hclk", HCLKCON, 3, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA2, "dma2", "hclk", HCLKCON, 2, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA1, "dma1", "hclk", HCLKCON, 1, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA0, "dma0", "hclk", HCLKCON, 0, CLK_IGNORE_UNUSED, 0),
+ GATE(PCLK_GPIO, "gpio", "pclk", PCLKCON, 13, CLK_IGNORE_UNUSED, 0),
+ GATE(PCLK_RTC, "rtc", "pclk", PCLKCON, 12, 0, 0),
+ GATE(PCLK_WDT, "wdt", "pclk", PCLKCON, 11, 0, 0),
+ GATE(PCLK_PWM, "pwm", "pclk", PCLKCON, 10, 0, 0),
+ GATE(PCLK_I2S0, "i2s0", "pclk", PCLKCON, 9, 0, 0),
+ GATE(PCLK_AC97, "ac97", "pclk", PCLKCON, 8, 0, 0),
+ GATE(PCLK_ADC, "adc", "pclk", PCLKCON, 7, 0, 0),
+ GATE(PCLK_SPI0, "spi0", "pclk", PCLKCON, 6, 0, 0),
+ GATE(PCLK_I2C0, "i2c0", "pclk", PCLKCON, 4, 0, 0),
+ GATE(PCLK_UART3, "uart3", "pclk", PCLKCON, 3, 0, 0),
+ GATE(PCLK_UART2, "uart2", "pclk", PCLKCON, 2, 0, 0),
+ GATE(PCLK_UART1, "uart1", "pclk", PCLKCON, 1, 0, 0),
+ GATE(PCLK_UART0, "uart0", "pclk", PCLKCON, 0, 0, 0),
+};
+
+struct samsung_clock_alias s3c2443_common_aliases[] __initdata = {
+ ALIAS(HCLK, NULL, "hclk"),
+ ALIAS(HCLK_SSMC, NULL, "nand"),
+ ALIAS(PCLK_UART0, "s3c2440-uart.0", "uart"),
+ ALIAS(PCLK_UART1, "s3c2440-uart.1", "uart"),
+ ALIAS(PCLK_UART2, "s3c2440-uart.2", "uart"),
+ ALIAS(PCLK_UART3, "s3c2440-uart.3", "uart"),
+ ALIAS(PCLK_UART0, "s3c2440-uart.0", "clk_uart_baud2"),
+ ALIAS(PCLK_UART1, "s3c2440-uart.1", "clk_uart_baud2"),
+ ALIAS(PCLK_UART2, "s3c2440-uart.2", "clk_uart_baud2"),
+ ALIAS(PCLK_UART3, "s3c2440-uart.3", "clk_uart_baud2"),
+ ALIAS(SCLK_UART, NULL, "clk_uart_baud3"),
+ ALIAS(PCLK_PWM, NULL, "timers"),
+ ALIAS(PCLK_RTC, NULL, "rtc"),
+ ALIAS(PCLK_WDT, NULL, "watchdog"),
+ ALIAS(PCLK_ADC, NULL, "adc"),
+ ALIAS(PCLK_I2C0, "s3c2410-i2c.0", "i2c"),
+ ALIAS(HCLK_USBD, NULL, "usb-device"),
+ ALIAS(HCLK_USBH, NULL, "usb-host"),
+ ALIAS(SCLK_USBH, NULL, "usb-bus-host"),
+ ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi"),
+ ALIAS(PCLK_SPI0, "s3c2443-spi.0", "spi_busclk0"),
+ ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "hsmmc"),
+ ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"),
+ ALIAS(PCLK_I2S0, "samsung-i2s.0", "iis"),
+ ALIAS(SCLK_I2S0, NULL, "i2s-if"),
+ ALIAS(HCLK_LCD, NULL, "lcd"),
+ ALIAS(SCLK_FIMD, NULL, "sclk_fimd"),
+};
+
+/* S3C2416 specific clocks */
+
+static struct samsung_pll_clock s3c2416_pll_clks[] __initdata = {
+ [mpll] = PLL(pll_6552_s3c2416, 0, "mpll", "mpllref",
+ LOCKCON0, MPLLCON, NULL),
+ [epll] = PLL(pll_6553, 0, "epll", "epllref",
+ LOCKCON1, EPLLCON, NULL),
+};
+
+PNAME(s3c2416_hsmmc0_p) = { "sclk_hsmmc0", "sclk_hsmmcext" };
+PNAME(s3c2416_hsmmc1_p) = { "sclk_hsmmc1", "sclk_hsmmcext" };
+PNAME(s3c2416_hsspi0_p) = { "hsspi0_epll", "hsspi0_mpll" };
+
+static struct clk_div_table armdiv_s3c2416_d[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 5, .div = 6 },
+ { .val = 7, .div = 8 },
+ { /* sentinel */ },
+};
+
+struct samsung_div_clock s3c2416_dividers[] __initdata = {
+ DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 3, armdiv_s3c2416_d),
+ DIV(0, "div_hsspi0_mpll", "msysclk", CLKDIV2, 0, 4),
+ DIV(0, "div_hsmmc0", "esysclk", CLKDIV2, 6, 2),
+};
+
+struct samsung_mux_clock s3c2416_muxes[] __initdata = {
+ MUX(MUX_HSMMC0, "mux_hsmmc0", s3c2416_hsmmc0_p, CLKSRC, 16, 1),
+ MUX(MUX_HSMMC1, "mux_hsmmc1", s3c2416_hsmmc1_p, CLKSRC, 17, 1),
+ MUX(MUX_HSSPI0, "mux_hsspi0", s3c2416_hsspi0_p, CLKSRC, 18, 1),
+};
+
+struct samsung_gate_clock s3c2416_gates[] __initdata = {
+ GATE(0, "hsspi0_mpll", "div_hsspi0_mpll", SCLKCON, 19, 0, 0),
+ GATE(0, "hsspi0_epll", "div_hsspi0_epll", SCLKCON, 14, 0, 0),
+ GATE(0, "sclk_hsmmc0", "div_hsmmc0", SCLKCON, 6, 0, 0),
+ GATE(HCLK_2D, "2d", "hclk", HCLKCON, 20, 0, 0),
+ GATE(HCLK_HSMMC0, "hsmmc0", "hclk", HCLKCON, 15, 0, 0),
+ GATE(HCLK_IROM, "irom", "hclk", HCLKCON, 13, CLK_IGNORE_UNUSED, 0),
+ GATE(PCLK_PCM, "pcm", "pclk", PCLKCON, 19, 0, 0),
+};
+
+struct samsung_clock_alias s3c2416_aliases[] __initdata = {
+ ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"),
+ ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"),
+ ALIAS(MUX_HSMMC0, "s3c-sdhci.0", "mmc_busclk.2"),
+ ALIAS(MUX_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"),
+ ALIAS(MUX_HSSPI0, "s3c2443-spi.0", "spi_busclk2"),
+ ALIAS(ARMDIV, NULL, "armdiv"),
+};
+
+/* S3C2443 specific clocks */
+
+static struct samsung_pll_clock s3c2443_pll_clks[] __initdata = {
+ [mpll] = PLL(pll_3000, 0, "mpll", "mpllref",
+ LOCKCON0, MPLLCON, NULL),
+ [epll] = PLL(pll_2126, 0, "epll", "epllref",
+ LOCKCON1, EPLLCON, NULL),
+};
+
+static struct clk_div_table armdiv_s3c2443_d[] = {
+ { .val = 0, .div = 1 },
+ { .val = 8, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 9, .div = 4 },
+ { .val = 10, .div = 6 },
+ { .val = 11, .div = 8 },
+ { .val = 13, .div = 12 },
+ { .val = 15, .div = 16 },
+ { /* sentinel */ },
+};
+
+struct samsung_div_clock s3c2443_dividers[] __initdata = {
+ DIV_T(ARMDIV, "armdiv", "msysclk", CLKDIV0, 9, 4, armdiv_s3c2443_d),
+ DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4),
+};
+
+struct samsung_gate_clock s3c2443_gates[] __initdata = {
+ GATE(SCLK_HSSPI0, "sclk_hsspi0", "div_hsspi0_epll", SCLKCON, 14, 0, 0),
+ GATE(SCLK_CAM, "sclk_cam", "div_cam", SCLKCON, 11, 0, 0),
+ GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0),
+ GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 15, 0, 0),
+ GATE(PCLK_SDI, "sdi", "pclk", PCLKCON, 5, 0, 0),
+};
+
+struct samsung_clock_alias s3c2443_aliases[] __initdata = {
+ ALIAS(SCLK_HSSPI0, "s3c2443-spi.0", "spi_busclk2"),
+ ALIAS(SCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.2"),
+ ALIAS(SCLK_CAM, NULL, "camif-upll"),
+ ALIAS(PCLK_SPI1, "s3c2410-spi.0", "spi"),
+ ALIAS(PCLK_SDI, NULL, "sdi"),
+ ALIAS(HCLK_CFC, NULL, "cfc"),
+ ALIAS(ARMDIV, NULL, "armdiv"),
+};
+
+/* S3C2450 specific clocks */
+
+PNAME(s3c2450_cam_p) = { "div_cam", "hclk" };
+PNAME(s3c2450_hsspi1_p) = { "hsspi1_epll", "hsspi1_mpll" };
+PNAME(i2s1_p) = { "div_i2s1", "ext_i2s", "epllref", "epllref" };
+
+struct samsung_div_clock s3c2450_dividers[] __initdata = {
+ DIV(0, "div_cam", "esysclk", CLKDIV1, 26, 4),
+ DIV(0, "div_hsspi1_epll", "esysclk", CLKDIV2, 24, 2),
+ DIV(0, "div_hsspi1_mpll", "msysclk", CLKDIV2, 16, 4),
+ DIV(0, "div_i2s1", "esysclk", CLKDIV2, 12, 4),
+};
+
+struct samsung_mux_clock s3c2450_muxes[] __initdata = {
+ MUX(0, "mux_cam", s3c2450_cam_p, CLKSRC, 20, 1),
+ MUX(MUX_HSSPI1, "mux_hsspi1", s3c2450_hsspi1_p, CLKSRC, 19, 1),
+ MUX(0, "mux_i2s1", i2s1_p, CLKSRC, 12, 2),
+};
+
+struct samsung_gate_clock s3c2450_gates[] __initdata = {
+ GATE(SCLK_I2S1, "sclk_i2s1", "div_i2s1", SCLKCON, 5, 0, 0),
+ GATE(HCLK_CFC, "cfc", "hclk", HCLKCON, 17, 0, 0),
+ GATE(HCLK_CAM, "cam", "hclk", HCLKCON, 8, 0, 0),
+ GATE(HCLK_DMA7, "dma7", "hclk", HCLKCON, 7, CLK_IGNORE_UNUSED, 0),
+ GATE(HCLK_DMA6, "dma6", "hclk", HCLKCON, 6, CLK_IGNORE_UNUSED, 0),
+ GATE(PCLK_I2S1, "i2s1", "pclk", PCLKCON, 17, 0, 0),
+ GATE(PCLK_I2C1, "i2c1", "pclk", PCLKCON, 16, 0, 0),
+ GATE(PCLK_SPI1, "spi1", "pclk", PCLKCON, 14, 0, 0),
+};
+
+struct samsung_clock_alias s3c2450_aliases[] __initdata = {
+ ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi"),
+ ALIAS(PCLK_SPI1, "s3c2443-spi.1", "spi_busclk0"),
+ ALIAS(MUX_HSSPI1, "s3c2443-spi.1", "spi_busclk2"),
+ ALIAS(PCLK_I2C1, "s3c2410-i2c.1", "i2c"),
+};
+
+/*
+ * fixed rate clocks generated outside the soc
+ * Only necessary until the devicetree-move is complete
+ */
+struct samsung_fixed_rate_clock s3c2443_common_frate_clks[] __initdata = {
+ FRATE(0, "xti", NULL, CLK_IS_ROOT, 0),
+ FRATE(0, "ext", NULL, CLK_IS_ROOT, 0),
+ FRATE(0, "ext_i2s", NULL, CLK_IS_ROOT, 0),
+ FRATE(0, "ext_uart", NULL, CLK_IS_ROOT, 0),
+};
+
+static void __init s3c2443_common_clk_register_fixed_ext(unsigned long xti_f)
+{
+ s3c2443_common_frate_clks[0].fixed_rate = xti_f;
+ samsung_clk_register_fixed_rate(s3c2443_common_frate_clks,
+ ARRAY_SIZE(s3c2443_common_frate_clks));
+}
+
+void __init s3c2443_common_clk_init(struct device_node *np, unsigned long xti_f,
+ int current_soc,
+ void __iomem *base)
+{
+ reg_base = base;
+
+ if (np) {
+ reg_base = of_iomap(np, 0);
+ if (!reg_base)
+ panic("%s: failed to map registers\n", __func__);
+ }
+
+ samsung_clk_init(np, reg_base, NR_CLKS);
+
+ /* Register external clocks only in non-dt cases */
+ if (!np)
+ s3c2443_common_clk_register_fixed_ext(xti_f);
+
+ /* Register PLLs. */
+ if (current_soc == S3C2416 || current_soc == S3C2450)
+ samsung_clk_register_pll(s3c2416_pll_clks,
+ ARRAY_SIZE(s3c2416_pll_clks), reg_base);
+ else
+ samsung_clk_register_pll(s3c2443_pll_clks,
+ ARRAY_SIZE(s3c2443_pll_clks), reg_base);
+
+ /* Register common internal clocks. */
+ samsung_clk_register_mux(s3c2443_common_muxes,
+ ARRAY_SIZE(s3c2443_common_muxes));
+ samsung_clk_register_div(s3c2443_common_dividers,
+ ARRAY_SIZE(s3c2443_common_dividers));
+ samsung_clk_register_gate(s3c2443_common_gates,
+ ARRAY_SIZE(s3c2443_common_gates));
+ samsung_clk_register_alias(s3c2443_common_aliases,
+ ARRAY_SIZE(s3c2443_common_aliases));
+
+ /* Register SoC-specific clocks. */
+ switch (current_soc) {
+ case S3C2450:
+ samsung_clk_register_div(s3c2450_dividers,
+ ARRAY_SIZE(s3c2450_dividers));
+ samsung_clk_register_mux(s3c2450_muxes,
+ ARRAY_SIZE(s3c2450_muxes));
+ samsung_clk_register_gate(s3c2450_gates,
+ ARRAY_SIZE(s3c2450_gates));
+ samsung_clk_register_alias(s3c2450_aliases,
+ ARRAY_SIZE(s3c2450_aliases));
+ /* fall through, as s3c2450 extends the s3c2416 clocks */
+ case S3C2416:
+ samsung_clk_register_div(s3c2416_dividers,
+ ARRAY_SIZE(s3c2416_dividers));
+ samsung_clk_register_mux(s3c2416_muxes,
+ ARRAY_SIZE(s3c2416_muxes));
+ samsung_clk_register_gate(s3c2416_gates,
+ ARRAY_SIZE(s3c2416_gates));
+ samsung_clk_register_alias(s3c2416_aliases,
+ ARRAY_SIZE(s3c2416_aliases));
+ break;
+ case S3C2443:
+ samsung_clk_register_div(s3c2443_dividers,
+ ARRAY_SIZE(s3c2443_dividers));
+ samsung_clk_register_gate(s3c2443_gates,
+ ARRAY_SIZE(s3c2443_gates));
+ samsung_clk_register_alias(s3c2443_aliases,
+ ARRAY_SIZE(s3c2443_aliases));
+ break;
+ }
+
+ s3c2443_clk_sleep_init();
+}
+
+static void __init s3c2416_clk_init(struct device_node *np)
+{
+ s3c2443_common_clk_init(np, 0, S3C2416, 0);
+}
+CLK_OF_DECLARE(s3c2416_clk, "samsung,s3c2416-clock", s3c2416_clk_init);
+
+static void __init s3c2443_clk_init(struct device_node *np)
+{
+ s3c2443_common_clk_init(np, 0, S3C2443, 0);
+}
+CLK_OF_DECLARE(s3c2443_clk, "samsung,s3c2443-clock", s3c2443_clk_init);
+
+static void __init s3c2450_clk_init(struct device_node *np)
+{
+ s3c2443_common_clk_init(np, 0, S3C2450, 0);
+}
+CLK_OF_DECLARE(s3c2450_clk, "samsung,s3c2450-clock", s3c2450_clk_init);
diff --git a/drivers/clk/versatile/Kconfig b/drivers/clk/versatile/Kconfig
new file mode 100644
index 000000000000..1530c9352a76
--- /dev/null
+++ b/drivers/clk/versatile/Kconfig
@@ -0,0 +1,26 @@
+config COMMON_CLK_VERSATILE
+ bool "Clock driver for ARM Reference designs"
+ depends on ARCH_INTEGRATOR || ARCH_REALVIEW || ARCH_VEXPRESS || ARM64
+ ---help---
+ Supports clocking on ARM Reference designs:
+ - Integrator/AP and Integrator/CP
+ - RealView PB1176, EB, PB11MP and PBX
+ - Versatile Express
+
+config CLK_SP810
+ bool "Clock driver for ARM SP810 System Controller"
+ depends on COMMON_CLK_VERSATILE
+ default y if ARCH_VEXPRESS
+ ---help---
+ Supports clock muxing (REFCLK/TIMCLK to TIMERCLKEN0-3) capabilities
+ of the ARM SP810 System Controller cell.
+
+config CLK_VEXPRESS_OSC
+ bool "Clock driver for Versatile Express OSC clock generators"
+ depends on COMMON_CLK_VERSATILE
+ depends on VEXPRESS_CONFIG
+ default y if ARCH_VEXPRESS
+ ---help---
+ Simple regmap-based driver driving clock generators on Versatile
+ Express platforms hidden behind its configuration infrastructure,
+ commonly known as OSCs.
diff --git a/drivers/clk/versatile/Makefile b/drivers/clk/versatile/Makefile
index c16ca787170a..fd449f9b006d 100644
--- a/drivers/clk/versatile/Makefile
+++ b/drivers/clk/versatile/Makefile
@@ -3,5 +3,6 @@ obj-$(CONFIG_ICST) += clk-icst.o
obj-$(CONFIG_ARCH_INTEGRATOR) += clk-integrator.o
obj-$(CONFIG_INTEGRATOR_IMPD1) += clk-impd1.o
obj-$(CONFIG_ARCH_REALVIEW) += clk-realview.o
-obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o clk-sp810.o
-obj-$(CONFIG_VEXPRESS_CONFIG) += clk-vexpress-osc.o
+obj-$(CONFIG_ARCH_VEXPRESS) += clk-vexpress.o
+obj-$(CONFIG_CLK_SP810) += clk-sp810.o
+obj-$(CONFIG_CLK_VEXPRESS_OSC) += clk-vexpress-osc.o
diff --git a/drivers/clk/versatile/clk-vexpress-osc.c b/drivers/clk/versatile/clk-vexpress-osc.c
index 422391242b39..529a59c0fbfa 100644
--- a/drivers/clk/versatile/clk-vexpress-osc.c
+++ b/drivers/clk/versatile/clk-vexpress-osc.c
@@ -11,8 +11,6 @@
* Copyright (C) 2012 ARM Limited
*/
-#define pr_fmt(fmt) "vexpress-osc: " fmt
-
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/err.h>
@@ -22,7 +20,7 @@
#include <linux/vexpress.h>
struct vexpress_osc {
- struct vexpress_config_func *func;
+ struct regmap *reg;
struct clk_hw hw;
unsigned long rate_min;
unsigned long rate_max;
@@ -36,7 +34,7 @@ static unsigned long vexpress_osc_recalc_rate(struct clk_hw *hw,
struct vexpress_osc *osc = to_vexpress_osc(hw);
u32 rate;
- vexpress_config_read(osc->func, 0, &rate);
+ regmap_read(osc->reg, 0, &rate);
return rate;
}
@@ -60,7 +58,7 @@ static int vexpress_osc_set_rate(struct clk_hw *hw, unsigned long rate,
{
struct vexpress_osc *osc = to_vexpress_osc(hw);
- return vexpress_config_write(osc->func, 0, rate);
+ return regmap_write(osc->reg, 0, rate);
}
static struct clk_ops vexpress_osc_ops = {
@@ -70,58 +68,31 @@ static struct clk_ops vexpress_osc_ops = {
};
-struct clk * __init vexpress_osc_setup(struct device *dev)
-{
- struct clk_init_data init;
- struct vexpress_osc *osc = kzalloc(sizeof(*osc), GFP_KERNEL);
-
- if (!osc)
- return NULL;
-
- osc->func = vexpress_config_func_get_by_dev(dev);
- if (!osc->func) {
- kfree(osc);
- return NULL;
- }
-
- init.name = dev_name(dev);
- init.ops = &vexpress_osc_ops;
- init.flags = CLK_IS_ROOT;
- init.num_parents = 0;
- osc->hw.init = &init;
-
- return clk_register(NULL, &osc->hw);
-}
-
-void __init vexpress_osc_of_setup(struct device_node *node)
+static int vexpress_osc_probe(struct platform_device *pdev)
{
+ struct clk_lookup *cl = pdev->dev.platform_data; /* Non-DT lookup */
struct clk_init_data init;
struct vexpress_osc *osc;
struct clk *clk;
u32 range[2];
- vexpress_sysreg_of_early_init();
-
- osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ osc = devm_kzalloc(&pdev->dev, sizeof(*osc), GFP_KERNEL);
if (!osc)
- return;
+ return -ENOMEM;
- osc->func = vexpress_config_func_get_by_node(node);
- if (!osc->func) {
- pr_err("Failed to obtain config func for node '%s'!\n",
- node->full_name);
- goto error;
- }
+ osc->reg = devm_regmap_init_vexpress_config(&pdev->dev);
+ if (IS_ERR(osc->reg))
+ return PTR_ERR(osc->reg);
- if (of_property_read_u32_array(node, "freq-range", range,
+ if (of_property_read_u32_array(pdev->dev.of_node, "freq-range", range,
ARRAY_SIZE(range)) == 0) {
osc->rate_min = range[0];
osc->rate_max = range[1];
}
- of_property_read_string(node, "clock-output-names", &init.name);
- if (!init.name)
- init.name = node->full_name;
+ if (of_property_read_string(pdev->dev.of_node, "clock-output-names",
+ &init.name) != 0)
+ init.name = dev_name(&pdev->dev);
init.ops = &vexpress_osc_ops;
init.flags = CLK_IS_ROOT;
@@ -130,20 +101,37 @@ void __init vexpress_osc_of_setup(struct device_node *node)
osc->hw.init = &init;
clk = clk_register(NULL, &osc->hw);
- if (IS_ERR(clk)) {
- pr_err("Failed to register clock '%s'!\n", init.name);
- goto error;
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, clk);
+
+ /* Only happens for non-DT cases */
+ if (cl) {
+ cl->clk = clk;
+ clkdev_add(cl);
}
- of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ dev_dbg(&pdev->dev, "Registered clock '%s'\n", init.name);
+
+ return 0;
+}
- pr_debug("Registered clock '%s'\n", init.name);
+static struct of_device_id vexpress_osc_of_match[] = {
+ { .compatible = "arm,vexpress-osc", },
+ {}
+};
- return;
+static struct platform_driver vexpress_osc_driver = {
+ .driver = {
+ .name = "vexpress-osc",
+ .of_match_table = vexpress_osc_of_match,
+ },
+ .probe = vexpress_osc_probe,
+};
-error:
- if (osc->func)
- vexpress_config_func_put(osc->func);
- kfree(osc);
+static int __init vexpress_osc_init(void)
+{
+ return platform_driver_register(&vexpress_osc_driver);
}
-CLK_OF_DECLARE(vexpress_soc, "arm,vexpress-osc", vexpress_osc_of_setup);
+core_initcall(vexpress_osc_init);
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 96918e1f26a3..43f1acf0d1d2 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -191,3 +191,14 @@ config EM_TIMER_STI
config CLKSRC_QCOM
bool
+
+config CLKSRC_VERSATILE
+ bool "ARM Versatile (Express) reference platforms clock source"
+ depends on GENERIC_SCHED_CLOCK && !ARCH_USES_GETTIMEOFFSET
+ select CLKSRC_OF
+ default y if MFD_VEXPRESS_SYSREG
+ help
+ This option enables clock source based on free running
+ counter available in the "System Registers" block of
+ ARM Versatile, RealView and Versatile Express reference
+ platforms.
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 98cb6c51aa87..6f25bdffc176 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -39,3 +39,4 @@ obj-$(CONFIG_ARM_GLOBAL_TIMER) += arm_global_timer.o
obj-$(CONFIG_CLKSRC_METAG_GENERIC) += metag_generic.o
obj-$(CONFIG_ARCH_HAS_TICK_BROADCAST) += dummy_timer.o
obj-$(CONFIG_ARCH_KEYSTONE) += timer-keystone.o
+obj-$(CONFIG_CLKSRC_VERSATILE) += versatile.o
diff --git a/drivers/clocksource/timer-marco.c b/drivers/clocksource/timer-marco.c
index 7f5374dbefd9..dbd30398222a 100644
--- a/drivers/clocksource/timer-marco.c
+++ b/drivers/clocksource/timer-marco.c
@@ -252,15 +252,13 @@ static void __init sirfsoc_clockevent_init(void)
}
/* initialize the kernel jiffy timer source */
-static void __init sirfsoc_marco_timer_init(void)
+static void __init sirfsoc_marco_timer_init(struct device_node *np)
{
unsigned long rate;
u32 timer_div;
struct clk *clk;
- /* timer's input clock is io clock */
- clk = clk_get_sys("io", NULL);
-
+ clk = of_clk_get(np, 0);
BUG_ON(IS_ERR(clk));
rate = clk_get_rate(clk);
@@ -303,6 +301,6 @@ static void __init sirfsoc_of_timer_init(struct device_node *np)
if (!sirfsoc_timer1_irq.irq)
panic("No irq passed for timer1 via DT\n");
- sirfsoc_marco_timer_init();
+ sirfsoc_marco_timer_init(np);
}
CLOCKSOURCE_OF_DECLARE(sirfsoc_marco_timer, "sirf,marco-tick", sirfsoc_of_timer_init );
diff --git a/drivers/clocksource/timer-prima2.c b/drivers/clocksource/timer-prima2.c
index 1a6b2d6356d6..a722aac7ac02 100644
--- a/drivers/clocksource/timer-prima2.c
+++ b/drivers/clocksource/timer-prima2.c
@@ -61,7 +61,8 @@ static irqreturn_t sirfsoc_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *ce = dev_id;
- WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) & BIT(0)));
+ WARN_ON(!(readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_STATUS) &
+ BIT(0)));
/* clear timer0 interrupt */
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
@@ -77,9 +78,11 @@ static cycle_t sirfsoc_timer_read(struct clocksource *cs)
u64 cycles;
/* latch the 64-bit timer counter */
- writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+ writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
+ sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
cycles = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_HI);
- cycles = (cycles << 32) | readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
+ cycles = (cycles << 32) |
+ readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
return cycles;
}
@@ -89,11 +92,13 @@ static int sirfsoc_timer_set_next_event(unsigned long delta,
{
unsigned long now, next;
- writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+ writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
+ sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
next = now + delta;
writel_relaxed(next, sirfsoc_timer_base + SIRFSOC_TIMER_MATCH_0);
- writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+ writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
+ sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
now = readl_relaxed(sirfsoc_timer_base + SIRFSOC_TIMER_LATCHED_LO);
return next - now > delta ? -ETIME : 0;
@@ -108,10 +113,12 @@ static void sirfsoc_timer_set_mode(enum clock_event_mode mode,
WARN_ON(1);
break;
case CLOCK_EVT_MODE_ONESHOT:
- writel_relaxed(val | BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
+ writel_relaxed(val | BIT(0),
+ sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
break;
case CLOCK_EVT_MODE_SHUTDOWN:
- writel_relaxed(val & ~BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
+ writel_relaxed(val & ~BIT(0),
+ sirfsoc_timer_base + SIRFSOC_TIMER_INT_EN);
break;
case CLOCK_EVT_MODE_UNUSED:
case CLOCK_EVT_MODE_RESUME:
@@ -123,10 +130,13 @@ static void sirfsoc_clocksource_suspend(struct clocksource *cs)
{
int i;
- writel_relaxed(SIRFSOC_TIMER_LATCH_BIT, sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
+ writel_relaxed(SIRFSOC_TIMER_LATCH_BIT,
+ sirfsoc_timer_base + SIRFSOC_TIMER_LATCH);
for (i = 0; i < SIRFSOC_TIMER_REG_CNT; i++)
- sirfsoc_timer_reg_val[i] = readl_relaxed(sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
+ sirfsoc_timer_reg_val[i] =
+ readl_relaxed(sirfsoc_timer_base +
+ sirfsoc_timer_reg_list[i]);
}
static void sirfsoc_clocksource_resume(struct clocksource *cs)
@@ -134,10 +144,13 @@ static void sirfsoc_clocksource_resume(struct clocksource *cs)
int i;
for (i = 0; i < SIRFSOC_TIMER_REG_CNT - 2; i++)
- writel_relaxed(sirfsoc_timer_reg_val[i], sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
+ writel_relaxed(sirfsoc_timer_reg_val[i],
+ sirfsoc_timer_base + sirfsoc_timer_reg_list[i]);
- writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
- writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1], sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
+ writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 2],
+ sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
+ writel_relaxed(sirfsoc_timer_reg_val[SIRFSOC_TIMER_REG_CNT - 1],
+ sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
}
static struct clock_event_device sirfsoc_clockevent = {
@@ -185,11 +198,8 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
unsigned long rate;
struct clk *clk;
- /* timer's input clock is io clock */
- clk = clk_get_sys("io", NULL);
-
+ clk = of_clk_get(np, 0);
BUG_ON(IS_ERR(clk));
-
rate = clk_get_rate(clk);
BUG_ON(rate < PRIMA2_CLOCK_FREQ);
@@ -202,7 +212,7 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
sirfsoc_timer_irq.irq = irq_of_parse_and_map(np, 0);
writel_relaxed(rate / PRIMA2_CLOCK_FREQ / 2 - 1,
- sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
+ sirfsoc_timer_base + SIRFSOC_TIMER_DIV);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_LO);
writel_relaxed(0, sirfsoc_timer_base + SIRFSOC_TIMER_COUNTER_HI);
writel_relaxed(BIT(0), sirfsoc_timer_base + SIRFSOC_TIMER_STATUS);
@@ -216,4 +226,5 @@ static void __init sirfsoc_prima2_timer_init(struct device_node *np)
sirfsoc_clockevent_init();
}
-CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer, "sirf,prima2-tick", sirfsoc_prima2_timer_init);
+CLOCKSOURCE_OF_DECLARE(sirfsoc_prima2_timer,
+ "sirf,prima2-tick", sirfsoc_prima2_timer_init);
diff --git a/drivers/clocksource/versatile.c b/drivers/clocksource/versatile.c
new file mode 100644
index 000000000000..e4c50ad2f9d9
--- /dev/null
+++ b/drivers/clocksource/versatile.c
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/clocksource.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/sched_clock.h>
+
+#define SYS_24MHZ 0x05c
+
+static void __iomem *versatile_sys_24mhz;
+
+static u32 notrace versatile_sys_24mhz_read(void)
+{
+ return readl(versatile_sys_24mhz);
+}
+
+static void __init versatile_sched_clock_init(struct device_node *node)
+{
+ void __iomem *base = of_iomap(node, 0);
+
+ if (!base)
+ return;
+
+ versatile_sys_24mhz = base + SYS_24MHZ;
+
+ setup_sched_clock(versatile_sys_24mhz_read, 32, 24000000);
+}
+CLOCKSOURCE_OF_DECLARE(versatile, "arm,vexpress-sysreg",
+ versatile_sched_clock_init);
diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c
index f99cfe24e7bc..e8a4a7ed38c1 100644
--- a/drivers/cpufreq/exynos-cpufreq.c
+++ b/drivers/cpufreq/exynos-cpufreq.c
@@ -17,6 +17,7 @@
#include <linux/regulator/consumer.h>
#include <linux/cpufreq.h>
#include <linux/platform_device.h>
+#include <linux/of.h>
#include <plat/cpu.h>
@@ -163,14 +164,22 @@ static int exynos_cpufreq_probe(struct platform_device *pdev)
if (!exynos_info)
return -ENOMEM;
- if (soc_is_exynos4210())
+ if (of_machine_is_compatible("samsung,exynos4210")) {
+ exynos_info->type = EXYNOS_SOC_4210;
ret = exynos4210_cpufreq_init(exynos_info);
- else if (soc_is_exynos4212() || soc_is_exynos4412())
+ } else if (of_machine_is_compatible("samsung,exynos4212")) {
+ exynos_info->type = EXYNOS_SOC_4212;
ret = exynos4x12_cpufreq_init(exynos_info);
- else if (soc_is_exynos5250())
+ } else if (of_machine_is_compatible("samsung,exynos4412")) {
+ exynos_info->type = EXYNOS_SOC_4412;
+ ret = exynos4x12_cpufreq_init(exynos_info);
+ } else if (of_machine_is_compatible("samsung,exynos5250")) {
+ exynos_info->type = EXYNOS_SOC_5250;
ret = exynos5250_cpufreq_init(exynos_info);
- else
- return 0;
+ } else {
+ pr_err("%s: Unknown SoC type\n", __func__);
+ return -ENODEV;
+ }
if (ret)
goto err_vdd_arm;
diff --git a/drivers/cpufreq/exynos-cpufreq.h b/drivers/cpufreq/exynos-cpufreq.h
index 3ddade8a5125..f189547bb447 100644
--- a/drivers/cpufreq/exynos-cpufreq.h
+++ b/drivers/cpufreq/exynos-cpufreq.h
@@ -17,6 +17,13 @@ enum cpufreq_level_index {
L20,
};
+enum exynos_soc_type {
+ EXYNOS_SOC_4210,
+ EXYNOS_SOC_4212,
+ EXYNOS_SOC_4412,
+ EXYNOS_SOC_5250,
+};
+
#define APLL_FREQ(f, a0, a1, a2, a3, a4, a5, a6, a7, b0, b1, b2, m, p, s) \
{ \
.freq = (f) * 1000, \
@@ -34,6 +41,7 @@ struct apll_freq {
};
struct exynos_dvfs_info {
+ enum exynos_soc_type type;
unsigned long mpll_freq_khz;
unsigned int pll_safe_idx;
struct clk *cpu_clk;
diff --git a/drivers/cpufreq/exynos4x12-cpufreq.c b/drivers/cpufreq/exynos4x12-cpufreq.c
index 466c76ad335b..63a3907ce578 100644
--- a/drivers/cpufreq/exynos4x12-cpufreq.c
+++ b/drivers/cpufreq/exynos4x12-cpufreq.c
@@ -100,7 +100,6 @@ static struct apll_freq apll_freq_4412[] = {
static void exynos4x12_set_clkdiv(unsigned int div_index)
{
unsigned int tmp;
- unsigned int stat_cpu1;
/* Change Divider - CPU0 */
@@ -115,13 +114,11 @@ static void exynos4x12_set_clkdiv(unsigned int div_index)
tmp = apll_freq_4x12[div_index].clk_div_cpu1;
__raw_writel(tmp, EXYNOS4_CLKDIV_CPU1);
- if (soc_is_exynos4212())
- stat_cpu1 = 0x11;
- else
- stat_cpu1 = 0x111;
- while (__raw_readl(EXYNOS4_CLKDIV_STATCPU1) & stat_cpu1)
+ do {
cpu_relax();
+ tmp = __raw_readl(EXYNOS4_CLKDIV_STATCPU1);
+ } while (tmp != 0x0);
}
static void exynos4x12_set_apll(unsigned int index)
@@ -184,7 +181,7 @@ int exynos4x12_cpufreq_init(struct exynos_dvfs_info *info)
if (IS_ERR(mout_apll))
goto err_mout_apll;
- if (soc_is_exynos4212())
+ if (info->type == EXYNOS_SOC_4212)
apll_freq_4x12 = apll_freq_4212;
else
apll_freq_4x12 = apll_freq_4412;
diff --git a/drivers/cpufreq/s3c24xx-cpufreq.c b/drivers/cpufreq/s3c24xx-cpufreq.c
index be1b2b5c9753..227ebf7c1eea 100644
--- a/drivers/cpufreq/s3c24xx-cpufreq.c
+++ b/drivers/cpufreq/s3c24xx-cpufreq.c
@@ -141,6 +141,7 @@ static int s3c_cpufreq_calcdivs(struct s3c_cpufreq_config *cfg)
static void s3c_cpufreq_setfvco(struct s3c_cpufreq_config *cfg)
{
+ cfg->mpll = _clk_mpll;
(cfg->info->set_fvco)(cfg);
}
diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c
index 8242b75d96c8..611f34c7333d 100644
--- a/drivers/hwmon/vexpress.c
+++ b/drivers/hwmon/vexpress.c
@@ -26,7 +26,7 @@
struct vexpress_hwmon_data {
struct device *hwmon_dev;
- struct vexpress_config_func *func;
+ struct regmap *reg;
const char *name;
};
@@ -53,7 +53,7 @@ static ssize_t vexpress_hwmon_u32_show(struct device *dev,
int err;
u32 value;
- err = vexpress_config_read(data->func, 0, &value);
+ err = regmap_read(data->reg, 0, &value);
if (err)
return err;
@@ -68,11 +68,11 @@ static ssize_t vexpress_hwmon_u64_show(struct device *dev,
int err;
u32 value_hi, value_lo;
- err = vexpress_config_read(data->func, 0, &value_lo);
+ err = regmap_read(data->reg, 0, &value_lo);
if (err)
return err;
- err = vexpress_config_read(data->func, 1, &value_hi);
+ err = regmap_read(data->reg, 1, &value_hi);
if (err)
return err;
@@ -234,9 +234,9 @@ static int vexpress_hwmon_probe(struct platform_device *pdev)
type = match->data;
data->name = type->name;
- data->func = vexpress_config_func_get_by_dev(&pdev->dev);
- if (!data->func)
- return -ENODEV;
+ data->reg = devm_regmap_init_vexpress_config(&pdev->dev);
+ if (IS_ERR(data->reg))
+ return PTR_ERR(data->reg);
err = sysfs_create_groups(&pdev->dev.kobj, type->attr_groups);
if (err)
@@ -252,7 +252,6 @@ static int vexpress_hwmon_probe(struct platform_device *pdev)
error:
sysfs_remove_group(&pdev->dev.kobj, match->data);
- vexpress_config_func_put(data->func);
return err;
}
@@ -266,8 +265,6 @@ static int vexpress_hwmon_remove(struct platform_device *pdev)
match = of_match_device(vexpress_hwmon_of_match, &pdev->dev);
sysfs_remove_group(&pdev->dev.kobj, match->data);
- vexpress_config_func_put(data->func);
-
return 0;
}
diff --git a/drivers/irqchip/irq-sirfsoc.c b/drivers/irqchip/irq-sirfsoc.c
index 581eefe331ae..5e54f6d71e77 100644
--- a/drivers/irqchip/irq-sirfsoc.c
+++ b/drivers/irqchip/irq-sirfsoc.c
@@ -58,7 +58,8 @@ static void __exception_irq_entry sirfsoc_handle_irq(struct pt_regs *regs)
handle_IRQ(irqnr, regs);
}
-static int __init sirfsoc_irq_init(struct device_node *np, struct device_node *parent)
+static int __init sirfsoc_irq_init(struct device_node *np,
+ struct device_node *parent)
{
void __iomem *base = of_iomap(np, 0);
if (!base)
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index 6de9dfbf61c1..39e717797cc0 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -487,6 +487,14 @@ config LEDS_BLINKM
This option enables support for the BlinkM RGB LED connected
through I2C. Say Y to enable support for the BlinkM LED.
+config LEDS_VERSATILE
+ tristate "LED support for the ARM Versatile and RealView"
+ depends on ARCH_REALVIEW || ARCH_VERSATILE
+ depends on LEDS_CLASS
+ help
+ This option enabled support for the LEDs on the ARM Versatile
+ and RealView boards. Say Y to enabled these.
+
comment "LED Triggers"
source "drivers/leds/trigger/Kconfig"
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index 3cd76dbd9be2..8b4c956e11ba 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -54,6 +54,7 @@ obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o
obj-$(CONFIG_LEDS_MAX8997) += leds-max8997.o
obj-$(CONFIG_LEDS_LM355x) += leds-lm355x.o
obj-$(CONFIG_LEDS_BLINKM) += leds-blinkm.o
+obj-$(CONFIG_LEDS_VERSATILE) += leds-versatile.o
# LED SPI Drivers
obj-$(CONFIG_LEDS_DAC124S085) += leds-dac124s085.o
diff --git a/drivers/leds/leds-versatile.c b/drivers/leds/leds-versatile.c
new file mode 100644
index 000000000000..80553022d661
--- /dev/null
+++ b/drivers/leds/leds-versatile.c
@@ -0,0 +1,110 @@
+/*
+ * Driver for the 8 user LEDs found on the RealViews and Versatiles
+ * Based on DaVinci's DM365 board code
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ * Author: Linus Walleij <triad@df.lth.se>
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/leds.h>
+#include <linux/platform_device.h>
+
+struct versatile_led {
+ void __iomem *base;
+ struct led_classdev cdev;
+ u8 mask;
+};
+
+/*
+ * The triggers lines up below will only be used if the
+ * LED triggers are compiled in.
+ */
+static const struct {
+ const char *name;
+ const char *trigger;
+} versatile_leds[] = {
+ { "versatile:0", "heartbeat", },
+ { "versatile:1", "mmc0", },
+ { "versatile:2", "cpu0" },
+ { "versatile:3", "cpu1" },
+ { "versatile:4", "cpu2" },
+ { "versatile:5", "cpu3" },
+ { "versatile:6", },
+ { "versatile:7", },
+};
+
+static void versatile_led_set(struct led_classdev *cdev,
+ enum led_brightness b)
+{
+ struct versatile_led *led = container_of(cdev,
+ struct versatile_led, cdev);
+ u32 reg = readl(led->base);
+
+ if (b != LED_OFF)
+ reg |= led->mask;
+ else
+ reg &= ~led->mask;
+ writel(reg, led->base);
+}
+
+static enum led_brightness versatile_led_get(struct led_classdev *cdev)
+{
+ struct versatile_led *led = container_of(cdev,
+ struct versatile_led, cdev);
+ u32 reg = readl(led->base);
+
+ return (reg & led->mask) ? LED_FULL : LED_OFF;
+}
+
+static int versatile_leds_probe(struct platform_device *dev)
+{
+ int i;
+ struct resource *res;
+ void __iomem *base;
+
+ res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&dev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ /* All off */
+ writel(0, base);
+ for (i = 0; i < ARRAY_SIZE(versatile_leds); i++) {
+ struct versatile_led *led;
+
+ led = kzalloc(sizeof(*led), GFP_KERNEL);
+ if (!led)
+ break;
+
+ led->base = base;
+ led->cdev.name = versatile_leds[i].name;
+ led->cdev.brightness_set = versatile_led_set;
+ led->cdev.brightness_get = versatile_led_get;
+ led->cdev.default_trigger = versatile_leds[i].trigger;
+ led->mask = BIT(i);
+
+ if (led_classdev_register(NULL, &led->cdev) < 0) {
+ kfree(led);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver versatile_leds_driver = {
+ .driver = {
+ .name = "versatile-leds",
+ },
+ .probe = versatile_leds_probe,
+};
+
+module_platform_driver(versatile_leds_driver);
+
+MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
+MODULE_DESCRIPTION("ARM Versatile LED driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 33834120d057..f04ac62dd76b 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1227,12 +1227,17 @@ config MCP_UCB1200_TS
endmenu
-config VEXPRESS_CONFIG
- bool "ARM Versatile Express platform infrastructure"
- depends on ARM || ARM64
+config MFD_VEXPRESS_SYSREG
+ bool "Versatile Express System Registers"
+ depends on VEXPRESS_CONFIG && GPIOLIB
+ default y
+ select CLKSRC_MMIO
+ select GPIO_GENERIC_PLATFORM
+ select MFD_CORE
+ select MFD_SYSCON
help
- Platform configuration infrastructure for the ARM Ltd.
- Versatile Express.
+ System Registers are the platform configuration block
+ on the ARM Ltd. Versatile Express board.
endmenu
endif
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 2851275e2656..cec3487b539e 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -161,7 +161,7 @@ obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o
obj-$(CONFIG_MFD_SEC_CORE) += sec-core.o sec-irq.o
obj-$(CONFIG_MFD_SYSCON) += syscon.o
obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
-obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o
+obj-$(CONFIG_MFD_VEXPRESS_SYSREG) += vexpress-sysreg.o
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
obj-$(CONFIG_MFD_AS3722) += as3722.o
diff --git a/drivers/mfd/syscon.c b/drivers/mfd/syscon.c
index dbea55de4397..e2a04bb8bc1e 100644
--- a/drivers/mfd/syscon.c
+++ b/drivers/mfd/syscon.c
@@ -18,6 +18,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
+#include <linux/platform_data/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/mfd/syscon.h>
@@ -119,6 +120,7 @@ static struct regmap_config syscon_regmap_config = {
static int syscon_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ struct syscon_platform_data *pdata = dev_get_platdata(dev);
struct syscon *syscon;
struct resource *res;
void __iomem *base;
@@ -136,6 +138,8 @@ static int syscon_probe(struct platform_device *pdev)
return -ENOMEM;
syscon_regmap_config.max_register = res->end - res->start - 3;
+ if (pdata)
+ syscon_regmap_config.name = pdata->label;
syscon->regmap = devm_regmap_init_mmio(dev, base,
&syscon_regmap_config);
if (IS_ERR(syscon->regmap)) {
diff --git a/drivers/mfd/vexpress-config.c b/drivers/mfd/vexpress-config.c
deleted file mode 100644
index d0db89d13e01..000000000000
--- a/drivers/mfd/vexpress-config.c
+++ /dev/null
@@ -1,287 +0,0 @@
-/*
- * 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.
- *
- * 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.
- *
- * Copyright (C) 2012 ARM Limited
- */
-
-#define pr_fmt(fmt) "vexpress-config: " fmt
-
-#include <linux/bitops.h>
-#include <linux/completion.h>
-#include <linux/export.h>
-#include <linux/list.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/slab.h>
-#include <linux/string.h>
-#include <linux/vexpress.h>
-
-
-#define VEXPRESS_CONFIG_MAX_BRIDGES 2
-
-static struct vexpress_config_bridge {
- struct device_node *node;
- struct vexpress_config_bridge_info *info;
- struct list_head transactions;
- spinlock_t transactions_lock;
-} vexpress_config_bridges[VEXPRESS_CONFIG_MAX_BRIDGES];
-
-static DECLARE_BITMAP(vexpress_config_bridges_map,
- ARRAY_SIZE(vexpress_config_bridges));
-static DEFINE_MUTEX(vexpress_config_bridges_mutex);
-
-struct vexpress_config_bridge *vexpress_config_bridge_register(
- struct device_node *node,
- struct vexpress_config_bridge_info *info)
-{
- struct vexpress_config_bridge *bridge;
- int i;
-
- pr_debug("Registering bridge '%s'\n", info->name);
-
- mutex_lock(&vexpress_config_bridges_mutex);
- i = find_first_zero_bit(vexpress_config_bridges_map,
- ARRAY_SIZE(vexpress_config_bridges));
- if (i >= ARRAY_SIZE(vexpress_config_bridges)) {
- pr_err("Can't register more bridges!\n");
- mutex_unlock(&vexpress_config_bridges_mutex);
- return NULL;
- }
- __set_bit(i, vexpress_config_bridges_map);
- bridge = &vexpress_config_bridges[i];
-
- bridge->node = node;
- bridge->info = info;
- INIT_LIST_HEAD(&bridge->transactions);
- spin_lock_init(&bridge->transactions_lock);
-
- mutex_unlock(&vexpress_config_bridges_mutex);
-
- return bridge;
-}
-EXPORT_SYMBOL(vexpress_config_bridge_register);
-
-void vexpress_config_bridge_unregister(struct vexpress_config_bridge *bridge)
-{
- struct vexpress_config_bridge __bridge = *bridge;
- int i;
-
- mutex_lock(&vexpress_config_bridges_mutex);
- for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++)
- if (&vexpress_config_bridges[i] == bridge)
- __clear_bit(i, vexpress_config_bridges_map);
- mutex_unlock(&vexpress_config_bridges_mutex);
-
- WARN_ON(!list_empty(&__bridge.transactions));
- while (!list_empty(&__bridge.transactions))
- cpu_relax();
-}
-EXPORT_SYMBOL(vexpress_config_bridge_unregister);
-
-
-struct vexpress_config_func {
- struct vexpress_config_bridge *bridge;
- void *func;
-};
-
-struct vexpress_config_func *__vexpress_config_func_get(struct device *dev,
- struct device_node *node)
-{
- struct device_node *bridge_node;
- struct vexpress_config_func *func;
- int i;
-
- if (WARN_ON(dev && node && dev->of_node != node))
- return NULL;
- if (dev && !node)
- node = dev->of_node;
-
- func = kzalloc(sizeof(*func), GFP_KERNEL);
- if (!func)
- return NULL;
-
- bridge_node = of_node_get(node);
- while (bridge_node) {
- const __be32 *prop = of_get_property(bridge_node,
- "arm,vexpress,config-bridge", NULL);
-
- if (prop) {
- bridge_node = of_find_node_by_phandle(
- be32_to_cpup(prop));
- break;
- }
-
- bridge_node = of_get_next_parent(bridge_node);
- }
-
- mutex_lock(&vexpress_config_bridges_mutex);
- for (i = 0; i < ARRAY_SIZE(vexpress_config_bridges); i++) {
- struct vexpress_config_bridge *bridge =
- &vexpress_config_bridges[i];
-
- if (test_bit(i, vexpress_config_bridges_map) &&
- bridge->node == bridge_node) {
- func->bridge = bridge;
- func->func = bridge->info->func_get(dev, node);
- break;
- }
- }
- mutex_unlock(&vexpress_config_bridges_mutex);
-
- if (!func->func) {
- of_node_put(node);
- kfree(func);
- return NULL;
- }
-
- return func;
-}
-EXPORT_SYMBOL(__vexpress_config_func_get);
-
-void vexpress_config_func_put(struct vexpress_config_func *func)
-{
- func->bridge->info->func_put(func->func);
- of_node_put(func->bridge->node);
- kfree(func);
-}
-EXPORT_SYMBOL(vexpress_config_func_put);
-
-struct vexpress_config_trans {
- struct vexpress_config_func *func;
- int offset;
- bool write;
- u32 *data;
- int status;
- struct completion completion;
- struct list_head list;
-};
-
-static void vexpress_config_dump_trans(const char *what,
- struct vexpress_config_trans *trans)
-{
- pr_debug("%s %s trans %p func 0x%p offset %d data 0x%x status %d\n",
- what, trans->write ? "write" : "read", trans,
- trans->func->func, trans->offset,
- trans->data ? *trans->data : 0, trans->status);
-}
-
-static int vexpress_config_schedule(struct vexpress_config_trans *trans)
-{
- int status;
- struct vexpress_config_bridge *bridge = trans->func->bridge;
- unsigned long flags;
-
- init_completion(&trans->completion);
- trans->status = -EFAULT;
-
- spin_lock_irqsave(&bridge->transactions_lock, flags);
-
- if (list_empty(&bridge->transactions)) {
- vexpress_config_dump_trans("Executing", trans);
- status = bridge->info->func_exec(trans->func->func,
- trans->offset, trans->write, trans->data);
- } else {
- vexpress_config_dump_trans("Queuing", trans);
- status = VEXPRESS_CONFIG_STATUS_WAIT;
- }
-
- switch (status) {
- case VEXPRESS_CONFIG_STATUS_DONE:
- vexpress_config_dump_trans("Finished", trans);
- trans->status = status;
- break;
- case VEXPRESS_CONFIG_STATUS_WAIT:
- list_add_tail(&trans->list, &bridge->transactions);
- break;
- }
-
- spin_unlock_irqrestore(&bridge->transactions_lock, flags);
-
- return status;
-}
-
-void vexpress_config_complete(struct vexpress_config_bridge *bridge,
- int status)
-{
- struct vexpress_config_trans *trans;
- unsigned long flags;
- const char *message = "Completed";
-
- spin_lock_irqsave(&bridge->transactions_lock, flags);
-
- trans = list_first_entry(&bridge->transactions,
- struct vexpress_config_trans, list);
- trans->status = status;
-
- do {
- vexpress_config_dump_trans(message, trans);
- list_del(&trans->list);
- complete(&trans->completion);
-
- if (list_empty(&bridge->transactions))
- break;
-
- trans = list_first_entry(&bridge->transactions,
- struct vexpress_config_trans, list);
- vexpress_config_dump_trans("Executing pending", trans);
- trans->status = bridge->info->func_exec(trans->func->func,
- trans->offset, trans->write, trans->data);
- message = "Finished pending";
- } while (trans->status == VEXPRESS_CONFIG_STATUS_DONE);
-
- spin_unlock_irqrestore(&bridge->transactions_lock, flags);
-}
-EXPORT_SYMBOL(vexpress_config_complete);
-
-int vexpress_config_wait(struct vexpress_config_trans *trans)
-{
- wait_for_completion(&trans->completion);
-
- return trans->status;
-}
-EXPORT_SYMBOL(vexpress_config_wait);
-
-int vexpress_config_read(struct vexpress_config_func *func, int offset,
- u32 *data)
-{
- struct vexpress_config_trans trans = {
- .func = func,
- .offset = offset,
- .write = false,
- .data = data,
- .status = 0,
- };
- int status = vexpress_config_schedule(&trans);
-
- if (status == VEXPRESS_CONFIG_STATUS_WAIT)
- status = vexpress_config_wait(&trans);
-
- return status;
-}
-EXPORT_SYMBOL(vexpress_config_read);
-
-int vexpress_config_write(struct vexpress_config_func *func, int offset,
- u32 data)
-{
- struct vexpress_config_trans trans = {
- .func = func,
- .offset = offset,
- .write = true,
- .data = &data,
- .status = 0,
- };
- int status = vexpress_config_schedule(&trans);
-
- if (status == VEXPRESS_CONFIG_STATUS_WAIT)
- status = vexpress_config_wait(&trans);
-
- return status;
-}
-EXPORT_SYMBOL(vexpress_config_write);
diff --git a/drivers/mfd/vexpress-sysreg.c b/drivers/mfd/vexpress-sysreg.c
index 35281e804e7e..9e21e4fc9599 100644
--- a/drivers/mfd/vexpress-sysreg.c
+++ b/drivers/mfd/vexpress-sysreg.c
@@ -11,23 +11,22 @@
* Copyright (C) 2012 ARM Limited
*/
+#include <linux/basic_mmio_gpio.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/io.h>
-#include <linux/leds.h>
+#include <linux/mfd/core.h>
#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_data/syscon.h>
#include <linux/platform_device.h>
-#include <linux/regulator/driver.h>
#include <linux/slab.h>
#include <linux/stat.h>
-#include <linux/timer.h>
#include <linux/vexpress.h>
#define SYS_ID 0x000
#define SYS_SW 0x004
#define SYS_LED 0x008
#define SYS_100HZ 0x024
-#define SYS_FLAGS 0x030
#define SYS_FLAGSSET 0x030
#define SYS_FLAGSCLR 0x034
#define SYS_NVFLAGS 0x038
@@ -46,465 +45,209 @@
#define SYS_CFGSTAT 0x0a8
#define SYS_HBI_MASK 0xfff
-#define SYS_ID_HBI_SHIFT 16
#define SYS_PROCIDx_HBI_SHIFT 0
-#define SYS_LED_LED(n) (1 << (n))
-
#define SYS_MCI_CARDIN (1 << 0)
#define SYS_MCI_WPROT (1 << 1)
-#define SYS_FLASH_WPn (1 << 0)
-
#define SYS_MISC_MASTERSITE (1 << 14)
-#define SYS_CFGCTRL_START (1 << 31)
-#define SYS_CFGCTRL_WRITE (1 << 30)
-#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26)
-#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
-#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16)
-#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
-#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
-
-#define SYS_CFGSTAT_ERR (1 << 1)
-#define SYS_CFGSTAT_COMPLETE (1 << 0)
-
-
-static void __iomem *vexpress_sysreg_base;
-static struct device *vexpress_sysreg_dev;
-static int vexpress_master_site;
-
-void vexpress_flags_set(u32 data)
-{
- writel(~0, vexpress_sysreg_base + SYS_FLAGSCLR);
- writel(data, vexpress_sysreg_base + SYS_FLAGSSET);
-}
+static void __iomem *__vexpress_sysreg_base;
-u32 vexpress_get_procid(int site)
+static void __iomem *vexpress_sysreg_base(void)
{
- if (site == VEXPRESS_SITE_MASTER)
- site = vexpress_master_site;
-
- return readl(vexpress_sysreg_base + (site == VEXPRESS_SITE_DB1 ?
- SYS_PROCID0 : SYS_PROCID1));
-}
+ if (!__vexpress_sysreg_base) {
+ struct device_node *node = of_find_compatible_node(NULL, NULL,
+ "arm,vexpress-sysreg");
-u32 vexpress_get_hbi(int site)
-{
- u32 id;
-
- switch (site) {
- case VEXPRESS_SITE_MB:
- id = readl(vexpress_sysreg_base + SYS_ID);
- return (id >> SYS_ID_HBI_SHIFT) & SYS_HBI_MASK;
- case VEXPRESS_SITE_MASTER:
- case VEXPRESS_SITE_DB1:
- case VEXPRESS_SITE_DB2:
- id = vexpress_get_procid(site);
- return (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
+ __vexpress_sysreg_base = of_iomap(node, 0);
}
- return ~0;
-}
+ WARN_ON(!__vexpress_sysreg_base);
-void __iomem *vexpress_get_24mhz_clock_base(void)
-{
- return vexpress_sysreg_base + SYS_24MHZ;
+ return __vexpress_sysreg_base;
}
-static void vexpress_sysreg_find_prop(struct device_node *node,
- const char *name, u32 *val)
+static int vexpress_sysreg_get_master(void)
{
- of_node_get(node);
- while (node) {
- if (of_property_read_u32(node, name, val) == 0) {
- of_node_put(node);
- return;
- }
- node = of_get_next_parent(node);
- }
-}
-
-unsigned __vexpress_get_site(struct device *dev, struct device_node *node)
-{
- u32 site = 0;
-
- WARN_ON(dev && node && dev->of_node != node);
- if (dev && !node)
- node = dev->of_node;
-
- if (node) {
- vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site);
- } else if (dev && dev->bus == &platform_bus_type) {
- struct platform_device *pdev = to_platform_device(dev);
-
- if (pdev->num_resources == 1 &&
- pdev->resource[0].flags == IORESOURCE_BUS)
- site = pdev->resource[0].start;
- } else if (dev && strncmp(dev_name(dev), "ct:", 3) == 0) {
- site = VEXPRESS_SITE_MASTER;
- }
+ if (readl(vexpress_sysreg_base() + SYS_MISC) & SYS_MISC_MASTERSITE)
+ return VEXPRESS_SITE_DB2;
- if (site == VEXPRESS_SITE_MASTER)
- site = vexpress_master_site;
-
- return site;
+ return VEXPRESS_SITE_DB1;
}
-
-struct vexpress_sysreg_config_func {
- u32 template;
- u32 device;
-};
-
-static struct vexpress_config_bridge *vexpress_sysreg_config_bridge;
-static struct timer_list vexpress_sysreg_config_timer;
-static u32 *vexpress_sysreg_config_data;
-static int vexpress_sysreg_config_tries;
-
-static void *vexpress_sysreg_config_func_get(struct device *dev,
- struct device_node *node)
+void vexpress_flags_set(u32 data)
{
- struct vexpress_sysreg_config_func *config_func;
- u32 site = 0;
- u32 position = 0;
- u32 dcc = 0;
- u32 func_device[2];
- int err = -EFAULT;
-
- if (node) {
- of_node_get(node);
- vexpress_sysreg_find_prop(node, "arm,vexpress,site", &site);
- vexpress_sysreg_find_prop(node, "arm,vexpress,position",
- &position);
- vexpress_sysreg_find_prop(node, "arm,vexpress,dcc", &dcc);
- err = of_property_read_u32_array(node,
- "arm,vexpress-sysreg,func", func_device,
- ARRAY_SIZE(func_device));
- of_node_put(node);
- } else if (dev && dev->bus == &platform_bus_type) {
- struct platform_device *pdev = to_platform_device(dev);
-
- if (pdev->num_resources == 1 &&
- pdev->resource[0].flags == IORESOURCE_BUS) {
- site = pdev->resource[0].start;
- func_device[0] = pdev->resource[0].end;
- func_device[1] = pdev->id;
- err = 0;
- }
- }
- if (err)
- return NULL;
-
- config_func = kzalloc(sizeof(*config_func), GFP_KERNEL);
- if (!config_func)
- return NULL;
-
- config_func->template = SYS_CFGCTRL_DCC(dcc);
- config_func->template |= SYS_CFGCTRL_FUNC(func_device[0]);
- config_func->template |= SYS_CFGCTRL_SITE(site == VEXPRESS_SITE_MASTER ?
- vexpress_master_site : site);
- config_func->template |= SYS_CFGCTRL_POSITION(position);
- config_func->device |= func_device[1];
-
- dev_dbg(vexpress_sysreg_dev, "func 0x%p = 0x%x, %d\n", config_func,
- config_func->template, config_func->device);
-
- return config_func;
+ writel(~0, vexpress_sysreg_base() + SYS_FLAGSCLR);
+ writel(data, vexpress_sysreg_base() + SYS_FLAGSSET);
}
-static void vexpress_sysreg_config_func_put(void *func)
+unsigned int vexpress_get_mci_cardin(struct device *dev)
{
- kfree(func);
+ return readl(vexpress_sysreg_base() + SYS_MCI) & SYS_MCI_CARDIN;
}
-static int vexpress_sysreg_config_func_exec(void *func, int offset,
- bool write, u32 *data)
+u32 vexpress_get_procid(int site)
{
- int status;
- struct vexpress_sysreg_config_func *config_func = func;
- u32 command;
-
- if (WARN_ON(!vexpress_sysreg_base))
- return -ENOENT;
-
- command = readl(vexpress_sysreg_base + SYS_CFGCTRL);
- if (WARN_ON(command & SYS_CFGCTRL_START))
- return -EBUSY;
-
- command = SYS_CFGCTRL_START;
- command |= write ? SYS_CFGCTRL_WRITE : 0;
- command |= config_func->template;
- command |= SYS_CFGCTRL_DEVICE(config_func->device + offset);
-
- /* Use a canary for reads */
- if (!write)
- *data = 0xdeadbeef;
-
- dev_dbg(vexpress_sysreg_dev, "command %x, data %x\n",
- command, *data);
- writel(*data, vexpress_sysreg_base + SYS_CFGDATA);
- writel(0, vexpress_sysreg_base + SYS_CFGSTAT);
- writel(command, vexpress_sysreg_base + SYS_CFGCTRL);
- mb();
-
- if (vexpress_sysreg_dev) {
- /* Schedule completion check */
- if (!write)
- vexpress_sysreg_config_data = data;
- vexpress_sysreg_config_tries = 100;
- mod_timer(&vexpress_sysreg_config_timer,
- jiffies + usecs_to_jiffies(100));
- status = VEXPRESS_CONFIG_STATUS_WAIT;
- } else {
- /* Early execution, no timer available, have to spin */
- u32 cfgstat;
-
- do {
- cpu_relax();
- cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT);
- } while (!cfgstat);
-
- if (!write && (cfgstat & SYS_CFGSTAT_COMPLETE))
- *data = readl(vexpress_sysreg_base + SYS_CFGDATA);
- status = VEXPRESS_CONFIG_STATUS_DONE;
-
- if (cfgstat & SYS_CFGSTAT_ERR)
- status = -EINVAL;
- }
+ if (site == VEXPRESS_SITE_MASTER)
+ site = vexpress_sysreg_get_master();
- return status;
+ return readl(vexpress_sysreg_base() + (site == VEXPRESS_SITE_DB1 ?
+ SYS_PROCID0 : SYS_PROCID1));
}
-struct vexpress_config_bridge_info vexpress_sysreg_config_bridge_info = {
- .name = "vexpress-sysreg",
- .func_get = vexpress_sysreg_config_func_get,
- .func_put = vexpress_sysreg_config_func_put,
- .func_exec = vexpress_sysreg_config_func_exec,
-};
-
-static void vexpress_sysreg_config_complete(unsigned long data)
+void __iomem *vexpress_get_24mhz_clock_base(void)
{
- int status = VEXPRESS_CONFIG_STATUS_DONE;
- u32 cfgstat = readl(vexpress_sysreg_base + SYS_CFGSTAT);
-
- if (cfgstat & SYS_CFGSTAT_ERR)
- status = -EINVAL;
- if (!vexpress_sysreg_config_tries--)
- status = -ETIMEDOUT;
-
- if (status < 0) {
- dev_err(vexpress_sysreg_dev, "error %d\n", status);
- } else if (!(cfgstat & SYS_CFGSTAT_COMPLETE)) {
- mod_timer(&vexpress_sysreg_config_timer,
- jiffies + usecs_to_jiffies(50));
- return;
- }
-
- if (vexpress_sysreg_config_data) {
- *vexpress_sysreg_config_data = readl(vexpress_sysreg_base +
- SYS_CFGDATA);
- dev_dbg(vexpress_sysreg_dev, "read data %x\n",
- *vexpress_sysreg_config_data);
- vexpress_sysreg_config_data = NULL;
- }
-
- vexpress_config_complete(vexpress_sysreg_config_bridge, status);
+ return vexpress_sysreg_base() + SYS_24MHZ;
}
-void vexpress_sysreg_setup(struct device_node *node)
-{
- if (WARN_ON(!vexpress_sysreg_base))
- return;
-
- if (readl(vexpress_sysreg_base + SYS_MISC) & SYS_MISC_MASTERSITE)
- vexpress_master_site = VEXPRESS_SITE_DB2;
- else
- vexpress_master_site = VEXPRESS_SITE_DB1;
-
- vexpress_sysreg_config_bridge = vexpress_config_bridge_register(
- node, &vexpress_sysreg_config_bridge_info);
- WARN_ON(!vexpress_sysreg_config_bridge);
-}
-
void __init vexpress_sysreg_early_init(void __iomem *base)
{
- vexpress_sysreg_base = base;
- vexpress_sysreg_setup(NULL);
-}
-
-void __init vexpress_sysreg_of_early_init(void)
-{
- struct device_node *node;
-
- if (vexpress_sysreg_base)
- return;
+ __vexpress_sysreg_base = base;
- node = of_find_compatible_node(NULL, NULL, "arm,vexpress-sysreg");
- if (node) {
- vexpress_sysreg_base = of_iomap(node, 0);
- vexpress_sysreg_setup(node);
- }
+ vexpress_config_set_master(vexpress_sysreg_get_master());
}
-#ifdef CONFIG_GPIOLIB
-
-#define VEXPRESS_SYSREG_GPIO(_name, _reg, _value) \
- [VEXPRESS_GPIO_##_name] = { \
- .reg = _reg, \
- .value = _reg##_##_value, \
- }
+/* The sysreg block is just a random collection of various functions... */
-static struct vexpress_sysreg_gpio {
- unsigned long reg;
- u32 value;
-} vexpress_sysreg_gpios[] = {
- VEXPRESS_SYSREG_GPIO(MMC_CARDIN, SYS_MCI, CARDIN),
- VEXPRESS_SYSREG_GPIO(MMC_WPROT, SYS_MCI, WPROT),
- VEXPRESS_SYSREG_GPIO(FLASH_WPn, SYS_FLASH, WPn),
- VEXPRESS_SYSREG_GPIO(LED0, SYS_LED, LED(0)),
- VEXPRESS_SYSREG_GPIO(LED1, SYS_LED, LED(1)),
- VEXPRESS_SYSREG_GPIO(LED2, SYS_LED, LED(2)),
- VEXPRESS_SYSREG_GPIO(LED3, SYS_LED, LED(3)),
- VEXPRESS_SYSREG_GPIO(LED4, SYS_LED, LED(4)),
- VEXPRESS_SYSREG_GPIO(LED5, SYS_LED, LED(5)),
- VEXPRESS_SYSREG_GPIO(LED6, SYS_LED, LED(6)),
- VEXPRESS_SYSREG_GPIO(LED7, SYS_LED, LED(7)),
+static struct syscon_platform_data vexpress_sysreg_sys_id_pdata = {
+ .label = "sys_id",
};
-static int vexpress_sysreg_gpio_direction_input(struct gpio_chip *chip,
- unsigned offset)
-{
- return 0;
-}
-
-static int vexpress_sysreg_gpio_get(struct gpio_chip *chip,
- unsigned offset)
-{
- struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset];
- u32 reg_value = readl(vexpress_sysreg_base + gpio->reg);
-
- return !!(reg_value & gpio->value);
-}
-
-static void vexpress_sysreg_gpio_set(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- struct vexpress_sysreg_gpio *gpio = &vexpress_sysreg_gpios[offset];
- u32 reg_value = readl(vexpress_sysreg_base + gpio->reg);
-
- if (value)
- reg_value |= gpio->value;
- else
- reg_value &= ~gpio->value;
-
- writel(reg_value, vexpress_sysreg_base + gpio->reg);
-}
-
-static int vexpress_sysreg_gpio_direction_output(struct gpio_chip *chip,
- unsigned offset, int value)
-{
- vexpress_sysreg_gpio_set(chip, offset, value);
-
- return 0;
-}
-
-static struct gpio_chip vexpress_sysreg_gpio_chip = {
- .label = "vexpress-sysreg",
- .direction_input = vexpress_sysreg_gpio_direction_input,
- .direction_output = vexpress_sysreg_gpio_direction_output,
- .get = vexpress_sysreg_gpio_get,
- .set = vexpress_sysreg_gpio_set,
- .ngpio = ARRAY_SIZE(vexpress_sysreg_gpios),
- .base = 0,
+static struct bgpio_pdata vexpress_sysreg_sys_led_pdata = {
+ .label = "sys_led",
+ .base = -1,
+ .ngpio = 8,
};
-
-#define VEXPRESS_SYSREG_GREEN_LED(_name, _default_trigger, _gpio) \
- { \
- .name = "v2m:green:"_name, \
- .default_trigger = _default_trigger, \
- .gpio = VEXPRESS_GPIO_##_gpio, \
- }
-
-struct gpio_led vexpress_sysreg_leds[] = {
- VEXPRESS_SYSREG_GREEN_LED("user1", "heartbeat", LED0),
- VEXPRESS_SYSREG_GREEN_LED("user2", "mmc0", LED1),
- VEXPRESS_SYSREG_GREEN_LED("user3", "cpu0", LED2),
- VEXPRESS_SYSREG_GREEN_LED("user4", "cpu1", LED3),
- VEXPRESS_SYSREG_GREEN_LED("user5", "cpu2", LED4),
- VEXPRESS_SYSREG_GREEN_LED("user6", "cpu3", LED5),
- VEXPRESS_SYSREG_GREEN_LED("user7", "cpu4", LED6),
- VEXPRESS_SYSREG_GREEN_LED("user8", "cpu5", LED7),
+static struct bgpio_pdata vexpress_sysreg_sys_mci_pdata = {
+ .label = "sys_mci",
+ .base = -1,
+ .ngpio = 2,
};
-struct gpio_led_platform_data vexpress_sysreg_leds_pdata = {
- .num_leds = ARRAY_SIZE(vexpress_sysreg_leds),
- .leds = vexpress_sysreg_leds,
+static struct bgpio_pdata vexpress_sysreg_sys_flash_pdata = {
+ .label = "sys_flash",
+ .base = -1,
+ .ngpio = 1,
};
-#endif
-
+static struct syscon_platform_data vexpress_sysreg_sys_misc_pdata = {
+ .label = "sys_misc",
+};
-static ssize_t vexpress_sysreg_sys_id_show(struct device *dev,
- struct device_attribute *attr, char *buf)
-{
- return sprintf(buf, "0x%08x\n", readl(vexpress_sysreg_base + SYS_ID));
-}
+static struct syscon_platform_data vexpress_sysreg_sys_procid_pdata = {
+ .label = "sys_procid",
+};
-DEVICE_ATTR(sys_id, S_IRUGO, vexpress_sysreg_sys_id_show, NULL);
+static struct mfd_cell vexpress_sysreg_cells[] = {
+ {
+ .name = "syscon",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_ID, 0x4),
+ },
+ .platform_data = &vexpress_sysreg_sys_id_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_id_pdata),
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_led",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM_NAMED(SYS_LED, 0x4, "dat"),
+ },
+ .platform_data = &vexpress_sysreg_sys_led_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_led_pdata),
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_mci",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM_NAMED(SYS_MCI, 0x4, "dat"),
+ },
+ .platform_data = &vexpress_sysreg_sys_mci_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_mci_pdata),
+ }, {
+ .name = "basic-mmio-gpio",
+ .of_compatible = "arm,vexpress-sysreg,sys_flash",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM_NAMED(SYS_FLASH, 0x4, "dat"),
+ },
+ .platform_data = &vexpress_sysreg_sys_flash_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_flash_pdata),
+ }, {
+ .name = "syscon",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_MISC, 0x4),
+ },
+ .platform_data = &vexpress_sysreg_sys_misc_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_misc_pdata),
+ }, {
+ .name = "syscon",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_PROCID0, 0x8),
+ },
+ .platform_data = &vexpress_sysreg_sys_procid_pdata,
+ .pdata_size = sizeof(vexpress_sysreg_sys_procid_pdata),
+ }, {
+ .name = "vexpress-syscfg",
+ .num_resources = 1,
+ .resources = (struct resource []) {
+ DEFINE_RES_MEM(SYS_CFGDATA, 0xc),
+ },
+ }
+};
static int vexpress_sysreg_probe(struct platform_device *pdev)
{
- int err;
- struct resource *res = platform_get_resource(pdev,
- IORESOURCE_MEM, 0);
-
- if (!devm_request_mem_region(&pdev->dev, res->start,
- resource_size(res), pdev->name)) {
- dev_err(&pdev->dev, "Failed to request memory region!\n");
- return -EBUSY;
- }
+ struct resource *mem;
+ void __iomem *base;
+ struct bgpio_chip *mmc_gpio_chip;
+ u32 dt_hbi;
- if (!vexpress_sysreg_base) {
- vexpress_sysreg_base = devm_ioremap(&pdev->dev, res->start,
- resource_size(res));
- vexpress_sysreg_setup(pdev->dev.of_node);
- }
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem)
+ return -EINVAL;
- if (!vexpress_sysreg_base) {
- dev_err(&pdev->dev, "Failed to obtain base address!\n");
- return -EFAULT;
- }
+ base = devm_ioremap(&pdev->dev, mem->start, resource_size(mem));
+ if (!base)
+ return -ENOMEM;
- setup_timer(&vexpress_sysreg_config_timer,
- vexpress_sysreg_config_complete, 0);
+ vexpress_config_set_master(vexpress_sysreg_get_master());
- vexpress_sysreg_dev = &pdev->dev;
+ /* Confirm board type against DT property, if available */
+ if (of_property_read_u32(of_allnodes, "arm,hbi", &dt_hbi) == 0) {
+ u32 id = vexpress_get_procid(VEXPRESS_SITE_MASTER);
+ u32 hbi = (id >> SYS_PROCIDx_HBI_SHIFT) & SYS_HBI_MASK;
-#ifdef CONFIG_GPIOLIB
- vexpress_sysreg_gpio_chip.dev = &pdev->dev;
- err = gpiochip_add(&vexpress_sysreg_gpio_chip);
- if (err) {
- vexpress_config_bridge_unregister(
- vexpress_sysreg_config_bridge);
- dev_err(&pdev->dev, "Failed to register GPIO chip! (%d)\n",
- err);
- return err;
+ if (WARN_ON(dt_hbi != hbi))
+ dev_warn(&pdev->dev, "DT HBI (%x) is not matching hardware (%x)!\n",
+ dt_hbi, hbi);
}
- platform_device_register_data(vexpress_sysreg_dev, "leds-gpio",
- PLATFORM_DEVID_AUTO, &vexpress_sysreg_leds_pdata,
- sizeof(vexpress_sysreg_leds_pdata));
-#endif
-
- device_create_file(vexpress_sysreg_dev, &dev_attr_sys_id);
-
- return 0;
+ /*
+ * Duplicated SYS_MCI pseudo-GPIO controller for compatibility with
+ * older trees using sysreg node for MMC control lines.
+ */
+ mmc_gpio_chip = devm_kzalloc(&pdev->dev, sizeof(*mmc_gpio_chip),
+ GFP_KERNEL);
+ if (!mmc_gpio_chip)
+ return -ENOMEM;
+ bgpio_init(mmc_gpio_chip, &pdev->dev, 0x4, base + SYS_MCI,
+ NULL, NULL, NULL, NULL, 0);
+ mmc_gpio_chip->gc.ngpio = 2;
+ gpiochip_add(&mmc_gpio_chip->gc);
+
+ return mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO,
+ vexpress_sysreg_cells,
+ ARRAY_SIZE(vexpress_sysreg_cells), mem, 0, NULL);
}
static const struct of_device_id vexpress_sysreg_match[] = {
@@ -522,7 +265,12 @@ static struct platform_driver vexpress_sysreg_driver = {
static int __init vexpress_sysreg_init(void)
{
- vexpress_sysreg_of_early_init();
+ struct device_node *node;
+
+ /* Need the sysreg early, before any other device... */
+ for_each_matching_node(node, vexpress_sysreg_match)
+ of_platform_device_create(node, NULL, NULL);
+
return platform_driver_register(&vexpress_sysreg_driver);
}
core_initcall(vexpress_sysreg_init);
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 8baff0effc7d..d9663ef90ce8 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -515,6 +515,15 @@ config SRAM
the genalloc API. It is supposed to be used for small on-chip SRAM
areas found on many SoCs.
+config VEXPRESS_SYSCFG
+ bool "Versatile Express System Configuration driver"
+ depends on VEXPRESS_CONFIG
+ default y
+ help
+ ARM Ltd. Versatile Express uses specialised platform configuration
+ bus. System Configuration interface is one of the possible means
+ of generating transactions on this bus.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7eb4b69580c0..d59ce1261b38 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -55,3 +55,4 @@ obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/
obj-$(CONFIG_ECHO) += echo/
+obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o
diff --git a/drivers/misc/vexpress-syscfg.c b/drivers/misc/vexpress-syscfg.c
new file mode 100644
index 000000000000..73068e50e56d
--- /dev/null
+++ b/drivers/misc/vexpress-syscfg.c
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ *
+ * 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.
+ *
+ * Copyright (C) 2014 ARM Limited
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/syscore_ops.h>
+#include <linux/vexpress.h>
+
+
+#define SYS_CFGDATA 0x0
+
+#define SYS_CFGCTRL 0x4
+#define SYS_CFGCTRL_START (1 << 31)
+#define SYS_CFGCTRL_WRITE (1 << 30)
+#define SYS_CFGCTRL_DCC(n) (((n) & 0xf) << 26)
+#define SYS_CFGCTRL_FUNC(n) (((n) & 0x3f) << 20)
+#define SYS_CFGCTRL_SITE(n) (((n) & 0x3) << 16)
+#define SYS_CFGCTRL_POSITION(n) (((n) & 0xf) << 12)
+#define SYS_CFGCTRL_DEVICE(n) (((n) & 0xfff) << 0)
+
+#define SYS_CFGSTAT 0x8
+#define SYS_CFGSTAT_ERR (1 << 1)
+#define SYS_CFGSTAT_COMPLETE (1 << 0)
+
+
+struct vexpress_syscfg {
+ struct device *dev;
+ void __iomem *base;
+ struct list_head funcs;
+};
+
+struct vexpress_syscfg_func {
+ struct list_head list;
+ struct vexpress_syscfg *syscfg;
+ struct regmap *regmap;
+ int num_templates;
+ u32 template[0]; /* Keep it last! */
+};
+
+
+static int vexpress_syscfg_exec(struct vexpress_syscfg_func *func,
+ int index, bool write, u32 *data)
+{
+ struct vexpress_syscfg *syscfg = func->syscfg;
+ u32 command, status;
+ int tries;
+ long timeout;
+
+ if (WARN_ON(index > func->num_templates))
+ return -EINVAL;
+
+ command = readl(syscfg->base + SYS_CFGCTRL);
+ if (WARN_ON(command & SYS_CFGCTRL_START))
+ return -EBUSY;
+
+ command = func->template[index];
+ command |= SYS_CFGCTRL_START;
+ command |= write ? SYS_CFGCTRL_WRITE : 0;
+
+ /* Use a canary for reads */
+ if (!write)
+ *data = 0xdeadbeef;
+
+ dev_dbg(syscfg->dev, "func %p, command %x, data %x\n",
+ func, command, *data);
+ writel(*data, syscfg->base + SYS_CFGDATA);
+ writel(0, syscfg->base + SYS_CFGSTAT);
+ writel(command, syscfg->base + SYS_CFGCTRL);
+ mb();
+
+ /* The operation can take ages... Go to sleep, 100us initially */
+ tries = 100;
+ timeout = 100;
+ do {
+ if (!irqs_disabled()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(usecs_to_jiffies(timeout));
+ if (signal_pending(current))
+ return -EINTR;
+ } else {
+ udelay(timeout);
+ }
+
+ status = readl(syscfg->base + SYS_CFGSTAT);
+ if (status & SYS_CFGSTAT_ERR)
+ return -EFAULT;
+
+ if (timeout > 20)
+ timeout -= 20;
+ } while (--tries && !(status & SYS_CFGSTAT_COMPLETE));
+ if (WARN_ON_ONCE(!tries))
+ return -ETIMEDOUT;
+
+ if (!write) {
+ *data = readl(syscfg->base + SYS_CFGDATA);
+ dev_dbg(syscfg->dev, "func %p, read data %x\n", func, *data);
+ }
+
+ return 0;
+}
+
+static int vexpress_syscfg_read(void *context, unsigned int index,
+ unsigned int *val)
+{
+ struct vexpress_syscfg_func *func = context;
+
+ return vexpress_syscfg_exec(func, index, false, val);
+}
+
+static int vexpress_syscfg_write(void *context, unsigned int index,
+ unsigned int val)
+{
+ struct vexpress_syscfg_func *func = context;
+
+ return vexpress_syscfg_exec(func, index, true, &val);
+}
+
+struct regmap_config vexpress_syscfg_regmap_config = {
+ .lock = vexpress_config_lock,
+ .unlock = vexpress_config_unlock,
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_read = vexpress_syscfg_read,
+ .reg_write = vexpress_syscfg_write,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+
+static struct regmap *vexpress_syscfg_regmap_init(struct device *dev,
+ void *context)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct vexpress_syscfg *syscfg = context;
+ struct vexpress_syscfg_func *func;
+ struct property *prop;
+ const __be32 *val = NULL;
+ __be32 energy_quirk[4];
+ int num;
+ u32 site, position, dcc;
+ int i;
+
+ if (dev->of_node) {
+ int err = vexpress_config_get_topo(dev->of_node, &site,
+ &position, &dcc);
+
+ if (err)
+ return ERR_PTR(err);
+
+ prop = of_find_property(dev->of_node,
+ "arm,vexpress-sysreg,func", NULL);
+ if (!prop)
+ return ERR_PTR(-EINVAL);
+
+ num = prop->length / sizeof(u32) / 2;
+ val = prop->value;
+ } else {
+ if (pdev->num_resources != 1 ||
+ pdev->resource[0].flags != IORESOURCE_BUS)
+ return ERR_PTR(-EFAULT);
+
+ site = pdev->resource[0].start;
+ if (site == VEXPRESS_SITE_MASTER)
+ site = vexpress_config_get_master();
+ position = 0;
+ dcc = 0;
+ num = 1;
+ }
+
+ /*
+ * "arm,vexpress-energy" function used to be described
+ * by its first device only, now it requires both
+ */
+ if (num == 1 && of_device_is_compatible(dev->of_node,
+ "arm,vexpress-energy")) {
+ num = 2;
+ energy_quirk[0] = *val;
+ energy_quirk[2] = *val++;
+ energy_quirk[1] = *val;
+ energy_quirk[3] = cpu_to_be32(be32_to_cpup(val) + 1);
+ val = energy_quirk;
+ }
+
+ func = kzalloc(sizeof(*func) + sizeof(*func->template) * num,
+ GFP_KERNEL);
+ if (!func)
+ return NULL;
+
+ func->syscfg = syscfg;
+ func->num_templates = num;
+
+ for (i = 0; i < num; i++) {
+ u32 function, device;
+
+ if (dev->of_node) {
+ function = be32_to_cpup(val++);
+ device = be32_to_cpup(val++);
+ } else {
+ function = pdev->resource[0].end;
+ device = pdev->id;
+ }
+
+ dev_dbg(dev, "func %p: %u/%u/%u/%u/%u\n",
+ func, site, position, dcc,
+ function, device);
+
+ func->template[i] = SYS_CFGCTRL_DCC(dcc);
+ func->template[i] |= SYS_CFGCTRL_SITE(site);
+ func->template[i] |= SYS_CFGCTRL_POSITION(position);
+ func->template[i] |= SYS_CFGCTRL_FUNC(function);
+ func->template[i] |= SYS_CFGCTRL_DEVICE(device);
+ }
+
+ vexpress_syscfg_regmap_config.max_register = num - 1;
+
+ func->regmap = regmap_init(dev, NULL, func,
+ &vexpress_syscfg_regmap_config);
+
+ if (IS_ERR(func->regmap))
+ kfree(func);
+ else
+ list_add(&func->list, &syscfg->funcs);
+
+ return func->regmap;
+}
+
+static void vexpress_syscfg_regmap_exit(struct regmap *regmap, void *context)
+{
+ struct vexpress_syscfg *syscfg = context;
+ struct vexpress_syscfg_func *func, *tmp;
+
+ regmap_exit(regmap);
+
+ list_for_each_entry_safe(func, tmp, &syscfg->funcs, list) {
+ if (func->regmap == regmap) {
+ list_del(&syscfg->funcs);
+ kfree(func);
+ break;
+ }
+ }
+}
+
+static struct vexpress_config_bridge_ops vexpress_syscfg_bridge_ops = {
+ .regmap_init = vexpress_syscfg_regmap_init,
+ .regmap_exit = vexpress_syscfg_regmap_exit,
+};
+
+
+/* Non-DT hack, to be gone... */
+static struct device *vexpress_syscfg_bridge;
+
+int vexpress_syscfg_device_register(struct platform_device *pdev)
+{
+ pdev->dev.parent = vexpress_syscfg_bridge;
+
+ return platform_device_register(pdev);
+}
+
+
+int vexpress_syscfg_probe(struct platform_device *pdev)
+{
+ struct vexpress_syscfg *syscfg;
+ struct resource *res;
+ struct device *bridge;
+
+ syscfg = devm_kzalloc(&pdev->dev, sizeof(*syscfg), GFP_KERNEL);
+ if (!syscfg)
+ return -ENOMEM;
+ syscfg->dev = &pdev->dev;
+ INIT_LIST_HEAD(&syscfg->funcs);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!devm_request_mem_region(&pdev->dev, res->start,
+ resource_size(res), pdev->name))
+ return -EBUSY;
+
+ syscfg->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+ if (!syscfg->base)
+ return -EFAULT;
+
+ /* Must use dev.parent (MFD), as that's where DT phandle points at... */
+ bridge = vexpress_config_bridge_register(pdev->dev.parent,
+ &vexpress_syscfg_bridge_ops, syscfg);
+ if (IS_ERR(bridge))
+ return PTR_ERR(bridge);
+
+ /* Non-DT case */
+ if (!pdev->dev.of_node)
+ vexpress_syscfg_bridge = bridge;
+
+ return 0;
+}
+
+static const struct platform_device_id vexpress_syscfg_id_table[] = {
+ { "vexpress-syscfg", },
+ {},
+};
+
+static struct platform_driver vexpress_syscfg_driver = {
+ .driver.name = "vexpress-syscfg",
+ .id_table = vexpress_syscfg_id_table,
+ .probe = vexpress_syscfg_probe,
+};
+
+static int __init vexpress_syscfg_init(void)
+{
+ return platform_driver_register(&vexpress_syscfg_driver);
+}
+core_initcall(vexpress_syscfg_init);
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index bd47fbc53dc9..e8376d646d98 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -206,12 +206,13 @@ static struct platform_device *of_platform_device_create_pdata(
{
struct platform_device *dev;
- if (!of_device_is_available(np))
+ if (!of_device_is_available(np) ||
+ of_node_test_and_set_flag(np, OF_POPULATED))
return NULL;
dev = of_device_alloc(np, bus_id, parent);
if (!dev)
- return NULL;
+ goto err_clear_flag;
#if defined(CONFIG_MICROBLAZE)
dev->archdata.dma_mask = 0xffffffffUL;
@@ -229,10 +230,14 @@ static struct platform_device *of_platform_device_create_pdata(
if (of_device_add(dev) != 0) {
platform_device_put(dev);
- return NULL;
+ goto err_clear_flag;
}
return dev;
+
+err_clear_flag:
+ of_node_clear_flag(np, OF_POPULATED);
+ return NULL;
}
/**
@@ -264,14 +269,15 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
pr_debug("Creating amba device %s\n", node->full_name);
- if (!of_device_is_available(node))
+ if (!of_device_is_available(node) ||
+ of_node_test_and_set_flag(node, OF_POPULATED))
return NULL;
dev = amba_device_alloc(NULL, 0, 0);
if (!dev) {
pr_err("%s(): amba_device_alloc() failed for %s\n",
__func__, node->full_name);
- return NULL;
+ goto err_clear_flag;
}
/* setup generic device info */
@@ -311,6 +317,8 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
err_free:
amba_device_put(dev);
+err_clear_flag:
+ of_node_clear_flag(node, OF_POPULATED);
return NULL;
}
#else /* CONFIG_ARM_AMBA */
@@ -487,4 +495,60 @@ int of_platform_populate(struct device_node *root,
return rc;
}
EXPORT_SYMBOL_GPL(of_platform_populate);
+
+static int of_platform_device_destroy(struct device *dev, void *data)
+{
+ bool *children_left = data;
+
+ /* Do not touch devices not populated from the device tree */
+ if (!dev->of_node || !of_node_check_flag(dev->of_node, OF_POPULATED)) {
+ *children_left = true;
+ return 0;
+ }
+
+ /* Recurse, but don't touch this device if it has any children left */
+ if (of_platform_depopulate(dev) != 0) {
+ *children_left = true;
+ return 0;
+ }
+
+ if (dev->bus == &platform_bus_type)
+ platform_device_unregister(to_platform_device(dev));
+#ifdef CONFIG_ARM_AMBA
+ else if (dev->bus == &amba_bustype)
+ amba_device_unregister(to_amba_device(dev));
+#endif
+ else {
+ *children_left = true;
+ return 0;
+ }
+
+ of_node_clear_flag(dev->of_node, OF_POPULATED);
+
+ return 0;
+}
+
+/**
+ * of_platform_depopulate() - Remove devices populated from device tree
+ * @parent: device which childred will be removed
+ *
+ * Complementary to of_platform_populate(), this function removes children
+ * of the given device (and, recurrently, their children) that have been
+ * created from their respective device tree nodes (and only those,
+ * leaving others - eg. manually created - unharmed).
+ *
+ * Returns 0 when all children devices have been removed or
+ * -EBUSY when some children remained.
+ */
+int of_platform_depopulate(struct device *parent)
+{
+ bool children_left = false;
+
+ device_for_each_child(parent, &children_left,
+ of_platform_device_destroy);
+
+ return children_left ? -EBUSY : 0;
+}
+EXPORT_SYMBOL_GPL(of_platform_depopulate);
+
#endif /* CONFIG_OF_ADDRESS */
diff --git a/drivers/power/reset/vexpress-poweroff.c b/drivers/power/reset/vexpress-poweroff.c
index b95cf71ed695..4dc102e2b230 100644
--- a/drivers/power/reset/vexpress-poweroff.c
+++ b/drivers/power/reset/vexpress-poweroff.c
@@ -23,10 +23,10 @@
static void vexpress_reset_do(struct device *dev, const char *what)
{
int err = -ENOENT;
- struct vexpress_config_func *func = dev_get_drvdata(dev);
+ struct regmap *reg = dev_get_drvdata(dev);
- if (func) {
- err = vexpress_config_write(func, 0, 0);
+ if (reg) {
+ err = regmap_write(reg, 0, 0);
if (!err)
mdelay(1000);
}
@@ -91,17 +91,17 @@ static int vexpress_reset_probe(struct platform_device *pdev)
enum vexpress_reset_func func;
const struct of_device_id *match =
of_match_device(vexpress_reset_of_match, &pdev->dev);
- struct vexpress_config_func *config_func;
+ struct regmap *regmap;
if (match)
func = (enum vexpress_reset_func)match->data;
else
func = pdev->id_entry->driver_data;
- config_func = vexpress_config_func_get_by_dev(&pdev->dev);
- if (!config_func)
- return -EINVAL;
- dev_set_drvdata(&pdev->dev, config_func);
+ regmap = devm_regmap_init_vexpress_config(&pdev->dev);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+ dev_set_drvdata(&pdev->dev, regmap);
switch (func) {
case FUNC_SHUTDOWN:
diff --git a/drivers/regulator/vexpress.c b/drivers/regulator/vexpress.c
index f3ae28a7e663..2863428813e4 100644
--- a/drivers/regulator/vexpress.c
+++ b/drivers/regulator/vexpress.c
@@ -26,14 +26,14 @@
struct vexpress_regulator {
struct regulator_desc desc;
struct regulator_dev *regdev;
- struct vexpress_config_func *func;
+ struct regmap *regmap;
};
static int vexpress_regulator_get_voltage(struct regulator_dev *regdev)
{
struct vexpress_regulator *reg = rdev_get_drvdata(regdev);
u32 uV;
- int err = vexpress_config_read(reg->func, 0, &uV);
+ int err = regmap_read(reg->regmap, 0, &uV);
return err ? err : uV;
}
@@ -43,7 +43,7 @@ static int vexpress_regulator_set_voltage(struct regulator_dev *regdev,
{
struct vexpress_regulator *reg = rdev_get_drvdata(regdev);
- return vexpress_config_write(reg->func, 0, min_uV);
+ return regmap_write(reg->regmap, 0, min_uV);
}
static struct regulator_ops vexpress_regulator_ops_ro = {
@@ -57,22 +57,17 @@ static struct regulator_ops vexpress_regulator_ops = {
static int vexpress_regulator_probe(struct platform_device *pdev)
{
- int err;
struct vexpress_regulator *reg;
struct regulator_init_data *init_data;
struct regulator_config config = { };
reg = devm_kzalloc(&pdev->dev, sizeof(*reg), GFP_KERNEL);
- if (!reg) {
- err = -ENOMEM;
- goto error_kzalloc;
- }
+ if (!reg)
+ return -ENOMEM;
- reg->func = vexpress_config_func_get_by_dev(&pdev->dev);
- if (!reg->func) {
- err = -ENXIO;
- goto error_get_func;
- }
+ reg->regmap = devm_regmap_init_vexpress_config(&pdev->dev);
+ if (IS_ERR(reg->regmap))
+ return PTR_ERR(reg->regmap);
reg->desc.name = dev_name(&pdev->dev);
reg->desc.type = REGULATOR_VOLTAGE;
@@ -80,10 +75,8 @@ static int vexpress_regulator_probe(struct platform_device *pdev)
reg->desc.continuous_voltage_range = true;
init_data = of_get_regulator_init_data(&pdev->dev, pdev->dev.of_node);
- if (!init_data) {
- err = -EINVAL;
- goto error_get_regulator_init_data;
- }
+ if (!init_data)
+ return -EINVAL;
init_data->constraints.apply_uV = 0;
if (init_data->constraints.min_uV && init_data->constraints.max_uV)
@@ -97,30 +90,12 @@ static int vexpress_regulator_probe(struct platform_device *pdev)
config.of_node = pdev->dev.of_node;
reg->regdev = devm_regulator_register(&pdev->dev, &reg->desc, &config);
- if (IS_ERR(reg->regdev)) {
- err = PTR_ERR(reg->regdev);
- goto error_regulator_register;
- }
+ if (IS_ERR(reg->regdev))
+ return PTR_ERR(reg->regdev);
platform_set_drvdata(pdev, reg);
return 0;
-
-error_regulator_register:
-error_get_regulator_init_data:
- vexpress_config_func_put(reg->func);
-error_get_func:
-error_kzalloc:
- return err;
-}
-
-static int vexpress_regulator_remove(struct platform_device *pdev)
-{
- struct vexpress_regulator *reg = platform_get_drvdata(pdev);
-
- vexpress_config_func_put(reg->func);
-
- return 0;
}
static struct of_device_id vexpress_regulator_of_match[] = {
@@ -130,7 +105,6 @@ static struct of_device_id vexpress_regulator_of_match[] = {
static struct platform_driver vexpress_regulator_driver = {
.probe = vexpress_regulator_probe,
- .remove = vexpress_regulator_remove,
.driver = {
.name = DRVNAME,
.owner = THIS_MODULE,