1*af873fceSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23b01f87bSUlf Hansson /*
33b01f87bSUlf Hansson * PRCC clock implementation for ux500 platform.
43b01f87bSUlf Hansson *
53b01f87bSUlf Hansson * Copyright (C) 2012 ST-Ericsson SA
63b01f87bSUlf Hansson * Author: Ulf Hansson <ulf.hansson@linaro.org>
73b01f87bSUlf Hansson */
83b01f87bSUlf Hansson
93b01f87bSUlf Hansson #include <linux/clk-provider.h>
103b01f87bSUlf Hansson #include <linux/slab.h>
113b01f87bSUlf Hansson #include <linux/io.h>
123b01f87bSUlf Hansson #include <linux/err.h>
133b01f87bSUlf Hansson #include <linux/types.h>
143b01f87bSUlf Hansson
153b01f87bSUlf Hansson #include "clk.h"
163b01f87bSUlf Hansson
173b01f87bSUlf Hansson #define PRCC_PCKEN 0x000
183b01f87bSUlf Hansson #define PRCC_PCKDIS 0x004
193b01f87bSUlf Hansson #define PRCC_KCKEN 0x008
203b01f87bSUlf Hansson #define PRCC_KCKDIS 0x00C
213b01f87bSUlf Hansson #define PRCC_PCKSR 0x010
223b01f87bSUlf Hansson #define PRCC_KCKSR 0x014
233b01f87bSUlf Hansson
243b01f87bSUlf Hansson #define to_clk_prcc(_hw) container_of(_hw, struct clk_prcc, hw)
253b01f87bSUlf Hansson
263b01f87bSUlf Hansson struct clk_prcc {
273b01f87bSUlf Hansson struct clk_hw hw;
283b01f87bSUlf Hansson void __iomem *base;
293b01f87bSUlf Hansson u32 cg_sel;
303b01f87bSUlf Hansson int is_enabled;
313b01f87bSUlf Hansson };
323b01f87bSUlf Hansson
333b01f87bSUlf Hansson /* PRCC clock operations. */
343b01f87bSUlf Hansson
clk_prcc_pclk_enable(struct clk_hw * hw)353b01f87bSUlf Hansson static int clk_prcc_pclk_enable(struct clk_hw *hw)
363b01f87bSUlf Hansson {
373b01f87bSUlf Hansson struct clk_prcc *clk = to_clk_prcc(hw);
383b01f87bSUlf Hansson
393b01f87bSUlf Hansson writel(clk->cg_sel, (clk->base + PRCC_PCKEN));
403b01f87bSUlf Hansson while (!(readl(clk->base + PRCC_PCKSR) & clk->cg_sel))
413b01f87bSUlf Hansson cpu_relax();
423b01f87bSUlf Hansson
433b01f87bSUlf Hansson clk->is_enabled = 1;
443b01f87bSUlf Hansson return 0;
453b01f87bSUlf Hansson }
463b01f87bSUlf Hansson
clk_prcc_pclk_disable(struct clk_hw * hw)473b01f87bSUlf Hansson static void clk_prcc_pclk_disable(struct clk_hw *hw)
483b01f87bSUlf Hansson {
493b01f87bSUlf Hansson struct clk_prcc *clk = to_clk_prcc(hw);
503b01f87bSUlf Hansson
513b01f87bSUlf Hansson writel(clk->cg_sel, (clk->base + PRCC_PCKDIS));
523b01f87bSUlf Hansson clk->is_enabled = 0;
533b01f87bSUlf Hansson }
543b01f87bSUlf Hansson
clk_prcc_kclk_enable(struct clk_hw * hw)553b01f87bSUlf Hansson static int clk_prcc_kclk_enable(struct clk_hw *hw)
563b01f87bSUlf Hansson {
573b01f87bSUlf Hansson struct clk_prcc *clk = to_clk_prcc(hw);
583b01f87bSUlf Hansson
593b01f87bSUlf Hansson writel(clk->cg_sel, (clk->base + PRCC_KCKEN));
603b01f87bSUlf Hansson while (!(readl(clk->base + PRCC_KCKSR) & clk->cg_sel))
613b01f87bSUlf Hansson cpu_relax();
623b01f87bSUlf Hansson
633b01f87bSUlf Hansson clk->is_enabled = 1;
643b01f87bSUlf Hansson return 0;
653b01f87bSUlf Hansson }
663b01f87bSUlf Hansson
clk_prcc_kclk_disable(struct clk_hw * hw)673b01f87bSUlf Hansson static void clk_prcc_kclk_disable(struct clk_hw *hw)
683b01f87bSUlf Hansson {
693b01f87bSUlf Hansson struct clk_prcc *clk = to_clk_prcc(hw);
703b01f87bSUlf Hansson
713b01f87bSUlf Hansson writel(clk->cg_sel, (clk->base + PRCC_KCKDIS));
723b01f87bSUlf Hansson clk->is_enabled = 0;
733b01f87bSUlf Hansson }
743b01f87bSUlf Hansson
clk_prcc_is_enabled(struct clk_hw * hw)753b01f87bSUlf Hansson static int clk_prcc_is_enabled(struct clk_hw *hw)
763b01f87bSUlf Hansson {
773b01f87bSUlf Hansson struct clk_prcc *clk = to_clk_prcc(hw);
783b01f87bSUlf Hansson return clk->is_enabled;
793b01f87bSUlf Hansson }
803b01f87bSUlf Hansson
8156d87776SArvind Yadav static const struct clk_ops clk_prcc_pclk_ops = {
823b01f87bSUlf Hansson .enable = clk_prcc_pclk_enable,
833b01f87bSUlf Hansson .disable = clk_prcc_pclk_disable,
843b01f87bSUlf Hansson .is_enabled = clk_prcc_is_enabled,
853b01f87bSUlf Hansson };
863b01f87bSUlf Hansson
8756d87776SArvind Yadav static const struct clk_ops clk_prcc_kclk_ops = {
883b01f87bSUlf Hansson .enable = clk_prcc_kclk_enable,
893b01f87bSUlf Hansson .disable = clk_prcc_kclk_disable,
903b01f87bSUlf Hansson .is_enabled = clk_prcc_is_enabled,
913b01f87bSUlf Hansson };
923b01f87bSUlf Hansson
clk_reg_prcc(const char * name,const char * parent_name,resource_size_t phy_base,u32 cg_sel,unsigned long flags,const struct clk_ops * clk_prcc_ops)933b01f87bSUlf Hansson static struct clk *clk_reg_prcc(const char *name,
943b01f87bSUlf Hansson const char *parent_name,
953b01f87bSUlf Hansson resource_size_t phy_base,
963b01f87bSUlf Hansson u32 cg_sel,
973b01f87bSUlf Hansson unsigned long flags,
9856d87776SArvind Yadav const struct clk_ops *clk_prcc_ops)
993b01f87bSUlf Hansson {
1003b01f87bSUlf Hansson struct clk_prcc *clk;
1013b01f87bSUlf Hansson struct clk_init_data clk_prcc_init;
1023b01f87bSUlf Hansson struct clk *clk_reg;
1033b01f87bSUlf Hansson
1043b01f87bSUlf Hansson if (!name) {
1053b01f87bSUlf Hansson pr_err("clk_prcc: %s invalid arguments passed\n", __func__);
1063b01f87bSUlf Hansson return ERR_PTR(-EINVAL);
1073b01f87bSUlf Hansson }
1083b01f87bSUlf Hansson
1090b10adbaSMarkus Elfring clk = kzalloc(sizeof(*clk), GFP_KERNEL);
1107a294dc6SMarkus Elfring if (!clk)
1113b01f87bSUlf Hansson return ERR_PTR(-ENOMEM);
1123b01f87bSUlf Hansson
1133b01f87bSUlf Hansson clk->base = ioremap(phy_base, SZ_4K);
1143b01f87bSUlf Hansson if (!clk->base)
1153b01f87bSUlf Hansson goto free_clk;
1163b01f87bSUlf Hansson
1173b01f87bSUlf Hansson clk->cg_sel = cg_sel;
1183b01f87bSUlf Hansson clk->is_enabled = 1;
1193b01f87bSUlf Hansson
1203b01f87bSUlf Hansson clk_prcc_init.name = name;
1213b01f87bSUlf Hansson clk_prcc_init.ops = clk_prcc_ops;
1223b01f87bSUlf Hansson clk_prcc_init.flags = flags;
1233b01f87bSUlf Hansson clk_prcc_init.parent_names = (parent_name ? &parent_name : NULL);
1243b01f87bSUlf Hansson clk_prcc_init.num_parents = (parent_name ? 1 : 0);
1253b01f87bSUlf Hansson clk->hw.init = &clk_prcc_init;
1263b01f87bSUlf Hansson
1273b01f87bSUlf Hansson clk_reg = clk_register(NULL, &clk->hw);
1283b01f87bSUlf Hansson if (IS_ERR_OR_NULL(clk_reg))
1293b01f87bSUlf Hansson goto unmap_clk;
1303b01f87bSUlf Hansson
1313b01f87bSUlf Hansson return clk_reg;
1323b01f87bSUlf Hansson
1333b01f87bSUlf Hansson unmap_clk:
1343b01f87bSUlf Hansson iounmap(clk->base);
1353b01f87bSUlf Hansson free_clk:
1363b01f87bSUlf Hansson kfree(clk);
1373b01f87bSUlf Hansson pr_err("clk_prcc: %s failed to register clk\n", __func__);
1383b01f87bSUlf Hansson return ERR_PTR(-ENOMEM);
1393b01f87bSUlf Hansson }
1403b01f87bSUlf Hansson
clk_reg_prcc_pclk(const char * name,const char * parent_name,resource_size_t phy_base,u32 cg_sel,unsigned long flags)1413b01f87bSUlf Hansson struct clk *clk_reg_prcc_pclk(const char *name,
1423b01f87bSUlf Hansson const char *parent_name,
1433b01f87bSUlf Hansson resource_size_t phy_base,
1443b01f87bSUlf Hansson u32 cg_sel,
1453b01f87bSUlf Hansson unsigned long flags)
1463b01f87bSUlf Hansson {
1473b01f87bSUlf Hansson return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags,
1483b01f87bSUlf Hansson &clk_prcc_pclk_ops);
1493b01f87bSUlf Hansson }
1503b01f87bSUlf Hansson
clk_reg_prcc_kclk(const char * name,const char * parent_name,resource_size_t phy_base,u32 cg_sel,unsigned long flags)1513b01f87bSUlf Hansson struct clk *clk_reg_prcc_kclk(const char *name,
1523b01f87bSUlf Hansson const char *parent_name,
1533b01f87bSUlf Hansson resource_size_t phy_base,
1543b01f87bSUlf Hansson u32 cg_sel,
1553b01f87bSUlf Hansson unsigned long flags)
1563b01f87bSUlf Hansson {
1573b01f87bSUlf Hansson return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags,
1583b01f87bSUlf Hansson &clk_prcc_kclk_ops);
1593b01f87bSUlf Hansson }
160