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 160 161 162 163 164 165 166 167 | // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> * Copyright 2019 NXP * * Gated clock implementation */ #define LOG_CATEGORY UCLASS_CLK #include <clk.h> #include <log.h> #include <clk-uclass.h> #include <malloc.h> #include <asm/io.h> #include <dm/device.h> #include <dm/device_compat.h> #include <dm/devres.h> #include <linux/bitops.h> #include <linux/clk-provider.h> #include <linux/err.h> #include <linux/printk.h> #include "clk.h" #define UBOOT_DM_CLK_GATE "clk_gate" /** * DOC: basic gatable clock which can gate and ungate it's output * * Traits of this clock: * prepare - clk_(un)prepare only ensures parent is (un)prepared * enable - clk_enable and clk_disable are functional & control gating * rate - inherits rate from parent. No clk_set_rate support * parent - fixed parent. No clk_set_parent support */ /* * It works on following logic: * * For enabling clock, enable = 1 * set2dis = 1 -> clear bit -> set = 0 * set2dis = 0 -> set bit -> set = 1 * * For disabling clock, enable = 0 * set2dis = 1 -> set bit -> set = 1 * set2dis = 0 -> clear bit -> set = 0 * * So, result is always: enable xor set2dis. */ static void clk_gate_endisable(struct clk *clk, int enable) { struct clk_gate *gate = to_clk_gate(clk); int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0; u32 reg; set ^= enable; if (gate->flags & CLK_GATE_HIWORD_MASK) { reg = BIT(gate->bit_idx + 16); if (set) reg |= BIT(gate->bit_idx); } else { #if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF) reg = gate->io_gate_val; #else reg = readl(gate->reg); #endif if (set) reg |= BIT(gate->bit_idx); else reg &= ~BIT(gate->bit_idx); } writel(reg, gate->reg); } static int clk_gate_enable(struct clk *clk) { clk_gate_endisable(clk, 1); return 0; } static int clk_gate_disable(struct clk *clk) { clk_gate_endisable(clk, 0); return 0; } int clk_gate_is_enabled(struct clk *clk) { struct clk_gate *gate = to_clk_gate(clk); u32 reg; #if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF) reg = gate->io_gate_val; #else reg = readl(gate->reg); #endif /* if a set bit disables this clk, flip it before masking */ if (gate->flags & CLK_GATE_SET_TO_DISABLE) reg ^= BIT(gate->bit_idx); reg &= BIT(gate->bit_idx); return reg ? 1 : 0; } const struct clk_ops clk_gate_ops = { .enable = clk_gate_enable, .disable = clk_gate_disable, .get_rate = clk_generic_get_rate, }; struct clk *clk_register_gate(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 clk_gate_flags, spinlock_t *lock) { struct clk_gate *gate; struct clk *clk; int ret; if (clk_gate_flags & CLK_GATE_HIWORD_MASK) { if (bit_idx > 15) { dev_err(dev, "gate bit exceeds LOWORD field\n"); return ERR_PTR(-EINVAL); } } /* allocate the gate */ gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) return ERR_PTR(-ENOMEM); /* struct clk_gate assignments */ gate->reg = reg; gate->bit_idx = bit_idx; gate->flags = clk_gate_flags; #if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF) gate->io_gate_val = *(u32 *)reg; #endif clk = &gate->clk; clk->flags = flags; ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(gate); return ERR_PTR(ret); } return clk; } U_BOOT_DRIVER(clk_gate) = { .name = UBOOT_DM_CLK_GATE, .id = UCLASS_CLK, .ops = &clk_gate_ops, .flags = DM_FLAG_PRE_RELOC, }; |