brintos

brintos / linux-shallow public Read only

0
0
Text · 4.9 KiB · d0b193b Raw
207 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Toshiba Visconti clock controller4 *5 * Copyright (c) 2021 TOSHIBA CORPORATION6 * Copyright (c) 2021 Toshiba Electronic Devices & Storage Corporation7 *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}207