Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | // SPDX-License-Identifier: GPL-2.0-only /* * PRCC clock implementation for ux500 platform. * * Copyright (C) 2012 ST-Ericsson SA * Author: Ulf Hansson <ulf.hansson@linaro.org> */ #include <linux/clk-provider.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/err.h> #include <linux/types.h> #include "clk.h" #define PRCC_PCKEN 0x000 #define PRCC_PCKDIS 0x004 #define PRCC_KCKEN 0x008 #define PRCC_KCKDIS 0x00C #define PRCC_PCKSR 0x010 #define PRCC_KCKSR 0x014 #define to_clk_prcc(_hw) container_of(_hw, struct clk_prcc, hw) struct clk_prcc { struct clk_hw hw; void __iomem *base; u32 cg_sel; int is_enabled; }; /* PRCC clock operations. */ static int clk_prcc_pclk_enable(struct clk_hw *hw) { struct clk_prcc *clk = to_clk_prcc(hw); writel(clk->cg_sel, (clk->base + PRCC_PCKEN)); while (!(readl(clk->base + PRCC_PCKSR) & clk->cg_sel)) cpu_relax(); clk->is_enabled = 1; return 0; } static void clk_prcc_pclk_disable(struct clk_hw *hw) { struct clk_prcc *clk = to_clk_prcc(hw); writel(clk->cg_sel, (clk->base + PRCC_PCKDIS)); clk->is_enabled = 0; } static int clk_prcc_kclk_enable(struct clk_hw *hw) { struct clk_prcc *clk = to_clk_prcc(hw); writel(clk->cg_sel, (clk->base + PRCC_KCKEN)); while (!(readl(clk->base + PRCC_KCKSR) & clk->cg_sel)) cpu_relax(); clk->is_enabled = 1; return 0; } static void clk_prcc_kclk_disable(struct clk_hw *hw) { struct clk_prcc *clk = to_clk_prcc(hw); writel(clk->cg_sel, (clk->base + PRCC_KCKDIS)); clk->is_enabled = 0; } static int clk_prcc_is_enabled(struct clk_hw *hw) { struct clk_prcc *clk = to_clk_prcc(hw); return clk->is_enabled; } static const struct clk_ops clk_prcc_pclk_ops = { .enable = clk_prcc_pclk_enable, .disable = clk_prcc_pclk_disable, .is_enabled = clk_prcc_is_enabled, }; static const struct clk_ops clk_prcc_kclk_ops = { .enable = clk_prcc_kclk_enable, .disable = clk_prcc_kclk_disable, .is_enabled = clk_prcc_is_enabled, }; static struct clk *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) { struct clk_prcc *clk; struct clk_init_data clk_prcc_init; struct clk *clk_reg; if (!name) { pr_err("clk_prcc: %s invalid arguments passed\n", __func__); return ERR_PTR(-EINVAL); } clk = kzalloc(sizeof(*clk), GFP_KERNEL); if (!clk) return ERR_PTR(-ENOMEM); clk->base = ioremap(phy_base, SZ_4K); if (!clk->base) goto free_clk; clk->cg_sel = cg_sel; clk->is_enabled = 1; clk_prcc_init.name = name; clk_prcc_init.ops = clk_prcc_ops; clk_prcc_init.flags = flags; clk_prcc_init.parent_names = (parent_name ? &parent_name : NULL); clk_prcc_init.num_parents = (parent_name ? 1 : 0); clk->hw.init = &clk_prcc_init; clk_reg = clk_register(NULL, &clk->hw); if (IS_ERR_OR_NULL(clk_reg)) goto unmap_clk; return clk_reg; unmap_clk: iounmap(clk->base); free_clk: kfree(clk); pr_err("clk_prcc: %s failed to register clk\n", __func__); return ERR_PTR(-ENOMEM); } struct clk *clk_reg_prcc_pclk(const char *name, const char *parent_name, resource_size_t phy_base, u32 cg_sel, unsigned long flags) { return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags, &clk_prcc_pclk_ops); } struct clk *clk_reg_prcc_kclk(const char *name, const char *parent_name, resource_size_t phy_base, u32 cg_sel, unsigned long flags) { return clk_reg_prcc(name, parent_name, phy_base, cg_sel, flags, &clk_prcc_kclk_ops); } |