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 | // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2019 DENX Software Engineering * Lukasz Majewski, DENX Software Engineering, lukma@denx.de * * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com> * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org> * * 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. * * Gated clock implementation * */ #include <asm/io.h> #include <malloc.h> #include <clk-uclass.h> #include <dm/device.h> #include <dm/devres.h> #include <linux/bug.h> #include <linux/clk-provider.h> #include <clk.h> #include "clk.h" #include <linux/err.h> #define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2" struct clk_gate2 { struct clk clk; void __iomem *reg; u8 bit_idx; u8 cgr_val; u8 flags; unsigned int *share_count; }; #define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk) static int clk_gate2_enable(struct clk *clk) { struct clk_gate2 *gate = to_clk_gate2(clk); u32 reg; if (gate->share_count && (*gate->share_count)++ > 0) return 0; reg = readl(gate->reg); reg &= ~(3 << gate->bit_idx); reg |= gate->cgr_val << gate->bit_idx; writel(reg, gate->reg); return 0; } static int clk_gate2_disable(struct clk *clk) { struct clk_gate2 *gate = to_clk_gate2(clk); u32 reg; if (gate->share_count) { if (WARN_ON(*gate->share_count == 0)) return 0; else if (--(*gate->share_count) > 0) return 0; } reg = readl(gate->reg); reg &= ~(3 << gate->bit_idx); writel(reg, gate->reg); return 0; } static ulong clk_gate2_set_rate(struct clk *clk, ulong rate) { struct clk *parent = clk_get_parent(clk); if (parent) return clk_set_rate(parent, rate); return -ENODEV; } static const struct clk_ops clk_gate2_ops = { .set_rate = clk_gate2_set_rate, .enable = clk_gate2_enable, .disable = clk_gate2_disable, .get_rate = clk_generic_get_rate, }; struct clk *clk_register_gate2(struct udevice *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 bit_idx, u8 cgr_val, u8 clk_gate2_flags, unsigned int *share_count) { struct clk_gate2 *gate; struct clk *clk; int ret; gate = kzalloc(sizeof(*gate), GFP_KERNEL); if (!gate) return ERR_PTR(-ENOMEM); gate->reg = reg; gate->bit_idx = bit_idx; gate->cgr_val = cgr_val; gate->flags = clk_gate2_flags; gate->share_count = share_count; clk = &gate->clk; ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, name, clk_resolve_parent_clk(dev, parent_name)); if (ret) { kfree(gate); return ERR_PTR(ret); } return clk; } U_BOOT_DRIVER(clk_gate2) = { .name = UBOOT_DM_CLK_IMX_GATE2, .id = UCLASS_CLK, .ops = &clk_gate2_ops, .flags = DM_FLAG_PRE_RELOC, }; |