blob: cfd90b717e73f3dd0a630617e16ee65d5ae40e45 [file] [log] [blame]
Peng Fan0f085152019-07-31 07:01:34 +00001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
4 * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
5 * Copyright 2019 NXP
6 *
7 * Gated clock implementation
8 */
9
Patrick Delaunay8767e792021-11-19 15:12:07 +010010#define LOG_CATEGORY UCLASS_CLK
11
Tom Riniabb9a042024-05-18 20:20:43 -060012#include <common.h>
Patrick Delaunay283dadf2021-11-19 15:12:06 +010013#include <clk.h>
Patrick Delaunay8767e792021-11-19 15:12:07 +010014#include <log.h>
Peng Fan0f085152019-07-31 07:01:34 +000015#include <clk-uclass.h>
Patrick Delaunay283dadf2021-11-19 15:12:06 +010016#include <malloc.h>
17#include <asm/io.h>
Peng Fan0f085152019-07-31 07:01:34 +000018#include <dm/device.h>
Patrick Delaunay8767e792021-11-19 15:12:07 +010019#include <dm/device_compat.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070020#include <dm/devres.h>
Simon Glass4dcacfc2020-05-10 11:40:13 -060021#include <linux/bitops.h>
Peng Fan0f085152019-07-31 07:01:34 +000022#include <linux/clk-provider.h>
Simon Glassd66c5f72020-02-03 07:36:15 -070023#include <linux/err.h>
Simon Glassbdd5f812023-09-14 18:21:46 -060024#include <linux/printk.h>
Peng Fan0f085152019-07-31 07:01:34 +000025
Patrick Delaunay283dadf2021-11-19 15:12:06 +010026#include "clk.h"
27
Peng Fan0f085152019-07-31 07:01:34 +000028#define UBOOT_DM_CLK_GATE "clk_gate"
29
30/**
31 * DOC: basic gatable clock which can gate and ungate it's output
32 *
33 * Traits of this clock:
34 * prepare - clk_(un)prepare only ensures parent is (un)prepared
35 * enable - clk_enable and clk_disable are functional & control gating
36 * rate - inherits rate from parent. No clk_set_rate support
37 * parent - fixed parent. No clk_set_parent support
38 */
39
40/*
41 * It works on following logic:
42 *
43 * For enabling clock, enable = 1
44 * set2dis = 1 -> clear bit -> set = 0
45 * set2dis = 0 -> set bit -> set = 1
46 *
47 * For disabling clock, enable = 0
48 * set2dis = 1 -> set bit -> set = 1
49 * set2dis = 0 -> clear bit -> set = 0
50 *
51 * So, result is always: enable xor set2dis.
52 */
53static void clk_gate_endisable(struct clk *clk, int enable)
54{
Sean Andersoncfc2f022020-06-24 06:41:06 -040055 struct clk_gate *gate = to_clk_gate(clk);
Peng Fan0f085152019-07-31 07:01:34 +000056 int set = gate->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
57 u32 reg;
58
59 set ^= enable;
60
61 if (gate->flags & CLK_GATE_HIWORD_MASK) {
62 reg = BIT(gate->bit_idx + 16);
63 if (set)
64 reg |= BIT(gate->bit_idx);
65 } else {
Simon Glass0a6a0c42023-02-05 15:40:43 -070066#if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF)
Peng Fan3b7f3ae2019-07-31 07:01:57 +000067 reg = gate->io_gate_val;
68#else
Peng Fan0f085152019-07-31 07:01:34 +000069 reg = readl(gate->reg);
Peng Fan3b7f3ae2019-07-31 07:01:57 +000070#endif
Peng Fan0f085152019-07-31 07:01:34 +000071
72 if (set)
73 reg |= BIT(gate->bit_idx);
74 else
75 reg &= ~BIT(gate->bit_idx);
76 }
77
78 writel(reg, gate->reg);
79}
80
81static int clk_gate_enable(struct clk *clk)
82{
83 clk_gate_endisable(clk, 1);
84
85 return 0;
86}
87
88static int clk_gate_disable(struct clk *clk)
89{
90 clk_gate_endisable(clk, 0);
91
92 return 0;
93}
94
95int clk_gate_is_enabled(struct clk *clk)
96{
Sean Andersoncfc2f022020-06-24 06:41:06 -040097 struct clk_gate *gate = to_clk_gate(clk);
Peng Fan0f085152019-07-31 07:01:34 +000098 u32 reg;
99
Simon Glass0a6a0c42023-02-05 15:40:43 -0700100#if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF)
Peng Fan3b7f3ae2019-07-31 07:01:57 +0000101 reg = gate->io_gate_val;
102#else
Peng Fan0f085152019-07-31 07:01:34 +0000103 reg = readl(gate->reg);
Peng Fan3b7f3ae2019-07-31 07:01:57 +0000104#endif
Peng Fan0f085152019-07-31 07:01:34 +0000105
106 /* if a set bit disables this clk, flip it before masking */
107 if (gate->flags & CLK_GATE_SET_TO_DISABLE)
108 reg ^= BIT(gate->bit_idx);
109
110 reg &= BIT(gate->bit_idx);
111
112 return reg ? 1 : 0;
113}
114
115const struct clk_ops clk_gate_ops = {
116 .enable = clk_gate_enable,
117 .disable = clk_gate_disable,
118 .get_rate = clk_generic_get_rate,
119};
120
121struct clk *clk_register_gate(struct device *dev, const char *name,
122 const char *parent_name, unsigned long flags,
123 void __iomem *reg, u8 bit_idx,
124 u8 clk_gate_flags, spinlock_t *lock)
125{
126 struct clk_gate *gate;
127 struct clk *clk;
128 int ret;
129
130 if (clk_gate_flags & CLK_GATE_HIWORD_MASK) {
131 if (bit_idx > 15) {
Patrick Delaunay8767e792021-11-19 15:12:07 +0100132 dev_err(dev, "gate bit exceeds LOWORD field\n");
Peng Fan0f085152019-07-31 07:01:34 +0000133 return ERR_PTR(-EINVAL);
134 }
135 }
136
137 /* allocate the gate */
138 gate = kzalloc(sizeof(*gate), GFP_KERNEL);
139 if (!gate)
140 return ERR_PTR(-ENOMEM);
141
142 /* struct clk_gate assignments */
143 gate->reg = reg;
144 gate->bit_idx = bit_idx;
145 gate->flags = clk_gate_flags;
Simon Glass0a6a0c42023-02-05 15:40:43 -0700146#if IS_ENABLED(CONFIG_SANDBOX_CLK_CCF)
Peng Fan3b7f3ae2019-07-31 07:01:57 +0000147 gate->io_gate_val = *(u32 *)reg;
148#endif
Peng Fan0f085152019-07-31 07:01:34 +0000149
150 clk = &gate->clk;
Dario Binacchi1a62dc12020-04-13 14:36:27 +0200151 clk->flags = flags;
Peng Fan0f085152019-07-31 07:01:34 +0000152
153 ret = clk_register(clk, UBOOT_DM_CLK_GATE, name, parent_name);
154 if (ret) {
155 kfree(gate);
156 return ERR_PTR(ret);
157 }
158
159 return clk;
160}
161
162U_BOOT_DRIVER(clk_gate) = {
163 .name = UBOOT_DM_CLK_GATE,
164 .id = UCLASS_CLK,
165 .ops = &clk_gate_ops,
166 .flags = DM_FLAG_PRE_RELOC,
167};