Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

clk: sprd: add divider clock support

This is a feature that can also be found in sprd composite clocks,
provide a bunch of helpers that can be reused later on.

Signed-off-by: Chunyan Zhang <chunyan.zhang@spreadtrum.com>
Signed-off-by: Stephen Boyd <sboyd@codeaurora.org>

authored by

Chunyan Zhang and committed by
Stephen Boyd
e3f05d3b ab73cf2a

+166
+1
drivers/clk/sprd/Makefile
··· 3 3 clk-sprd-y += common.o 4 4 clk-sprd-y += gate.o 5 5 clk-sprd-y += mux.o 6 + clk-sprd-y += div.o
+90
drivers/clk/sprd/div.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // 3 + // Spreadtrum divider clock driver 4 + // 5 + // Copyright (C) 2017 Spreadtrum, Inc. 6 + // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 7 + 8 + #include <linux/clk-provider.h> 9 + 10 + #include "div.h" 11 + 12 + long sprd_div_helper_round_rate(struct sprd_clk_common *common, 13 + const struct sprd_div_internal *div, 14 + unsigned long rate, 15 + unsigned long *parent_rate) 16 + { 17 + return divider_round_rate(&common->hw, rate, parent_rate, 18 + NULL, div->width, 0); 19 + } 20 + EXPORT_SYMBOL_GPL(sprd_div_helper_round_rate); 21 + 22 + static long sprd_div_round_rate(struct clk_hw *hw, unsigned long rate, 23 + unsigned long *parent_rate) 24 + { 25 + struct sprd_div *cd = hw_to_sprd_div(hw); 26 + 27 + return sprd_div_helper_round_rate(&cd->common, &cd->div, 28 + rate, parent_rate); 29 + } 30 + 31 + unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common, 32 + const struct sprd_div_internal *div, 33 + unsigned long parent_rate) 34 + { 35 + unsigned long val; 36 + unsigned int reg; 37 + 38 + regmap_read(common->regmap, common->reg, &reg); 39 + val = reg >> div->shift; 40 + val &= (1 << div->width) - 1; 41 + 42 + return divider_recalc_rate(&common->hw, parent_rate, val, NULL, 0); 43 + } 44 + EXPORT_SYMBOL_GPL(sprd_div_helper_recalc_rate); 45 + 46 + static unsigned long sprd_div_recalc_rate(struct clk_hw *hw, 47 + unsigned long parent_rate) 48 + { 49 + struct sprd_div *cd = hw_to_sprd_div(hw); 50 + 51 + return sprd_div_helper_recalc_rate(&cd->common, &cd->div, parent_rate); 52 + } 53 + 54 + int sprd_div_helper_set_rate(const struct sprd_clk_common *common, 55 + const struct sprd_div_internal *div, 56 + unsigned long rate, 57 + unsigned long parent_rate) 58 + { 59 + unsigned long val; 60 + unsigned int reg; 61 + 62 + val = divider_get_val(rate, parent_rate, NULL, 63 + div->width, 0); 64 + 65 + regmap_read(common->regmap, common->reg, &reg); 66 + reg &= ~GENMASK(div->width + div->shift - 1, div->shift); 67 + 68 + regmap_write(common->regmap, common->reg, 69 + reg | (val << div->shift)); 70 + 71 + return 0; 72 + 73 + } 74 + EXPORT_SYMBOL_GPL(sprd_div_helper_set_rate); 75 + 76 + static int sprd_div_set_rate(struct clk_hw *hw, unsigned long rate, 77 + unsigned long parent_rate) 78 + { 79 + struct sprd_div *cd = hw_to_sprd_div(hw); 80 + 81 + return sprd_div_helper_set_rate(&cd->common, &cd->div, 82 + rate, parent_rate); 83 + } 84 + 85 + const struct clk_ops sprd_div_ops = { 86 + .recalc_rate = sprd_div_recalc_rate, 87 + .round_rate = sprd_div_round_rate, 88 + .set_rate = sprd_div_set_rate, 89 + }; 90 + EXPORT_SYMBOL_GPL(sprd_div_ops);
+75
drivers/clk/sprd/div.h
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + // 3 + // Spreadtrum divider clock driver 4 + // 5 + // Copyright (C) 2017 Spreadtrum, Inc. 6 + // Author: Chunyan Zhang <chunyan.zhang@spreadtrum.com> 7 + 8 + #ifndef _SPRD_DIV_H_ 9 + #define _SPRD_DIV_H_ 10 + 11 + #include "common.h" 12 + 13 + /** 14 + * struct sprd_div_internal - Internal divider description 15 + * @shift: Bit offset of the divider in its register 16 + * @width: Width of the divider field in its register 17 + * 18 + * That structure represents a single divider, and is meant to be 19 + * embedded in other structures representing the various clock 20 + * classes. 21 + */ 22 + struct sprd_div_internal { 23 + u8 shift; 24 + u8 width; 25 + }; 26 + 27 + #define _SPRD_DIV_CLK(_shift, _width) \ 28 + { \ 29 + .shift = _shift, \ 30 + .width = _width, \ 31 + } 32 + 33 + struct sprd_div { 34 + struct sprd_div_internal div; 35 + struct sprd_clk_common common; 36 + }; 37 + 38 + #define SPRD_DIV_CLK(_struct, _name, _parent, _reg, \ 39 + _shift, _width, _flags) \ 40 + struct sprd_div _struct = { \ 41 + .div = _SPRD_DIV_CLK(_shift, _width), \ 42 + .common = { \ 43 + .regmap = NULL, \ 44 + .reg = _reg, \ 45 + .hw.init = CLK_HW_INIT(_name, \ 46 + _parent, \ 47 + &sprd_div_ops, \ 48 + _flags), \ 49 + } \ 50 + } 51 + 52 + static inline struct sprd_div *hw_to_sprd_div(const struct clk_hw *hw) 53 + { 54 + struct sprd_clk_common *common = hw_to_sprd_clk_common(hw); 55 + 56 + return container_of(common, struct sprd_div, common); 57 + } 58 + 59 + long sprd_div_helper_round_rate(struct sprd_clk_common *common, 60 + const struct sprd_div_internal *div, 61 + unsigned long rate, 62 + unsigned long *parent_rate); 63 + 64 + unsigned long sprd_div_helper_recalc_rate(struct sprd_clk_common *common, 65 + const struct sprd_div_internal *div, 66 + unsigned long parent_rate); 67 + 68 + int sprd_div_helper_set_rate(const struct sprd_clk_common *common, 69 + const struct sprd_div_internal *div, 70 + unsigned long rate, 71 + unsigned long parent_rate); 72 + 73 + extern const struct clk_ops sprd_div_ops; 74 + 75 + #endif /* _SPRD_DIV_H_ */