1
0
Fork 0

clk: sprd: add gate clock support

Some clocks on the Spreadtrum's SoCs are just simple gates. Add
support for those clocks.

Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>
hifive-unleashed-5.1
Chunyan Zhang 2017-12-07 20:57:06 +08:00 committed by Stephen Boyd
parent d41f59fd92
commit cdb09f67a2
3 changed files with 171 additions and 0 deletions

View File

@ -1,3 +1,4 @@
obj-$(CONFIG_SPRD_COMMON_CLK) += clk-sprd.o
clk-sprd-y += common.o
clk-sprd-y += gate.o

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0
//
// Spreadtrum gate clock driver
//
// Copyright (C) 2017 Spreadtrum, Inc.
// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
#include <linux/clk-provider.h>
#include <linux/regmap.h>
#include "gate.h"
static void clk_gate_toggle(const struct sprd_gate *sg, bool en)
{
const struct sprd_clk_common *common = &sg->common;
unsigned int reg;
bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? true : false;
set ^= en;
regmap_read(common->regmap, common->reg, &reg);
if (set)
reg |= sg->enable_mask;
else
reg &= ~sg->enable_mask;
regmap_write(common->regmap, common->reg, reg);
}
static void clk_sc_gate_toggle(const struct sprd_gate *sg, bool en)
{
const struct sprd_clk_common *common = &sg->common;
bool set = sg->flags & CLK_GATE_SET_TO_DISABLE ? 1 : 0;
unsigned int offset;
set ^= en;
/*
* Each set/clear gate clock has three registers:
* common->reg - base register
* common->reg + offset - set register
* common->reg + 2 * offset - clear register
*/
offset = set ? sg->sc_offset : sg->sc_offset * 2;
regmap_write(common->regmap, common->reg + offset,
sg->enable_mask);
}
static void sprd_gate_disable(struct clk_hw *hw)
{
struct sprd_gate *sg = hw_to_sprd_gate(hw);
clk_gate_toggle(sg, false);
}
static int sprd_gate_enable(struct clk_hw *hw)
{
struct sprd_gate *sg = hw_to_sprd_gate(hw);
clk_gate_toggle(sg, true);
return 0;
}
static void sprd_sc_gate_disable(struct clk_hw *hw)
{
struct sprd_gate *sg = hw_to_sprd_gate(hw);
clk_sc_gate_toggle(sg, false);
}
static int sprd_sc_gate_enable(struct clk_hw *hw)
{
struct sprd_gate *sg = hw_to_sprd_gate(hw);
clk_sc_gate_toggle(sg, true);
return 0;
}
static int sprd_gate_is_enabled(struct clk_hw *hw)
{
struct sprd_gate *sg = hw_to_sprd_gate(hw);
struct sprd_clk_common *common = &sg->common;
unsigned int reg;
regmap_read(common->regmap, common->reg, &reg);
if (sg->flags & CLK_GATE_SET_TO_DISABLE)
reg ^= sg->enable_mask;
reg &= sg->enable_mask;
return reg ? 1 : 0;
}
const struct clk_ops sprd_gate_ops = {
.disable = sprd_gate_disable,
.enable = sprd_gate_enable,
.is_enabled = sprd_gate_is_enabled,
};
EXPORT_SYMBOL_GPL(sprd_gate_ops);
const struct clk_ops sprd_sc_gate_ops = {
.disable = sprd_sc_gate_disable,
.enable = sprd_sc_gate_enable,
.is_enabled = sprd_gate_is_enabled,
};
EXPORT_SYMBOL_GPL(sprd_sc_gate_ops);

View File

@ -0,0 +1,59 @@
// SPDX-License-Identifier: GPL-2.0
//
// Spreadtrum gate clock driver
//
// Copyright (C) 2017 Spreadtrum, Inc.
// Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
#ifndef _SPRD_GATE_H_
#define _SPRD_GATE_H_
#include "common.h"
struct sprd_gate {
u32 enable_mask;
u16 flags;
u16 sc_offset;
struct sprd_clk_common common;
};
#define SPRD_SC_GATE_CLK_OPS(_struct, _name, _parent, _reg, _sc_offset, \
_enable_mask, _flags, _gate_flags, _ops) \
struct sprd_gate _struct = { \
.enable_mask = _enable_mask, \
.sc_offset = _sc_offset, \
.flags = _gate_flags, \
.common = { \
.regmap = NULL, \
.reg = _reg, \
.hw.init = CLK_HW_INIT(_name, \
_parent, \
_ops, \
_flags), \
} \
}
#define SPRD_GATE_CLK(_struct, _name, _parent, _reg, \
_enable_mask, _flags, _gate_flags) \
SPRD_SC_GATE_CLK_OPS(_struct, _name, _parent, _reg, 0, \
_enable_mask, _flags, _gate_flags, \
&sprd_gate_ops)
#define SPRD_SC_GATE_CLK(_struct, _name, _parent, _reg, _sc_offset, \
_enable_mask, _flags, _gate_flags) \
SPRD_SC_GATE_CLK_OPS(_struct, _name, _parent, _reg, _sc_offset, \
_enable_mask, _flags, _gate_flags, \
&sprd_sc_gate_ops)
static inline struct sprd_gate *hw_to_sprd_gate(const struct clk_hw *hw)
{
struct sprd_clk_common *common = hw_to_sprd_clk_common(hw);
return container_of(common, struct sprd_gate, common);
}
extern const struct clk_ops sprd_gate_ops;
extern const struct clk_ops sprd_sc_gate_ops;
#endif /* _SPRD_GATE_H_ */