summaryrefslogtreecommitdiffstats
path: root/drivers/clk
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2014-06-02 16:15:12 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2014-06-02 16:15:12 -0700
commit825f4e0271b0de3f7f31d963dcdaa0056fe9b73a (patch)
treeaef1f198da011a96fefbe9851137ca17afd929a4 /drivers/clk
parent0a58471541cc823ef8056d23945c39fec154481c (diff)
parentb5b9324a6296bd0176fe1f8e06a1220207bd1bd3 (diff)
Merge tag 'soc-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc into next
Pull part one of ARM SoC updates from Olof Johansson: "A quite large set of SoC updates this cycle. In no particular order: - Multi-cluster power management for Samsung Exynos, adding support for big.LITTLE CPU switching on EXYNOS5 - SMP support for Marvell Armada 375 and 38x - SMP rework on Allwinner A31 - Xilinx Zynq support for SOC_BUS, big endian - Marvell orion5x platform cleanup, modernizing the implementation and moving to DT. - _Finally_ moving Samsung Exynos over to support MULTIPLATFORM, so that their platform can be enabled in the same kernel binary as most of the other v7 platforms in the tree. \o/ The work isn't quite complete, there's some driver fixes still needed, but the basics now work. New SoC support added: - Freescale i.MX6SX - LSI Axxia AXM55xx SoCs - Samsung EXYNOS 3250, 5260, 5410, 5420 and 5800 - STi STIH407 plus a large set of various smaller updates for different platforms. I'm probably missing some important one here" * tag 'soc-for-3.16' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (281 commits) ARM: exynos: don't run exynos4 l2x0 setup on other platforms ARM: exynos: Fix "allmodconfig" build errors in mcpm and hotplug ARM: EXYNOS: mcpm rename the power_down_finish ARM: EXYNOS: Enable mcpm for dual-cluster exynos5800 SoC ARM: EXYNOS: Enable multi-platform build support ARM: EXYNOS: Consolidate Kconfig entries ARM: EXYNOS: Add support for EXYNOS5410 SoC ARM: EXYNOS: Support secondary CPU boot of Exynos3250 ARM: EXYNOS: Add Exynos3250 SoC ID ARM: EXYNOS: Add 5800 SoC support ARM: EXYNOS: initial board support for exynos5260 SoC clk: exynos5410: register clocks using common clock framework ARM: debug: qcom: add UART addresses to Kconfig help for APQ8084 ARM: sunxi: allow building without reset controller Documentation: devicetree: arm: sort enable-method entries ARM: rockchip: convert smp bringup to CPU_METHOD_OF_DECLARE clk: exynos5250: Add missing sysmmu clocks for DISP and ISP blocks ARM: dts: axxia: Add reset controller power: reset: Add Axxia system reset driver ARM: axxia: Adding defconfig for AXM55xx ...
Diffstat (limited to 'drivers/clk')
-rw-r--r--drivers/clk/Kconfig2
-rw-r--r--drivers/clk/Makefile2
-rw-r--r--drivers/clk/at91/Makefile4
-rw-r--r--drivers/clk/at91/clk-main.c577
-rw-r--r--drivers/clk/at91/clk-slow.c467
-rw-r--r--drivers/clk/at91/pmc.c17
-rw-r--r--drivers/clk/at91/pmc.h9
-rw-r--r--drivers/clk/at91/sckc.c57
-rw-r--r--drivers/clk/at91/sckc.h22
-rw-r--r--drivers/clk/samsung/Kconfig26
-rw-r--r--drivers/clk/samsung/Makefile3
-rw-r--r--drivers/clk/samsung/clk-exynos3250.c780
-rw-r--r--drivers/clk/samsung/clk-exynos4.c51
-rw-r--r--drivers/clk/samsung/clk-exynos5250.c76
-rw-r--r--drivers/clk/samsung/clk-exynos5260.c1980
-rw-r--r--drivers/clk/samsung/clk-exynos5260.h459
-rw-r--r--drivers/clk/samsung/clk-exynos5410.c209
-rw-r--r--drivers/clk/samsung/clk-exynos5420.c1127
-rw-r--r--drivers/clk/samsung/clk-exynos5440.c18
-rw-r--r--drivers/clk/samsung/clk-pll.c223
-rw-r--r--drivers/clk/samsung/clk-pll.h2
-rw-r--r--drivers/clk/samsung/clk-s3c2410.c51
-rw-r--r--drivers/clk/samsung/clk-s3c2412.c29
-rw-r--r--drivers/clk/samsung/clk-s3c2443.c46
-rw-r--r--drivers/clk/samsung/clk-s3c64xx.c44
-rw-r--r--drivers/clk/samsung/clk.c123
-rw-r--r--drivers/clk/samsung/clk.h72
27 files changed, 5862 insertions, 614 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 4fdfd6c70bd3..3a2196481b11 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -108,3 +108,5 @@ endmenu
source "drivers/clk/bcm/Kconfig"
source "drivers/clk/mvebu/Kconfig"
+
+source "drivers/clk/samsung/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5f8a28735c96..17d7f13d19a5 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -41,7 +41,7 @@ obj-$(CONFIG_PLAT_ORION) += mvebu/
obj-$(CONFIG_ARCH_MXS) += mxs/
obj-$(CONFIG_COMMON_CLK_QCOM) += qcom/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
-obj-$(CONFIG_PLAT_SAMSUNG) += samsung/
+obj-$(CONFIG_COMMON_CLK_SAMSUNG) += samsung/
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += shmobile/
obj-$(CONFIG_ARCH_SIRF) += sirf/
obj-$(CONFIG_ARCH_SOCFPGA) += socfpga/
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 46c1d3d0d66b..4998aee59267 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -2,8 +2,8 @@
# Makefile for at91 specific clk
#
-obj-y += pmc.o
-obj-y += clk-main.o clk-pll.o clk-plldiv.o clk-master.o
+obj-y += pmc.o sckc.o
+obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
obj-y += clk-system.o clk-peripheral.o clk-programmable.o
obj-$(CONFIG_HAVE_AT91_UTMI) += clk-utmi.o
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
index 8e9e8cc0412d..733306131b99 100644
--- a/drivers/clk/at91/clk-main.c
+++ b/drivers/clk/at91/clk-main.c
@@ -30,99 +30,546 @@
#define MAINF_LOOP_MIN_WAIT (USEC_PER_SEC / SLOW_CLOCK_FREQ)
#define MAINF_LOOP_MAX_WAIT MAINFRDY_TIMEOUT
-struct clk_main {
+#define MOR_KEY_MASK (0xff << 16)
+
+struct clk_main_osc {
struct clk_hw hw;
struct at91_pmc *pmc;
- unsigned long rate;
unsigned int irq;
wait_queue_head_t wait;
};
-#define to_clk_main(hw) container_of(hw, struct clk_main, hw)
+#define to_clk_main_osc(hw) container_of(hw, struct clk_main_osc, hw)
+
+struct clk_main_rc_osc {
+ struct clk_hw hw;
+ struct at91_pmc *pmc;
+ unsigned int irq;
+ wait_queue_head_t wait;
+ unsigned long frequency;
+ unsigned long accuracy;
+};
+
+#define to_clk_main_rc_osc(hw) container_of(hw, struct clk_main_rc_osc, hw)
+
+struct clk_rm9200_main {
+ struct clk_hw hw;
+ struct at91_pmc *pmc;
+};
+
+#define to_clk_rm9200_main(hw) container_of(hw, struct clk_rm9200_main, hw)
-static irqreturn_t clk_main_irq_handler(int irq, void *dev_id)
+struct clk_sam9x5_main {
+ struct clk_hw hw;
+ struct at91_pmc *pmc;
+ unsigned int irq;
+ wait_queue_head_t wait;
+ u8 parent;
+};
+
+#define to_clk_sam9x5_main(hw) container_of(hw, struct clk_sam9x5_main, hw)
+
+static irqreturn_t clk_main_osc_irq_handler(int irq, void *dev_id)
{
- struct clk_main *clkmain = (struct clk_main *)dev_id;
+ struct clk_main_osc *osc = dev_id;
- wake_up(&clkmain->wait);
- disable_irq_nosync(clkmain->irq);
+ wake_up(&osc->wait);
+ disable_irq_nosync(osc->irq);
return IRQ_HANDLED;
}
-static int clk_main_prepare(struct clk_hw *hw)
+static int clk_main_osc_prepare(struct clk_hw *hw)
{
- struct clk_main *clkmain = to_clk_main(hw);
- struct at91_pmc *pmc = clkmain->pmc;
- unsigned long halt_time, timeout;
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
+ struct at91_pmc *pmc = osc->pmc;
u32 tmp;
+ tmp = pmc_read(pmc, AT91_CKGR_MOR) & ~MOR_KEY_MASK;
+ if (tmp & AT91_PMC_OSCBYPASS)
+ return 0;
+
+ if (!(tmp & AT91_PMC_MOSCEN)) {
+ tmp |= AT91_PMC_MOSCEN | AT91_PMC_KEY;
+ pmc_write(pmc, AT91_CKGR_MOR, tmp);
+ }
+
while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS)) {
- enable_irq(clkmain->irq);
- wait_event(clkmain->wait,
+ enable_irq(osc->irq);
+ wait_event(osc->wait,
pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS);
}
- if (clkmain->rate)
- return 0;
+ return 0;
+}
+
+static void clk_main_osc_unprepare(struct clk_hw *hw)
+{
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
+ struct at91_pmc *pmc = osc->pmc;
+ u32 tmp = pmc_read(pmc, AT91_CKGR_MOR);
+
+ if (tmp & AT91_PMC_OSCBYPASS)
+ return;
+
+ if (!(tmp & AT91_PMC_MOSCEN))
+ return;
+
+ tmp &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN);
+ pmc_write(pmc, AT91_CKGR_MOR, tmp | AT91_PMC_KEY);
+}
+
+static int clk_main_osc_is_prepared(struct clk_hw *hw)
+{
+ struct clk_main_osc *osc = to_clk_main_osc(hw);
+ struct at91_pmc *pmc = osc->pmc;
+ u32 tmp = pmc_read(pmc, AT91_CKGR_MOR);
+
+ if (tmp & AT91_PMC_OSCBYPASS)
+ return 1;
+
+ return !!((pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCS) &&
+ (pmc_read(pmc, AT91_CKGR_MOR) & AT91_PMC_MOSCEN));
+}
+
+static const struct clk_ops main_osc_ops = {
+ .prepare = clk_main_osc_prepare,
+ .unprepare = clk_main_osc_unprepare,
+ .is_prepared = clk_main_osc_is_prepared,
+};
+
+static struct clk * __init
+at91_clk_register_main_osc(struct at91_pmc *pmc,
+ unsigned int irq,
+ const char *name,
+ const char *parent_name,
+ bool bypass)
+{
+ int ret;
+ struct clk_main_osc *osc;
+ struct clk *clk = NULL;
+ struct clk_init_data init;
+
+ if (!pmc || !irq || !name || !parent_name)
+ return ERR_PTR(-EINVAL);
+
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &main_osc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ osc->hw.init = &init;
+ osc->pmc = pmc;
+ osc->irq = irq;
+
+ init_waitqueue_head(&osc->wait);
+ irq_set_status_flags(osc->irq, IRQ_NOAUTOEN);
+ ret = request_irq(osc->irq, clk_main_osc_irq_handler,
+ IRQF_TRIGGER_HIGH, name, osc);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (bypass)
+ pmc_write(pmc, AT91_CKGR_MOR,
+ (pmc_read(pmc, AT91_CKGR_MOR) &
+ ~(MOR_KEY_MASK | AT91_PMC_MOSCEN)) |
+ AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
+
+ clk = clk_register(NULL, &osc->hw);
+ if (IS_ERR(clk)) {
+ free_irq(irq, osc);
+ kfree(osc);
+ }
+
+ return clk;
+}
+
+void __init of_at91rm9200_clk_main_osc_setup(struct device_node *np,
+ struct at91_pmc *pmc)
+{
+ struct clk *clk;
+ unsigned int irq;
+ const char *name = np->name;
+ const char *parent_name;
+ bool bypass;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+ parent_name = of_clk_get_parent_name(np, 0);
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq)
+ return;
+
+ clk = at91_clk_register_main_osc(pmc, irq, name, parent_name, bypass);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static irqreturn_t clk_main_rc_osc_irq_handler(int irq, void *dev_id)
+{
+ struct clk_main_rc_osc *osc = dev_id;
+
+ wake_up(&osc->wait);
+ disable_irq_nosync(osc->irq);
+
+ return IRQ_HANDLED;
+}
+
+static int clk_main_rc_osc_prepare(struct clk_hw *hw)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
+ struct at91_pmc *pmc = osc->pmc;
+ u32 tmp;
+
+ tmp = pmc_read(pmc, AT91_CKGR_MOR) & ~MOR_KEY_MASK;
+
+ if (!(tmp & AT91_PMC_MOSCRCEN)) {
+ tmp |= AT91_PMC_MOSCRCEN | AT91_PMC_KEY;
+ pmc_write(pmc, AT91_CKGR_MOR, tmp);
+ }
+
+ while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCRCS)) {
+ enable_irq(osc->irq);
+ wait_event(osc->wait,
+ pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCRCS);
+ }
+
+ return 0;
+}
+
+static void clk_main_rc_osc_unprepare(struct clk_hw *hw)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
+ struct at91_pmc *pmc = osc->pmc;
+ u32 tmp = pmc_read(pmc, AT91_CKGR_MOR);
+
+ if (!(tmp & AT91_PMC_MOSCRCEN))
+ return;
+
+ tmp &= ~(MOR_KEY_MASK | AT91_PMC_MOSCRCEN);
+ pmc_write(pmc, AT91_CKGR_MOR, tmp | AT91_PMC_KEY);
+}
+
+static int clk_main_rc_osc_is_prepared(struct clk_hw *hw)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
+ struct at91_pmc *pmc = osc->pmc;
+
+ return !!((pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCRCS) &&
+ (pmc_read(pmc, AT91_CKGR_MOR) & AT91_PMC_MOSCRCEN));
+}
+
+static unsigned long clk_main_rc_osc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
+
+ return osc->frequency;
+}
+
+static unsigned long clk_main_rc_osc_recalc_accuracy(struct clk_hw *hw,
+ unsigned long parent_acc)
+{
+ struct clk_main_rc_osc *osc = to_clk_main_rc_osc(hw);
+
+ return osc->accuracy;
+}
+
+static const struct clk_ops main_rc_osc_ops = {
+ .prepare = clk_main_rc_osc_prepare,
+ .unprepare = clk_main_rc_osc_unprepare,
+ .is_prepared = clk_main_rc_osc_is_prepared,
+ .recalc_rate = clk_main_rc_osc_recalc_rate,
+ .recalc_accuracy = clk_main_rc_osc_recalc_accuracy,
+};
+
+static struct clk * __init
+at91_clk_register_main_rc_osc(struct at91_pmc *pmc,
+ unsigned int irq,
+ const char *name,
+ u32 frequency, u32 accuracy)
+{
+ int ret;
+ struct clk_main_rc_osc *osc;
+ struct clk *clk = NULL;
+ struct clk_init_data init;
+
+ if (!pmc || !irq || !name || !frequency)
+ return ERR_PTR(-EINVAL);
+
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &main_rc_osc_ops;
+ init.parent_names = NULL;
+ init.num_parents = 0;
+ init.flags = CLK_IS_ROOT | CLK_IGNORE_UNUSED;
+
+ osc->hw.init = &init;
+ osc->pmc = pmc;
+ osc->irq = irq;
+ osc->frequency = frequency;
+ osc->accuracy = accuracy;
+
+ init_waitqueue_head(&osc->wait);
+ irq_set_status_flags(osc->irq, IRQ_NOAUTOEN);
+ ret = request_irq(osc->irq, clk_main_rc_osc_irq_handler,
+ IRQF_TRIGGER_HIGH, name, osc);
+ if (ret)
+ return ERR_PTR(ret);
+
+ clk = clk_register(NULL, &osc->hw);
+ if (IS_ERR(clk)) {
+ free_irq(irq, osc);
+ kfree(osc);
+ }
+
+ return clk;
+}
+
+void __init of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np,
+ struct at91_pmc *pmc)
+{
+ struct clk *clk;
+ unsigned int irq;
+ u32 frequency = 0;
+ u32 accuracy = 0;
+ const char *name = np->name;
+
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "clock-frequency", &frequency);
+ of_property_read_u32(np, "clock-accuracy", &accuracy);
+
+ irq = irq_of_parse_and_map(np, 0);
+ if (!irq)
+ return;
+
+ clk = at91_clk_register_main_rc_osc(pmc, irq, name, frequency,
+ accuracy);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+
+static int clk_main_probe_frequency(struct at91_pmc *pmc)
+{
+ unsigned long prep_time, timeout;
+ u32 tmp;
timeout = jiffies + usecs_to_jiffies(MAINFRDY_TIMEOUT);
do {
- halt_time = jiffies;
+ prep_time = jiffies;
tmp = pmc_read(pmc, AT91_CKGR_MCFR);
if (tmp & AT91_PMC_MAINRDY)
return 0;
usleep_range(MAINF_LOOP_MIN_WAIT, MAINF_LOOP_MAX_WAIT);
- } while (time_before(halt_time, timeout));
+ } while (time_before(prep_time, timeout));
- return 0;
+ return -ETIMEDOUT;
}
-static int clk_main_is_prepared(struct clk_hw *hw)
+static unsigned long clk_main_recalc_rate(struct at91_pmc *pmc,
+ unsigned long parent_rate)
{
- struct clk_main *clkmain = to_clk_main(hw);
+ u32 tmp;
+
+ if (parent_rate)
+ return parent_rate;
+
+ tmp = pmc_read(pmc, AT91_CKGR_MCFR);
+ if (!(tmp & AT91_PMC_MAINRDY))
+ return 0;
- return !!(pmc_read(clkmain->pmc, AT91_PMC_SR) & AT91_PMC_MOSCS);
+ return ((tmp & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV;
}
-static unsigned long clk_main_recalc_rate(struct clk_hw *hw,
- unsigned long parent_rate)
+static int clk_rm9200_main_prepare(struct clk_hw *hw)
{
- u32 tmp;
- struct clk_main *clkmain = to_clk_main(hw);
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
+
+ return clk_main_probe_frequency(clkmain->pmc);
+}
+
+static int clk_rm9200_main_is_prepared(struct clk_hw *hw)
+{
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
+
+ return !!(pmc_read(clkmain->pmc, AT91_CKGR_MCFR) & AT91_PMC_MAINRDY);
+}
+
+static unsigned long clk_rm9200_main_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_rm9200_main *clkmain = to_clk_rm9200_main(hw);
+
+ return clk_main_recalc_rate(clkmain->pmc, parent_rate);
+}
+
+static const struct clk_ops rm9200_main_ops = {
+ .prepare = clk_rm9200_main_prepare,
+ .is_prepared = clk_rm9200_main_is_prepared,
+ .recalc_rate = clk_rm9200_main_recalc_rate,
+};
+
+static struct clk * __init
+at91_clk_register_rm9200_main(struct at91_pmc *pmc,
+ const char *name,
+ const char *parent_name)
+{
+ struct clk_rm9200_main *clkmain;
+ struct clk *clk = NULL;
+ struct clk_init_data init;
+
+ if (!pmc || !name)
+ return ERR_PTR(-EINVAL);
+
+ if (!parent_name)
+ return ERR_PTR(-EINVAL);
+
+ clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
+ if (!clkmain)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &rm9200_main_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = 0;
+
+ clkmain->hw.init = &init;
+ clkmain->pmc = pmc;
+
+ clk = clk_register(NULL, &clkmain->hw);
+ if (IS_ERR(clk))
+ kfree(clkmain);
+
+ return clk;
+}
+
+void __init of_at91rm9200_clk_main_setup(struct device_node *np,
+ struct at91_pmc *pmc)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ of_property_read_string(np, "clock-output-names", &name);
+
+ clk = at91_clk_register_rm9200_main(pmc, name, parent_name);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static irqreturn_t clk_sam9x5_main_irq_handler(int irq, void *dev_id)
+{
+ struct clk_sam9x5_main *clkmain = dev_id;
+
+ wake_up(&clkmain->wait);
+ disable_irq_nosync(clkmain->irq);
+
+ return IRQ_HANDLED;
+}
+
+static int clk_sam9x5_main_prepare(struct clk_hw *hw)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
struct at91_pmc *pmc = clkmain->pmc;
- if (clkmain->rate)
- return clkmain->rate;
+ while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCSELS)) {
+ enable_irq(clkmain->irq);
+ wait_event(clkmain->wait,
+ pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCSELS);
+ }
+
+ return clk_main_probe_frequency(pmc);
+}
- tmp = pmc_read(pmc, AT91_CKGR_MCFR) & AT91_PMC_MAINF;
- clkmain->rate = (tmp * parent_rate) / MAINF_DIV;
+static int clk_sam9x5_main_is_prepared(struct clk_hw *hw)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
- return clkmain->rate;
+ return !!(pmc_read(clkmain->pmc, AT91_PMC_SR) & AT91_PMC_MOSCSELS);
}
-static const struct clk_ops main_ops = {
- .prepare = clk_main_prepare,
- .is_prepared = clk_main_is_prepared,
- .recalc_rate = clk_main_recalc_rate,
+static unsigned long clk_sam9x5_main_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
+
+ return clk_main_recalc_rate(clkmain->pmc, parent_rate);
+}
+
+static int clk_sam9x5_main_set_parent(struct clk_hw *hw, u8 index)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
+ struct at91_pmc *pmc = clkmain->pmc;
+ u32 tmp;
+
+ if (index > 1)
+ return -EINVAL;
+
+ tmp = pmc_read(pmc, AT91_CKGR_MOR) & ~MOR_KEY_MASK;
+
+ if (index && !(tmp & AT91_PMC_MOSCSEL))
+ pmc_write(pmc, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+ else if (!index && (tmp & AT91_PMC_MOSCSEL))
+ pmc_write(pmc, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+
+ while (!(pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCSELS)) {
+ enable_irq(clkmain->irq);
+ wait_event(clkmain->wait,
+ pmc_read(pmc, AT91_PMC_SR) & AT91_PMC_MOSCSELS);
+ }
+
+ return 0;
+}
+
+static u8 clk_sam9x5_main_get_parent(struct clk_hw *hw)
+{
+ struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(hw);
+
+ return !!(pmc_read(clkmain->pmc, AT91_CKGR_MOR) & AT91_PMC_MOSCEN);
+}
+
+static const struct clk_ops sam9x5_main_ops = {
+ .prepare = clk_sam9x5_main_prepare,
+ .is_prepared = clk_sam9x5_main_is_prepared,
+ .recalc_rate = clk_sam9x5_main_recalc_rate,
+ .set_parent = clk_sam9x5_main_set_parent,
+ .get_parent = clk_sam9x5_main_get_parent,
};
static struct clk * __init
-at91_clk_register_main(struct at91_pmc *pmc,
- unsigned int irq,
- const char *name,
- const char *parent_name,
- unsigned long rate)
+at91_clk_register_sam9x5_main(struct at91_pmc *pmc,
+ unsigned int irq,
+ const char *name,
+ const char **parent_names,
+ int num_parents)
{
int ret;
- struct clk_main *clkmain;
+ struct clk_sam9x5_main *clkmain;
struct clk *clk = NULL;
struct clk_init_data init;
if (!pmc || !irq || !name)
return ERR_PTR(-EINVAL);
- if (!rate && !parent_name)
+ if (!parent_names || !num_parents)
return ERR_PTR(-EINVAL);
clkmain = kzalloc(sizeof(*clkmain), GFP_KERNEL);
@@ -130,19 +577,20 @@ at91_clk_register_main(struct at91_pmc *pmc,
return ERR_PTR(-ENOMEM);
init.name = name;
- init.ops = &main_ops;
- init.parent_names = parent_name ? &parent_name : NULL;
- init.num_parents = parent_name ? 1 : 0;
- init.flags = parent_name ? 0 : CLK_IS_ROOT;
+ init.ops = &sam9x5_main_ops;
+ init.parent_names = parent_names;
+ init.num_parents = num_parents;
+ init.flags = CLK_SET_PARENT_GATE;
clkmain->hw.init = &init;
- clkmain->rate = rate;
clkmain->pmc = pmc;
clkmain->irq = irq;
+ clkmain->parent = !!(pmc_read(clkmain->pmc, AT91_CKGR_MOR) &
+ AT91_PMC_MOSCEN);
init_waitqueue_head(&clkmain->wait);
irq_set_status_flags(clkmain->irq, IRQ_NOAUTOEN);
- ret = request_irq(clkmain->irq, clk_main_irq_handler,
- IRQF_TRIGGER_HIGH, "clk-main", clkmain);
+ ret = request_irq(clkmain->irq, clk_sam9x5_main_irq_handler,
+ IRQF_TRIGGER_HIGH, name, clkmain);
if (ret)
return ERR_PTR(ret);
@@ -155,33 +603,36 @@ at91_clk_register_main(struct at91_pmc *pmc,
return clk;
}
-
-
-static void __init
-of_at91_clk_main_setup(struct device_node *np, struct at91_pmc *pmc)
+void __init of_at91sam9x5_clk_main_setup(struct device_node *np,
+ struct at91_pmc *pmc)
{
struct clk *clk;
+ const char *parent_names[2];
+ int num_parents;
unsigned int irq;
- const char *parent_name;
const char *name = np->name;
- u32 rate = 0;
+ int i;
+
+ num_parents = of_count_phandle_with_args(np, "clocks", "#clock-cells");
+ if (num_parents <= 0 || num_parents > 2)
+ return;
+
+ for (i = 0; i < num_parents; ++i) {
+ parent_names[i] = of_clk_get_parent_name(np, i);
+ if (!parent_names[i])
+ return;
+ }
- parent_name = of_clk_get_parent_name(np, 0);
of_property_read_string(np, "clock-output-names", &name);
- of_property_read_u32(np, "clock-frequency", &rate);
+
irq = irq_of_parse_and_map(np, 0);
if (!irq)
return;
- clk = at91_clk_register_main(pmc, irq, name, parent_name, rate);
+ clk = at91_clk_register_sam9x5_main(pmc, irq, name, parent_names,
+ num_parents);
if (IS_ERR(clk))
return;
of_clk_add_provider(np, of_clk_src_simple_get, clk);
}
-
-void __init of_at91rm9200_clk_main_setup(struct device_node *np,
- struct at91_pmc *pmc)
-{
- of_at91_clk_main_setup(np, pmc);
-}
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
new file mode 100644
index 000000000000..0300c46ee247
--- /dev/null
+++ b/drivers/clk/at91/clk-slow.c
@@ -0,0 +1,467 @@
+/*
+ * drivers/clk/at91/clk-slow.c
+ *
+ * Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+
+#include "pmc.h"
+#include "sckc.h"
+
+#define SLOW_CLOCK_FREQ 32768
+#define SLOWCK_SW_CYCLES 5
+#define SLOWCK_SW_TIME_USEC ((SLOWCK_SW_CYCLES * USEC_PER_SEC) / \
+ SLOW_CLOCK_FREQ)
+
+#define AT91_SCKC_CR 0x00
+#define AT91_SCKC_RCEN (1 << 0)
+#define AT91_SCKC_OSC32EN (1 << 1)
+#define AT91_SCKC_OSC32BYP (1 << 2)
+#define AT91_SCKC_OSCSEL (1 << 3)
+
+struct clk_slow_osc {
+ struct clk_hw hw;
+ void __iomem *sckcr;
+ unsigned long startup_usec;
+};
+
+#define to_clk_slow_osc(hw) container_of(hw, struct clk_slow_osc, hw)
+
+struct clk_slow_rc_osc {
+ struct clk_hw hw;
+ void __iomem *sckcr;
+ unsigned long frequency;
+ unsigned long accuracy;
+ unsigned long startup_usec;
+};
+
+#define to_clk_slow_rc_osc(hw) container_of(hw, struct clk_slow_rc_osc, hw)
+
+struct clk_sam9260_slow {
+ struct clk_hw hw;
+ struct at91_pmc *pmc;
+};
+
+#define to_clk_sam9260_slow(hw) container_of(hw, struct clk_sam9260_slow, hw)
+
+struct clk_sam9x5_slow {
+ struct clk_hw hw;
+ void __iomem *sckcr;
+ u8 parent;
+};
+
+#define to_clk_sam9x5_slow(hw) container_of(hw, struct clk_sam9x5_slow, hw)
+
+
+static int clk_slow_osc_prepare(struct clk_hw *hw)
+{
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
+ void __iomem *sckcr = osc->sckcr;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & AT91_SCKC_OSC32BYP)
+ return 0;
+
+ writel(tmp | AT91_SCKC_OSC32EN, sckcr);
+
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
+
+ return 0;
+}
+
+static void clk_slow_osc_unprepare(struct clk_hw *hw)
+{
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
+ void __iomem *sckcr = osc->sckcr;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & AT91_SCKC_OSC32BYP)
+ return;
+
+ writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
+}
+
+static int clk_slow_osc_is_prepared(struct clk_hw *hw)
+{
+ struct clk_slow_osc *osc = to_clk_slow_osc(hw);
+ void __iomem *sckcr = osc->sckcr;
+ u32 tmp = readl(sckcr);
+
+ if (tmp & AT91_SCKC_OSC32BYP)
+ return 1;
+
+ return !!(tmp & AT91_SCKC_OSC32EN);
+}
+
+static const struct clk_ops slow_osc_ops = {
+ .prepare = clk_slow_osc_prepare,
+ .unprepare = clk_slow_osc_unprepare,
+ .is_prepared = clk_slow_osc_is_prepared,
+};
+
+static struct clk * __init
+at91_clk_register_slow_osc(void __iomem *sckcr,
+ const char *name,
+ const char *parent_name,
+ unsigned long startup,
+ bool bypass)
+{
+ struct clk_slow_osc *osc;
+ struct clk *clk = NULL;
+ struct clk_init_data init;
+
+ if (!sckcr || !name || !parent_name)
+ return ERR_PTR(-EINVAL);
+
+ osc = kzalloc(sizeof(*osc), GFP_KERNEL);
+ if (!osc)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = name;
+ init.ops = &slow_osc_ops;
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+ init.flags = CLK_IGNORE_UNUSED;
+
+ osc->hw.init = &init;
+ osc->sckcr = sckcr;
+ osc->startup_usec = startup;
+
+ if (bypass)
+ writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
+ sckcr);
+
+ clk = clk_register(NULL, &osc->hw);
+ if (IS_ERR(clk))
+ kfree(osc);
+
+ return clk;
+}
+
+void __init of_at91sam9x5_clk_slow_osc_setup(struct device_node *np,
+ void __iomem *sckcr)
+{
+ struct clk *clk;
+ const char *parent_name;
+ const char *name = np->name;
+ u32 startup;
+ bool bypass;
+
+ parent_name = of_clk_get_parent_name(np, 0);
+ of_property_read_string(np, "clock-output-names", &name);
+ of_property_read_u32(np, "atmel,startup-time-usec", &startup);
+ bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+ clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
+ bypass);
+ if (IS_ERR(clk))
+ return;
+
+ of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static unsigned long clk_slow_rc_osc_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
+
+ return osc->frequency;
+}
+
+static unsigned long clk_slow_rc_osc_recalc_accuracy(struct clk_hw *hw,
+ unsigned long parent_acc)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
+
+ return osc->accuracy;
+}
+
+static int clk_slow_rc_osc_prepare(struct clk_hw *hw)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);
+ void __iomem *sckcr = osc->sckcr;
+
+ writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
+
+ usleep_range(osc->startup_usec, osc->startup_usec + 1);
+
+ return 0;
+}
+
+static void clk_slow_rc_osc_unprepare(struct clk_hw *hw)
+{
+ struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(hw);