File Index
Files and directories
Sign in
FILE

clkc.c

File preview with syntax highlighting.

linux-6.19.14/drivers/clk/visconti/clkc.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Toshiba Visconti clock controller
  4 *
  5 * Copyright (c) 2021 TOSHIBA CORPORATION
  6 * Copyright (c) 2021 Toshiba Electronic Devices & Storage Corporation
  7 *
  8 * Nobuhiro Iwamatsu <nobuhiro1.iwamatsu@toshiba.co.jp>
  9 */
 10
 11#include <linux/clk-provider.h>
 12#include <linux/delay.h>
 13#include <linux/device.h>
 14#include <linux/io.h>
 15#include <linux/of.h>
 16#include <linux/of_address.h>
 17#include <linux/regmap.h>
 18#include <linux/slab.h>
 19#include <linux/string.h>
 20
 21#include "clkc.h"
 22
 23static inline struct visconti_clk_gate *to_visconti_clk_gate(struct clk_hw *hw)
 24{
 25	return container_of(hw, struct visconti_clk_gate, hw);
 26}
 27
 28static int visconti_gate_clk_is_enabled(struct clk_hw *hw)
 29{
 30	struct visconti_clk_gate *gate = to_visconti_clk_gate(hw);
 31	u32 clk = BIT(gate->ck_idx);
 32	u32 val;
 33
 34	regmap_read(gate->regmap, gate->ckon_offset, &val);
 35	return (val & clk) ? 1 : 0;
 36}
 37
 38static void visconti_gate_clk_disable(struct clk_hw *hw)
 39{
 40	struct visconti_clk_gate *gate = to_visconti_clk_gate(hw);
 41	u32 clk = BIT(gate->ck_idx);
 42	unsigned long flags;
 43
 44	spin_lock_irqsave(gate->lock, flags);
 45
 46	if (!visconti_gate_clk_is_enabled(hw)) {
 47		spin_unlock_irqrestore(gate->lock, flags);
 48		return;
 49	}
 50
 51	regmap_update_bits(gate->regmap, gate->ckoff_offset, clk, clk);
 52	spin_unlock_irqrestore(gate->lock, flags);
 53}
 54
 55static int visconti_gate_clk_enable(struct clk_hw *hw)
 56{
 57	struct visconti_clk_gate *gate = to_visconti_clk_gate(hw);
 58	u32 clk = BIT(gate->ck_idx);
 59	unsigned long flags;
 60
 61	spin_lock_irqsave(gate->lock, flags);
 62	regmap_update_bits(gate->regmap, gate->ckon_offset, clk, clk);
 63	spin_unlock_irqrestore(gate->lock, flags);
 64
 65	return 0;
 66}
 67
 68static const struct clk_ops visconti_clk_gate_ops = {
 69	.enable = visconti_gate_clk_enable,
 70	.disable = visconti_gate_clk_disable,
 71	.is_enabled = visconti_gate_clk_is_enabled,
 72};
 73
 74static struct clk_hw *visconti_clk_register_gate(struct device *dev,
 75						 const char *name,
 76						 const char *parent_name,
 77						 struct regmap *regmap,
 78						 const struct visconti_clk_gate_table *clks,
 79						 u32	rson_offset,
 80						 u32	rsoff_offset,
 81						 u8	rs_idx,
 82						 spinlock_t *lock)
 83{
 84	struct visconti_clk_gate *gate;
 85	struct clk_parent_data *pdata;
 86	struct clk_init_data init;
 87	struct clk_hw *hw;
 88	int ret;
 89
 90	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 91	if (!pdata)
 92		return ERR_PTR(-ENOMEM);
 93
 94	pdata->name = pdata->fw_name = parent_name;
 95
 96	gate = devm_kzalloc(dev, sizeof(*gate), GFP_KERNEL);
 97	if (!gate)
 98		return ERR_PTR(-ENOMEM);
 99
100	init.name = name;
101	init.ops = &visconti_clk_gate_ops;
102	init.flags = clks->flags;
103	init.parent_data = pdata;
104	init.num_parents = 1;
105
106	gate->regmap = regmap;
107	gate->ckon_offset = clks->ckon_offset;
108	gate->ckoff_offset = clks->ckoff_offset;
109	gate->ck_idx = clks->ck_idx;
110	gate->rson_offset = rson_offset;
111	gate->rsoff_offset = rsoff_offset;
112	gate->rs_idx = rs_idx;
113	gate->lock = lock;
114	gate->hw.init = &init;
115
116	hw = &gate->hw;
117	ret = devm_clk_hw_register(dev, hw);
118	if (ret)
119		hw = ERR_PTR(ret);
120
121	return hw;
122}
123
124int visconti_clk_register_gates(struct visconti_clk_provider *ctx,
125				const struct visconti_clk_gate_table *clks,
126				int num_gate,
127				const struct visconti_reset_data *reset,
128				spinlock_t *lock)
129{
130	struct device *dev = ctx->dev;
131	int i;
132
133	for (i = 0; i < num_gate; i++) {
134		const char *parent_div_name = clks[i].parent_data[0].name;
135		struct clk_parent_data *pdata;
136		u32 rson_offset, rsoff_offset;
137		struct clk_hw *gate_clk;
138		struct clk_hw *div_clk;
139		char *dev_name;
140		u8 rs_idx;
141
142		pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
143		if (!pdata)
144			return -ENOMEM;
145
146		dev_name = devm_kasprintf(dev, GFP_KERNEL, "%s_div", clks[i].name);
147		if (!dev_name)
148			return -ENOMEM;
149
150		if (clks[i].rs_id != NO_RESET) {
151			rson_offset = reset[clks[i].rs_id].rson_offset;
152			rsoff_offset = reset[clks[i].rs_id].rsoff_offset;
153			rs_idx = reset[clks[i].rs_id].rs_idx;
154		} else {
155			rson_offset = rsoff_offset = rs_idx = -1;
156		}
157
158		div_clk = devm_clk_hw_register_fixed_factor(dev,
159							    dev_name,
160							    parent_div_name,
161							    0, 1,
162							    clks[i].div);
163		if (IS_ERR(div_clk))
164			return PTR_ERR(div_clk);
165
166		gate_clk = visconti_clk_register_gate(dev,
167						      clks[i].name,
168						      dev_name,
169						      ctx->regmap,
170						      &clks[i],
171						      rson_offset,
172						      rsoff_offset,
173						      rs_idx,
174						      lock);
175		if (IS_ERR(gate_clk)) {
176			dev_err(dev, "%s: failed to register clock %s\n",
177				__func__, clks[i].name);
178			return PTR_ERR(gate_clk);
179		}
180
181		ctx->clk_data.hws[clks[i].id] = gate_clk;
182	}
183
184	return 0;
185}
186
187struct visconti_clk_provider *visconti_init_clk(struct device *dev,
188						struct regmap *regmap,
189						unsigned long nr_clks)
190{
191	struct visconti_clk_provider *ctx;
192	int i;
193
194	ctx = devm_kzalloc(dev, struct_size(ctx, clk_data.hws, nr_clks), GFP_KERNEL);
195	if (!ctx)
196		return ERR_PTR(-ENOMEM);
197
198	for (i = 0; i < nr_clks; ++i)
199		ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
200	ctx->clk_data.num = nr_clks;
201
202	ctx->dev = dev;
203	ctx->regmap = regmap;
204
205	return ctx;
206}
~/ Read-only file preview.