qcom audio clock control drivers
v3 cover qcom audio clock control drivers
This patchset adds support for the low power audio subsystem (LPASS) clock controller hardware. I split out the #define patch for IPQ so that it can go through the clock tree and the arm-soc tree in parallel if desired.
Changes since v2:
* Rebased onto v3.19-rc2
* One fix to a clock parent name
Change since v1:
* Renamed clk-cdiv to clk-regmap-divider
* Added msm8960/apq8064 support
* Split out dividers and branches to check for halt bits
Josh Cartwright (1):
clk: qcom: Add support for regmap divider clocks
Rajendra Nayak (3):
dt-bindings: Add #defines for IPQ806x lpass clock control
clk: qcom: Add IPQ806X LPASS clock controller (LCC) driver
devicetree: bindings: Document qcom,lcc
Stephen Boyd (4):
clk: Add __clk_mux_determine_rate_closest
clk: divider: Make generic for usage elsewhere
clk: qcom: Add simple regmap based muxes
clk: qcom: Add MSM8960/APQ8064 LPASS clock controller (LCC) driver
.../devicetree/bindings/clock/qcom,lcc.txt | 21 + drivers/clk/clk-divider.c | 212 +++++--- drivers/clk/clk.c | 47 +- drivers/clk/qcom/Kconfig | 18 + drivers/clk/qcom/Makefile | 4 + drivers/clk/qcom/clk-regmap-divider.c | 70 +++ drivers/clk/qcom/clk-regmap-divider.h | 29 + drivers/clk/qcom/clk-regmap-mux.c | 59 +++ drivers/clk/qcom/clk-regmap-mux.h | 29 + drivers/clk/qcom/gcc-ipq806x.c | 12 + drivers/clk/qcom/lcc-ipq806x.c | 473 +++++++++++++++++ drivers/clk/qcom/lcc-msm8960.c | 585 +++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-ipq806x.h | 1 - include/dt-bindings/clock/qcom,lcc-ipq806x.h | 30 ++ include/dt-bindings/clock/qcom,lcc-msm8960.h | 50 ++ include/linux/clk-provider.h | 19 +- 16 files changed, 1564 insertions(+), 95 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/qcom,lcc.txt create mode 100644 drivers/clk/qcom/clk-regmap-divider.c create mode 100644 drivers/clk/qcom/clk-regmap-divider.h create mode 100644 drivers/clk/qcom/clk-regmap-mux.c create mode 100644 drivers/clk/qcom/clk-regmap-mux.h create mode 100644 drivers/clk/qcom/lcc-ipq806x.c create mode 100644 drivers/clk/qcom/lcc-msm8960.c create mode 100644 include/dt-bindings/clock/qcom,lcc-ipq806x.h create mode 100644 include/dt-bindings/clock/qcom,lcc-msm8960.h
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v3 1/8 clk: Add __clk_mux_determine_rate_closest
Some clock drivers want to find the closest rate on the input of a mux instead of a rate that's less than or equal to the desired rate. Add a generic mux function to support this.
--- drivers/clk/clk.c | 47 +++++++++++++++++++++++++++++++++++--------- include/linux/clk-provider.h | 8 +++++++- 2 files changed, 45 insertions(+), 10 deletions(-)
drivers/clk/clk.c +38 -9
@@ -694,14 +694,20 @@ struct clk *__clk_lookup(const char *name)return NULL ; } -/* -* Helper for finding best parent to provide a given frequency. This can be used -* directly as a determine_rate callback (e.g. for a mux), or from a more -* complex clock that may combine a mux with other operations. -*/ -long __clk_mux_determine_rate (struct clk_hw *hw,unsigned long rate, -unsigned long *best_parent_rate, -struct clk_hw **best_parent_p) +static bool mux_is_better_rate (unsigned long rate,unsigned long now, +unsigned long best,unsigned long flags) +{ +if (flags &CLK_MUX_ROUND_CLOSEST ) +return abs (now - rate) <abs (best - rate); + +return now <= rate && now > best; +} + +static long +clk_mux_determine_rate_flags (struct clk_hw *hw,unsigned long rate, +unsigned long *best_parent_rate, +struct clk_hw **best_parent_p, +unsigned long flags) {struct clk *clk = hw->clk, *parent, *best_parent =NULL ;int i, num_parents;
@@ -729,7 +735,7 @@ long __clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, parent_rate =__clk_round_rate (parent, rate); else parent_rate =__clk_get_rate (parent); -if (parent_rate <= rate && parent_rate > best) { +if (mux_is_better_rate (rate, parent_rate, best, flags)) { best_parent = parent; best = parent_rate; }
@@ -742,8 +748,31 @@ out:return best; } + +/* +* Helper for finding best parent to provide a given frequency. This can be used +* directly as a determine_rate callback (e.g. for a mux), or from a more +* complex clock that may combine a mux with other operations. +*/ +long __clk_mux_determine_rate (struct clk_hw *hw,unsigned long rate, +unsigned long *best_parent_rate, +struct clk_hw **best_parent_p) +{ +return clk_mux_determine_rate_flags (hw, rate, best_parent_rate, + best_parent_p,0 ); +}EXPORT_SYMBOL_GPL (__clk_mux_determine_rate); +long __clk_mux_determine_rate_closest (struct clk_hw *hw,unsigned long rate, +unsigned long *best_parent_rate, +struct clk_hw **best_parent_p) +{ +return clk_mux_determine_rate_flags (hw, rate, best_parent_rate, + best_parent_p, +CLK_MUX_ROUND_CLOSEST ); +} +EXPORT_SYMBOL_GPL (__clk_mux_determine_rate_closest); +/*** clk api ***/ void __clk_unprepare (struct clk *clk)
include/linux/clk-provider.h +7 -1
@@ -382,6 +382,8 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name,* register, and mask of mux bits are in higher 16-bit of this register. * While setting the mux bits, higher 16-bit should also be updated to * indicate changing mux bits. +* CLK_MUX_ROUND_CLOSEST - Use the parent rate that is closest to the desired +* frequency. */ struct clk_mux {struct clk_hw hw;
@@ -396,7 +398,8 @@ struct clk_mux {#define CLK_MUX_INDEX_ONE BIT(0)#define CLK_MUX_INDEX_BIT BIT(1)#define CLK_MUX_HIWORD_MASK BIT(2) -#define CLK_MUX_READ_ONLY BIT(3)/* mux setting cannot be changed */ +#define CLK_MUX_READ_ONLY BIT(3)/* mux can't be changed */ +#define CLK_MUX_ROUND_CLOSEST BIT(4)extern const struct clk_ops clk_mux_ops;extern const struct clk_ops clk_mux_ro_ops;
@@ -552,6 +555,9 @@ struct clk *__clk_lookup(const char *name);long __clk_mux_determine_rate (struct clk_hw *hw,unsigned long rate,unsigned long *best_parent_rate,struct clk_hw **best_parent_p); +long __clk_mux_determine_rate_closest (struct clk_hw *hw,unsigned long rate, +unsigned long *best_parent_rate, +struct clk_hw **best_parent_p);/* * FIXME clock api without lock protection
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v3 2/8 clk: divider: Make generic for usage elsewhere
In reply to Stephen BoydSome devices don't use mmio to interact with dividers. Split out the logic from the register read/write parts so that we can reuse the division logic elsewhere.
--- drivers/clk/clk-divider.c | 212 ++++++++++++++++++++++++++----------------- include/linux/clk-provider.h | 11 +++ 2 files changed, 139 insertions(+), 84 deletions(-)
drivers/clk/clk-divider.c +128 -84
@@ -30,7 +30,7 @@#define to_clk_divider (_hw) container_of(_hw, struct clk_divider, hw) -#define div_mask (d) ((1 << ((d)->width)) - 1) +#define div_mask (width) ((1 << (width)) - 1)static unsigned int _get_table_maxdiv (const struct clk_div_table *table) {
@@ -54,15 +54,16 @@ static unsigned int _get_table_mindiv(const struct clk_div_table *table)return mindiv; } -static unsigned int _get_maxdiv (struct clk_divider *divider) +static unsigned int _get_maxdiv (const struct clk_div_table *table,u8 width, +unsigned long flags) { -if (divider->flags &CLK_DIVIDER_ONE_BASED ) -return div_mask (divider); -if (divider->flags &CLK_DIVIDER_POWER_OF_TWO ) -return 1 <<div_mask (divider); -if (divider->table) -return _get_table_maxdiv (divider->table); -return div_mask (divider) +1 ; +if (flags &CLK_DIVIDER_ONE_BASED ) +return div_mask (width); +if (flags &CLK_DIVIDER_POWER_OF_TWO ) +return 1 <<div_mask (width); +if (table) +return _get_table_maxdiv (table); +return div_mask (width) +1 ; } static unsigned int _get_table_div(const struct clk_div_table *table,
@@ -76,14 +77,15 @@ static unsigned int _get_table_div(const struct clk_div_table *table,return 0 ; } -static unsigned int _get_div (struct clk_divider *divider,unsigned int val) +static unsigned int _get_div (const struct clk_div_table *table, +unsigned int val,unsigned long flags) { -if (divider->flags &CLK_DIVIDER_ONE_BASED ) +if (flags &CLK_DIVIDER_ONE_BASED )return val; -if (divider->flags &CLK_DIVIDER_POWER_OF_TWO ) +if (flags &CLK_DIVIDER_POWER_OF_TWO )return 1 << val; -if (divider->table) -return _get_table_div (divider->table, val); +if (table) +return _get_table_div (table, val);return val +1 ; }
@@ -98,29 +100,28 @@ static unsigned int _get_table_val(const struct clk_div_table *table,return 0 ; } -static unsigned int _get_val (struct clk_divider *divider,unsigned int div) +static unsigned int _get_val (const struct clk_div_table *table, +unsigned int div,unsigned long flags) { -if (divider->flags &CLK_DIVIDER_ONE_BASED ) +if (flags &CLK_DIVIDER_ONE_BASED )return div; -if (divider->flags &CLK_DIVIDER_POWER_OF_TWO ) +if (flags &CLK_DIVIDER_POWER_OF_TWO )return __ffs (div); -if (divider->table) -return _get_table_val (divider->table, div); +if (table) +return _get_table_val (table, div);return div -1 ; } -static unsigned long clk_divider_recalc_rate (struct clk_hw *hw, -unsigned long parent_rate) +unsigned long divider_recalc_rate (struct clk_hw *hw,unsigned long parent_rate, +unsigned int val, +const struct clk_div_table *table, +unsigned long flags) { -struct clk_divider *divider =to_clk_divider (hw); -unsigned int div, val; +unsigned int div; - val =clk_readl (divider->reg) >> divider->shift; - val &=div_mask (divider); - - div =_get_div (divider, val); + div =_get_div (table, val, flags);if (!div) { -WARN (!(divider->flags &CLK_DIVIDER_ALLOW_ZERO ), +WARN (!(flags &CLK_DIVIDER_ALLOW_ZERO ),"%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n" ,__clk_get_name (hw->clk));return parent_rate;
@@ -128,6 +129,20 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw,return DIV_ROUND_UP (parent_rate, div); } +EXPORT_SYMBOL_GPL (divider_recalc_rate); + +static unsigned long clk_divider_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_divider *divider =to_clk_divider (hw); +unsigned int val; + + val =clk_readl (divider->reg) >> divider->shift; + val &=div_mask (divider->width); + +return divider_recalc_rate (hw, parent_rate, val, divider->table, + divider->flags); +}/* * The reverse of DIV_ROUND_UP: The maximum number which
@@ -146,12 +161,13 @@ static bool _is_valid_table_div(const struct clk_div_table *table,return false; } -static bool _is_valid_div (struct clk_divider *divider,unsigned int div) +static bool _is_valid_div (const struct clk_div_table *table,unsigned int div, +unsigned long flags) { -if (divider->flags &CLK_DIVIDER_POWER_OF_TWO ) +if (flags &CLK_DIVIDER_POWER_OF_TWO )return is_power_of_2 (div); -if (divider->table) -return _is_valid_table_div (divider->table, div); +if (table) +return _is_valid_table_div (table, div);return true; }
@@ -191,71 +207,76 @@ static int _round_down_table(const struct clk_div_table *table, int div)return down; } -static int _div_round_up (struct clk_divider *divider, -unsigned long parent_rate,unsigned long rate) +static int _div_round_up (const struct clk_div_table *table, +unsigned long parent_rate,unsigned long rate, +unsigned long flags) {int div =DIV_ROUND_UP (parent_rate, rate); -if (divider->flags &CLK_DIVIDER_POWER_OF_TWO ) +if (flags &CLK_DIVIDER_POWER_OF_TWO ) div =__roundup_pow_of_two (div); -if (divider->table) - div =_round_up_table (divider->table, div); +if (table) + div =_round_up_table (table, div);return div; } -static int _div_round_closest (struct clk_divider *divider, -unsigned long parent_rate,unsigned long rate) +static int _div_round_closest (const struct clk_div_table *table, +unsigned long parent_rate,unsigned long rate, +unsigned long flags) {int up, down, div; up = down = div =DIV_ROUND_CLOSEST (parent_rate, rate); -if (divider->flags &CLK_DIVIDER_POWER_OF_TWO ) { +if (flags &CLK_DIVIDER_POWER_OF_TWO ) { up =__roundup_pow_of_two (div); down =__rounddown_pow_of_two (div); - }else if (divider->table) { - up =_round_up_table (divider->table, div); - down =_round_down_table (divider->table, div); + }else if (table) { + up =_round_up_table (table, div); + down =_round_down_table (table, div); }return (up - div) <= (div - down) ? up : down; } -static int _div_round (struct clk_divider *divider,unsigned long parent_rate, -unsigned long rate) +static int _div_round (const struct clk_div_table *table, +unsigned long parent_rate,unsigned long rate, +unsigned long flags) { -if (divider->flags &CLK_DIVIDER_ROUND_CLOSEST ) -return _div_round_closest (divider, parent_rate, rate); +if (flags &CLK_DIVIDER_ROUND_CLOSEST ) +return _div_round_closest (table, parent_rate, rate, flags); -return _div_round_up (divider, parent_rate, rate); +return _div_round_up (table, parent_rate, rate, flags); } -static bool _is_best_div (struct clk_divider *divider, -unsigned long rate,unsigned long now,unsigned long best) +static bool _is_best_div (unsigned long rate,unsigned long now, +unsigned long best,unsigned long flags) { -if (divider->flags &CLK_DIVIDER_ROUND_CLOSEST ) +if (flags &CLK_DIVIDER_ROUND_CLOSEST )return abs (rate - now) <abs (rate - best);return now <= rate && now > best; } -static int _next_div (struct clk_divider *divider,int div) +static int _next_div (const struct clk_div_table *table,int div, +unsigned long flags) { div++; -if (divider->flags &CLK_DIVIDER_POWER_OF_TWO ) +if (flags &CLK_DIVIDER_POWER_OF_TWO )return __roundup_pow_of_two (div); -if (divider->table) -return _round_up_table (divider->table, div); +if (table) +return _round_up_table (table, div);return div; }static int clk_divider_bestdiv (struct clk_hw *hw,unsigned long rate, -unsigned long *best_parent_rate) +unsigned long *best_parent_rate, +const struct clk_div_table *table,u8 width, +unsigned long flags) { -struct clk_divider *divider =to_clk_divider (hw);int i, bestdiv =0 ;unsigned long parent_rate, best =0 , now, maxdiv;unsigned long parent_rate_saved = *best_parent_rate;
@@ -263,19 +284,11 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,if (!rate) rate =1 ; -/* if read only, just return current value */ -if (divider->flags &CLK_DIVIDER_READ_ONLY ) { - bestdiv =readl (divider->reg) >> divider->shift; - bestdiv &=div_mask (divider); - bestdiv =_get_div (divider, bestdiv); -return bestdiv; - } - - maxdiv =_get_maxdiv (divider); + maxdiv =_get_maxdiv (table, width, flags);if (!(__clk_get_flags (hw->clk) &CLK_SET_RATE_PARENT )) { parent_rate = *best_parent_rate; - bestdiv =_div_round (divider, parent_rate, rate); + bestdiv =_div_round (table, parent_rate, rate, flags); bestdiv = bestdiv ==0 ?1 : bestdiv; bestdiv = bestdiv > maxdiv ? maxdiv : bestdiv;return bestdiv;
@@ -287,8 +300,8 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate,*/ maxdiv =min (ULONG_MAX / rate, maxdiv); -for (i =1 ; i <= maxdiv; i =_next_div (divider, i)) { -if (!_is_valid_div (divider, i)) +for (i =1 ; i <= maxdiv; i =_next_div (table, i, flags)) { +if (!_is_valid_div (table, i, flags))continue ;if (rate * i == parent_rate_saved) {/*
@@ -302,7 +315,7 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, parent_rate =__clk_round_rate (__clk_get_parent (hw->clk),MULT_ROUND_UP (rate, i)); now =DIV_ROUND_UP (parent_rate, i); -if (_is_best_div (divider, rate, now, best)) { +if (_is_best_div (rate, now, best, flags)) { bestdiv = i; best = now; *best_parent_rate = parent_rate;
@@ -310,48 +323,79 @@ static int clk_divider_bestdiv(struct clk_hw *hw, unsigned long rate, }if (!bestdiv) { - bestdiv =_get_maxdiv (divider); + bestdiv =_get_maxdiv (table, width, flags); *best_parent_rate =__clk_round_rate (__clk_get_parent (hw->clk),1 ); }return bestdiv; } -static long clk_divider_round_rate (struct clk_hw *hw,unsigned long rate, -unsigned long *prate) +long divider_round_rate (struct clk_hw *hw,unsigned long rate, +unsigned long *prate,const struct clk_div_table *table, +u8 width,unsigned long flags) {int div; - div =clk_divider_bestdiv (hw, rate, prate); + + div =clk_divider_bestdiv (hw, rate, prate, table, width, flags);return DIV_ROUND_UP (*prate, div); } +EXPORT_SYMBOL_GPL (divider_round_rate); -static int clk_divider_set_rate (struct clk_hw *hw,unsigned long rate, -unsigned long parent_rate) +static long clk_divider_round_rate (struct clk_hw *hw,unsigned long rate, +unsigned long *prate) {struct clk_divider *divider =to_clk_divider (hw); +int bestdiv; + +/* if read only, just return current value */ +if (divider->flags &CLK_DIVIDER_READ_ONLY ) { + bestdiv =readl (divider->reg) >> divider->shift; + bestdiv &=div_mask (divider->width); + bestdiv =_get_div (divider->table, bestdiv, divider->flags); +return bestdiv; + } + +return divider_round_rate (hw, rate, prate, divider->table, + divider->width, divider->flags); +} + +int divider_get_val (unsigned long rate,unsigned long parent_rate, +const struct clk_div_table *table,u8 width, +unsigned long flags) +{unsigned int div, value; -unsigned long flags =0 ; -u32 val; div =DIV_ROUND_UP (parent_rate, rate); -if (!_is_valid_div (divider, div)) +if (!_is_valid_div (table, div, flags))return -EINVAL ; - value =_get_val (divider, div); + value =_get_val (table, div, flags); + + return min_t(unsigned int, value, div_mask(width)); +} +EXPORT_SYMBOL_GPL (divider_get_val); + +static int clk_divider_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long parent_rate) +{ +struct clk_divider *divider =to_clk_divider (hw); +unsigned int value; +unsigned long flags =0 ; +u32 val; -if (value >div_mask (divider)) - value =div_mask (divider); + value =divider_get_val (rate, parent_rate, divider->table, + divider->width, divider->flags);if (divider->lock)spin_lock_irqsave (divider->lock, flags);if (divider->flags &CLK_DIVIDER_HIWORD_MASK ) { - val =div_mask (divider) << (divider->shift +16 ); + val =div_mask (divider->width) << (divider->shift +16 ); }else { val =clk_readl (divider->reg); - val &= ~(div_mask (divider) << divider->shift); + val &= ~(div_mask (divider->width) << divider->shift); } val |= value << divider->shift;clk_writel (val, divider->reg);
include/linux/clk-provider.h +11 -0
@@ -352,6 +352,17 @@ struct clk_divider {#define CLK_DIVIDER_READ_ONLY BIT(5)extern const struct clk_ops clk_divider_ops; + +unsigned long divider_recalc_rate (struct clk_hw *hw,unsigned long parent_rate, +unsigned int val,const struct clk_div_table *table, +unsigned long flags); +long divider_round_rate (struct clk_hw *hw,unsigned long rate, +unsigned long *prate,const struct clk_div_table *table, +u8 width,unsigned long flags); +int divider_get_val (unsigned long rate,unsigned long parent_rate, +const struct clk_div_table *table,u8 width, +unsigned long flags); + struct clk *clk_register_divider(struct device *dev, const char *name, const char *parent_name, unsigned long flags, void __iomem *reg, u8 shift, u8 width,
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v3 3/8 clk: qcom: Add support for regmap divider clocks
In reply to Stephen BoydFrom: Josh Cartwright <[email protected]>
Add support for dividers that use regmap instead of readl/writel.
[sboyd at codeaurora.org: Switch to using generic divider code, drop enable/disable, reword commit text]
--- drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-regmap-divider.c | 70 +++++++++++++++++++++++++++++++++++ drivers/clk/qcom/clk-regmap-divider.h | 29 +++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 drivers/clk/qcom/clk-regmap-divider.c create mode 100644 drivers/clk/qcom/clk-regmap-divider.h
drivers/clk/qcom/Makefile +1 -0
@@ -6,6 +6,7 @@ clk-qcom-y += clk-pll.o clk-qcom-y += clk-rcg.o clk-qcom-y += clk-rcg2.o clk-qcom-y += clk-branch.o +clk-qcom-y += clk-regmap-divider.o clk-qcom-y += reset.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
drivers/clk/qcom/clk-regmap-divider.c new file +70 -0
70 lines not on this page · raw message
drivers/clk/qcom/clk-regmap-divider.h new file +29 -0
@@ -0,0 +1,29 @@ +/* +* Copyright (c) 2014, The Linux Foundation. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef __QCOM_CLK_REGMAP_DIVIDER_H__ +#define __QCOM_CLK_REGMAP_DIVIDER_H__ + +#include <linux/clk-provider.h> +#include "clk-regmap.h" + +struct clk_regmap_div { +u32 reg; +u32 shift; +u32 width; +struct clk_regmap clkr; +}; + +extern const struct clk_ops clk_regmap_div_ops; + +#endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v3 4/8 clk: qcom: Add simple regmap based muxes
In reply to Stephen BoydAdd support for muxes that use regmap instead of readl/writel directly. We don't support as many features as clk-mux.c, but this is good enough to support getting and setting parents. Adding a table based lookup can be added in the future if needed.
--- drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-regmap-mux.c | 59 +++++++++++++++++++++++++++++++++++++++ drivers/clk/qcom/clk-regmap-mux.h | 29 +++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 drivers/clk/qcom/clk-regmap-mux.c create mode 100644 drivers/clk/qcom/clk-regmap-mux.h
drivers/clk/qcom/Makefile +1 -0
@@ -7,6 +7,7 @@ clk-qcom-y += clk-rcg.o clk-qcom-y += clk-rcg2.o clk-qcom-y += clk-branch.o clk-qcom-y += clk-regmap-divider.o +clk-qcom-y += clk-regmap-mux.o clk-qcom-y += reset.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o
drivers/clk/qcom/clk-regmap-mux.c new file +59 -0
59 lines not on this page · raw message
drivers/clk/qcom/clk-regmap-mux.h new file +29 -0
@@ -0,0 +1,29 @@ +/* +* Copyright (c) 2014, The Linux Foundation. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef __QCOM_CLK_REGMAP_MUX_H__ +#define __QCOM_CLK_REGMAP_MUX_H__ + +#include <linux/clk-provider.h> +#include "clk-regmap.h" + +struct clk_regmap_mux { +u32 reg; +u32 shift; +u32 width; +struct clk_regmap clkr; +}; + +extern const struct clk_ops clk_regmap_mux_closest_ops; + +#endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v3 5/8 dt-bindings: Add #defines for IPQ806x lpass clock control
In reply to Stephen BoydFrom: Rajendra Nayak <[email protected]>
Add defines to make more human readable numbers for the lpass clock controller found on IPQ806x SoCs. Also remove the PLL4 define in gcc to avoid #define conflicts because that clock doesn't exist in gcc, instead it lives in lcc.
--- include/dt-bindings/clock/qcom,gcc-ipq806x.h | 1 - include/dt-bindings/clock/qcom,lcc-ipq806x.h | 30 ++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 include/dt-bindings/clock/qcom,lcc-ipq806x.h
include/dt-bindings/clock/qcom,gcc-ipq806x.h +0 -1
@@ -238,7 +238,6 @@#define PLL0_VOTE 221#define PLL3 222#define PLL3_VOTE 223 -#define PLL4 224#define PLL4_VOTE 225#define PLL8 226#define PLL8_VOTE 227
include/dt-bindings/clock/qcom,lcc-ipq806x.h new file +30 -0
@@ -0,0 +1,30 @@ +/* +* Copyright (c) 2014, The Linux Foundation. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _DT_BINDINGS_CLK_LCC_IPQ806X_H +#define _DT_BINDINGS_CLK_LCC_IPQ806X_H + +#define PLL4 0 +#define MI2S_OSR_SRC 1 +#define MI2S_OSR_CLK 2 +#define MI2S_DIV_CLK 3 +#define MI2S_BIT_DIV_CLK 4 +#define MI2S_BIT_CLK 5 +#define PCM_SRC 6 +#define PCM_CLK_OUT 7 +#define PCM_CLK 8 +#define SPDIF_SRC 9 +#define SPDIF_CLK 10 +#define AHBIX_CLK 11 + +#endif
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v3 6/8 clk: qcom: Add IPQ806X LPASS clock controller (LCC) driver
In reply to Stephen BoydFrom: Rajendra Nayak <[email protected]>
Add an LCC driver for IPQ806x that supports the i2s, S/PDIF, and pcm clocks.
[sboyd at codeaurora.org: Reworded commit text, added Kconfig select, fleshed out Kconfig description a bit more, added pll4 configuration and reworked probe for it, added muxes, split out dt-binding file]
Change-Id: Idf4d008306a98d75d914c5625b46d15c6f7921b4
--- drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/gcc-ipq806x.c | 12 ++ drivers/clk/qcom/lcc-ipq806x.c | 473 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 495 insertions(+) create mode 100644 drivers/clk/qcom/lcc-ipq806x.c
drivers/clk/qcom/Kconfig +9 -0
@@ -29,6 +29,15 @@ config IPQ_GCC_806X Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, etc. +config IPQ_LCC_806X + tristate "IPQ806x LPASS Clock Controller" + select IPQ_GCC_806X + depends on COMMON_CLK_QCOM + help + Support for the LPASS clock controller on ipq806x devices. + Say Y if you want to use audio devices such as i2s, pcm, + S/PDIF, etc. + config MSM_GCC_8660 tristate "MSM8660 Global Clock Controller" depends on COMMON_CLK_QCOM
drivers/clk/qcom/Makefile +1 -0
@@ -13,6 +13,7 @@ clk-qcom-y += reset.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o
drivers/clk/qcom/gcc-ipq806x.c +12 -0
@@ -75,6 +75,17 @@ static struct clk_pll pll3 = { }, }; +static struct clk_regmap pll4_vote = { + .enable_reg =0x34c0 , + .enable_mask =BIT (4 ), + .hw.init = &(struct clk_init_data ){ + .name ="pll4_vote" , + .parent_names = (const char *[]){"pll4" }, + .num_parents =1 , + .ops = &clk_pll_vote_ops, + }, +}; +static struct clk_pll pll8 = { .l_reg =0x3144 , .m_reg =0x3148 ,
@@ -2163,6 +2174,7 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { [PLL0] = &pll0.clkr, [PLL0_VOTE] = &pll0_vote, [PLL3] = &pll3.clkr, + [PLL4_VOTE] = &pll4_vote, [PLL8] = &pll8.clkr, [PLL8_VOTE] = &pll8_vote, [PLL14] = &pll14.clkr,
drivers/clk/qcom/lcc-ipq806x.c new file +473 -0
473 lines not on this page · raw message
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v3 6/8] clk: qcom: Add IPQ806X LPASS clock controller (LCC) driver
Quoting Stephen Boyd (2015-01-19 18:05:33)
Quoted 8 lines
From: Rajendra Nayak <[email protected]>
Add an LCC driver for IPQ806x that supports the i2s, S/PDIF, and pcm clocks.
Signed-off-by Rajendra Nayak <[email protected]>Signed-off-by Kumar Gala <[email protected]>Signed-off-by Josh Cartwright <[email protected]>
Quoted 7 lines
[sboyd at codeaurora.org: Reworded commit text, added
Kconfigselect, fleshed outKconfigdescription a bit more, added pll4 configuration and reworked probe for it, added muxes, split out dt-binding file]Signed-off-by Stephen Boyd <[email protected]>Change-Id: Idf4d008306a98d75d914c5625b46d15c6f7921b4
I stripped out Change-Id while applying.
Regards,
Mike
Quoted 553 lines
--- drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/gcc-ipq806x.c | 12 ++ drivers/clk/qcom/lcc-ipq806x.c | 473 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 495 insertions(+) create mode 100644 drivers/clk/qcom/lcc-ipq806x.cdrivers/clk/qcom/Kconfig +9
-0@@ -29,6 +29,15 @@ config IPQ_GCC_806X Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, etc. +config IPQ_LCC_806X + tristate "IPQ806x LPASS Clock Controller" + select IPQ_GCC_806X + depends on COMMON_CLK_QCOM + help + Support for the LPASS clock controller on ipq806x devices. + Say Y if you want to use audio devices such as i2s, pcm, + S/PDIF, etc. + config MSM_GCC_8660 tristate "MSM8660 Global Clock Controller" depends on COMMON_CLK_QCOMdrivers/clk/qcom/Makefile +1
-0@@ -13,6 +13,7 @@ clk-qcom-y += reset.o obj-$(CONFIG_APQ_GCC_8084) += gcc-apq8084.o obj-$(CONFIG_APQ_MMCC_8084) += mmcc-apq8084.o obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o +obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.odrivers/clk/qcom/gcc-ipq806x.c +12
-0@@ -75,6 +75,17 @@ static struct clk_pll pll3 = { }, }; +static struct clk_regmap pll4_vote = { + .enable_reg =0x34c0 , + .enable_mask =BIT (4 ), + .hw.init = &(struct clk_init_data ){ + .name ="pll4_vote" , + .parent_names = (const char *[]){"pll4" }, + .num_parents =1 , + .ops = &clk_pll_vote_ops, + }, +}; +static struct clk_pll pll8 = { .l_reg =0x3144 , .m_reg =0x3148 ,@@ -2163,6 +2174,7 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { [PLL0] = &pll0.clkr, [PLL0_VOTE] = &pll0_vote, [PLL3] = &pll3.clkr, + [PLL4_VOTE] = &pll4_vote, [PLL8] = &pll8.clkr, [PLL8_VOTE] = &pll8_vote, [PLL14] = &pll14.clkr,drivers/clk/qcom/lcc-ipq806x.c new file +473
-0@@ -0,0 +1,473 @@ +/* +* Copyright (c) 2014, The Linux Foundation. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,lcc-ipq806x.h> + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" + +static struct clk_pll pll4 = { + .l_reg =0x4 , + .m_reg =0x8 , + .n_reg =0xc , + .config_reg =0x14 , + .mode_reg =0x0 , + .status_reg =0x18 , + .status_bit =16 , + .clkr.hw.init = &(struct clk_init_data ){ + .name ="pll4" , + .parent_names = (const char *[]){"pxo" }, + .num_parents =1 , + .ops = &clk_pll_ops, + }, +}; + +static const struct pll_config pll4_config = { + .l =0xf , + .m =0x91 , + .n =0xc7 , + .vco_val =0x0 , + .vco_mask =BIT (17 ) |BIT (16 ), + .pre_div_val =0x0 , + .pre_div_mask =BIT (19 ), + .post_div_val =0x0 , + .post_div_mask =BIT (21 ) |BIT (20 ), + .mn_ena_mask =BIT (22 ), + .main_output_mask =BIT (23 ), +}; + +#define P_PXO 0 +#define P_PLL4 1 + +static const u8 lcc_pxo_pll4_map[] = { + [P_PXO ] =0 , + [P_PLL4 ] =2 , +}; + +static const char *lcc_pxo_pll4[] = { +"pxo" , +"pll4_vote" , +}; + +static struct freq_tbl clk_tbl_aif_mi2s[] = { + {1024000 ,P_PLL4 ,4 ,1 ,96 }, + {1411200 ,P_PLL4 ,4 ,2 ,139 }, + {1536000 ,P_PLL4 ,4 ,1 ,64 }, + {2048000 ,P_PLL4 ,4 ,1 ,48 }, + {2116800 ,P_PLL4 ,4 ,2 ,93 }, + {2304000 ,P_PLL4 ,4 ,2 ,85 }, + {2822400 ,P_PLL4 ,4 ,6 ,209 }, + {3072000 ,P_PLL4 ,4 ,1 ,32 }, + {3175200 ,P_PLL4 ,4 ,1 ,31 }, + {4096000 ,P_PLL4 ,4 ,1 ,24 }, + {4233600 ,P_PLL4 ,4 ,9 ,209 }, + {4608000 ,P_PLL4 ,4 ,3 ,64 }, + {5644800 ,P_PLL4 ,4 ,12 ,209 }, + {6144000 ,P_PLL4 ,4 ,1 ,16 }, + {6350400 ,P_PLL4 ,4 ,2 ,31 }, + {8192000 ,P_PLL4 ,4 ,1 ,12 }, + {8467200 ,P_PLL4 ,4 ,18 ,209 }, + {9216000 ,P_PLL4 ,4 ,3 ,32 }, + {11289600 ,P_PLL4 ,4 ,24 ,209 }, + {12288000 ,P_PLL4 ,4 ,1 ,8 }, + {12700800 ,P_PLL4 ,4 ,27 ,209 }, + {13824000 ,P_PLL4 ,4 ,9 ,64 }, + {16384000 ,P_PLL4 ,4 ,1 ,6 }, + {16934400 ,P_PLL4 ,4 ,41 ,238 }, + {18432000 ,P_PLL4 ,4 ,3 ,16 }, + {22579200 ,P_PLL4 ,2 ,24 ,209 }, + {24576000 ,P_PLL4 ,4 ,1 ,4 }, + {27648000 ,P_PLL4 ,4 ,9 ,32 }, + {33868800 ,P_PLL4 ,4 ,41 ,119 }, + {36864000 ,P_PLL4 ,4 ,3 ,8 }, + {45158400 ,P_PLL4 ,1 ,24 ,209 }, + {49152000 ,P_PLL4 ,4 ,1 ,2 }, + {50803200 ,P_PLL4 ,1 ,27 ,209 }, + { } +}; + +static struct clk_rcg mi2s_osr_src = { + .ns_reg =0x48 , + .md_reg =0x4c , + .mn = { + .mnctr_en_bit =8 , + .mnctr_reset_bit =7 , + .mnctr_mode_shift =5 , + .n_val_shift =24 , + .m_val_shift =8 , + .width =8 , + }, + .p = { + .pre_div_shift =3 , + .pre_div_width =2 , + }, + .s = { + .src_sel_shift =0 , + .parent_map = lcc_pxo_pll4_map, + }, + .freq_tbl = clk_tbl_aif_mi2s, + .clkr = { + .enable_reg =0x48 , + .enable_mask =BIT (9 ), + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_osr_src" , + .parent_names = lcc_pxo_pll4, + .num_parents =2 , + .ops = &clk_rcg_ops, + .flags =CLK_SET_RATE_GATE , + }, + }, +}; + +static const char *lcc_mi2s_parents[] = { +"mi2s_osr_src" , +}; + +static struct clk_branch mi2s_osr_clk = { + .halt_reg =0x50 , + .halt_bit =1 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0x48 , + .enable_mask =BIT (17 ), + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_osr_clk" , + .parent_names = lcc_mi2s_parents, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_regmap_div mi2s_div_clk = { + .reg =0x48 , + .shift =10 , + .width =4 , + .clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_div_clk" , + .parent_names = lcc_mi2s_parents, + .num_parents =1 , + .ops = &clk_regmap_div_ops, + }, + }, +}; + +static struct clk_branch mi2s_bit_div_clk = { + .halt_reg =0x50 , + .halt_bit =0 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0x48 , + .enable_mask =BIT (15 ), + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_bit_div_clk" , + .parent_names = (const char *[]){"mi2s_div_clk" }, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + + +static struct clk_regmap_mux mi2s_bit_clk = { + .reg =0x48 , + .shift =14 , + .width =1 , + .clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_bit_clk" , + .parent_names = (const char *[]){ +"mi2s_bit_div_clk" , +"mi2s_codec_clk" , + }, + .num_parents =2 , + .ops = &clk_regmap_mux_closest_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct freq_tbl clk_tbl_pcm[] = { + {64000 ,P_PLL4 ,4 ,1 ,1536 }, + {128000 ,P_PLL4 ,4 ,1 ,768 }, + {256000 ,P_PLL4 ,4 ,1 ,384 }, + {512000 ,P_PLL4 ,4 ,1 ,192 }, + {1024000 ,P_PLL4 ,4 ,1 ,96 }, + {2048000 ,P_PLL4 ,4 ,1 ,48 }, + { }, +}; + +static struct clk_rcg pcm_src = { + .ns_reg =0x54 , + .md_reg =0x58 , + .mn = { + .mnctr_en_bit =8 , + .mnctr_reset_bit =7 , + .mnctr_mode_shift =5 , + .n_val_shift =16 , + .m_val_shift =16 , + .width =16 , + }, + .p = { + .pre_div_shift =3 , + .pre_div_width =2 , + }, + .s = { + .src_sel_shift =0 , + .parent_map = lcc_pxo_pll4_map, + }, + .freq_tbl = clk_tbl_pcm, + .clkr = { + .enable_reg =0x54 , + .enable_mask =BIT (9 ), + .hw.init = &(struct clk_init_data ){ + .name ="pcm_src" , + .parent_names = lcc_pxo_pll4, + .num_parents =2 , + .ops = &clk_rcg_ops, + .flags =CLK_SET_RATE_GATE , + }, + }, +}; + +static struct clk_branch pcm_clk_out = { + .halt_reg =0x5c , + .halt_bit =0 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0x54 , + .enable_mask =BIT (11 ), + .hw.init = &(struct clk_init_data ){ + .name ="pcm_clk_out" , + .parent_names = (const char *[]){"pcm_src" }, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_regmap_mux pcm_clk = { + .reg =0x54 , + .shift =10 , + .width =1 , + .clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="pcm_clk" , + .parent_names = (const char *[]){ +"pcm_clk_out" , +"pcm_codec_clk" , + }, + .num_parents =2 , + .ops = &clk_regmap_mux_closest_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct freq_tbl clk_tbl_aif_osr[] = { + {22050 ,P_PLL4 ,1 ,147 ,20480 }, + {32000 ,P_PLL4 ,1 ,1 ,96 }, + {44100 ,P_PLL4 ,1 ,147 ,10240 }, + {48000 ,P_PLL4 ,1 ,1 ,64 }, + {88200 ,P_PLL4 ,1 ,147 ,5120 }, + {96000 ,P_PLL4 ,1 ,1 ,32 }, + {176400 ,P_PLL4 ,1 ,147 ,2560 }, + {192000 ,P_PLL4 ,1 ,1 ,16 }, + { }, +}; + +static struct clk_rcg spdif_src = { + .ns_reg =0xcc , + .md_reg =0xd0 , + .mn = { + .mnctr_en_bit =8 , + .mnctr_reset_bit =7 , + .mnctr_mode_shift =5 , + .n_val_shift =16 , + .m_val_shift =16 , + .width =8 , + }, + .p = { + .pre_div_shift =3 , + .pre_div_width =2 , + }, + .s = { + .src_sel_shift =0 , + .parent_map = lcc_pxo_pll4_map, + }, + .freq_tbl = clk_tbl_aif_osr, + .clkr = { + .enable_reg =0xcc , + .enable_mask =BIT (9 ), + .hw.init = &(struct clk_init_data ){ + .name ="spdif_src" , + .parent_names = lcc_pxo_pll4, + .num_parents =2 , + .ops = &clk_rcg_ops, + .flags =CLK_SET_RATE_GATE , + }, + }, +}; + +static const char *lcc_spdif_parents[] = { +"spdif_src" , +}; + +static struct clk_branch spdif_clk = { + .halt_reg =0xd4 , + .halt_bit =1 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0xcc , + .enable_mask =BIT (12 ), + .hw.init = &(struct clk_init_data ){ + .name ="spdif_clk" , + .parent_names = lcc_spdif_parents, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct freq_tbl clk_tbl_ahbix[] = { + {131072 ,P_PLL4 ,1 ,1 ,3 }, + { }, +}; + +static struct clk_rcg ahbix_clk = { + .ns_reg =0x38 , + .md_reg =0x3c , + .mn = { + .mnctr_en_bit =8 , + .mnctr_reset_bit =7 , + .mnctr_mode_shift =5 , + .n_val_shift =24 , + .m_val_shift =8 , + .width =8 , + }, + .p = { + .pre_div_shift =3 , + .pre_div_width =2 , + }, + .s = { + .src_sel_shift =0 , + .parent_map = lcc_pxo_pll4_map, + }, + .freq_tbl = clk_tbl_ahbix, + .clkr = { + .enable_reg =0x38 , + .enable_mask =BIT (10 ),/* toggle the gfmux to select mn/pxo */ + .hw.init = &(struct clk_init_data ){ + .name ="ahbix" , + .parent_names = lcc_pxo_pll4, + .num_parents =2 , + .ops = &clk_rcg_ops, + .flags =CLK_SET_RATE_GATE , + }, + }, +}; + +static struct clk_regmap *lcc_ipq806x_clks[] = { + [PLL4 ] = &pll4.clkr, + [MI2S_OSR_SRC ] = &mi2s_osr_src.clkr, + [MI2S_OSR_CLK ] = &mi2s_osr_clk.clkr, + [MI2S_DIV_CLK ] = &mi2s_div_clk.clkr, + [MI2S_BIT_DIV_CLK ] = &mi2s_bit_div_clk.clkr, + [MI2S_BIT_CLK ] = &mi2s_bit_clk.clkr, + [PCM_SRC ] = &pcm_src.clkr, + [PCM_CLK_OUT ] = &pcm_clk_out.clkr, + [PCM_CLK ] = &pcm_clk.clkr, + [SPDIF_SRC ] = &spdif_src.clkr, + [SPDIF_CLK ] = &spdif_clk.clkr, + [AHBIX_CLK ] = &ahbix_clk.clkr, +}; + +static const struct regmap_config lcc_ipq806x_regmap_config = { + .reg_bits =32 , + .reg_stride =4 , + .val_bits =32 , + .max_register =0xfc , + .fast_io = true, +}; + +static const struct qcom_cc_desc lcc_ipq806x_desc = { + .config = &lcc_ipq806x_regmap_config, + .clks = lcc_ipq806x_clks, + .num_clks =ARRAY_SIZE (lcc_ipq806x_clks), +}; + +static const struct of_device_id lcc_ipq806x_match_table[] = { + { .compatible ="qcom,lcc-ipq8064" }, + { } +}; +MODULE_DEVICE_TABLE (of, lcc_ipq806x_match_table); + +static int lcc_ipq806x_probe (struct platform_device *pdev) +{ +u32 val; +struct regmap *regmap; + + regmap =qcom_cc_map (pdev, &lcc_ipq806x_desc); +if (IS_ERR (regmap)) +return PTR_ERR (regmap); + +/* Configure the rate of PLL4 if the bootloader hasn't already */ + val =regmap_read (regmap,0x0 , &val); +if (!val) +clk_pll_configure_sr (&pll4, regmap, &pll4_config, true); +/* Enable PLL4 source on the LPASS Primary PLL Mux */ +regmap_write (regmap,0xc4 ,0x1 ); + +return qcom_cc_really_probe (pdev, &lcc_ipq806x_desc, regmap); +} + +static int lcc_ipq806x_remove (struct platform_device *pdev) +{ +qcom_cc_remove (pdev); +return 0 ; +} + +static struct platform_driver lcc_ipq806x_driver = { + .probe = lcc_ipq806x_probe, + .remove = lcc_ipq806x_remove, + .driver = { + .name ="lcc-ipq806x" , + .owner =THIS_MODULE , + .of_match_table = lcc_ipq806x_match_table, + }, +}; +module_platform_driver (lcc_ipq806x_driver); + +MODULE_DESCRIPTION ("QCOM LCC IPQ806x Driver" ); +MODULE_LICENSE ("GPL v2" ); +MODULE_ALIAS ("platform:lcc-ipq806x" );Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v3 7/8 clk: qcom: Add MSM8960/APQ8064 LPASS clock controller (LCC) driver
In reply to Stephen BoydAdd an LCC driver for MSM8960/APQ8064 that supports the i2s, slimbus, and pcm clocks.
--- drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/lcc-msm8960.c | 585 +++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,lcc-msm8960.h | 50 +++ 4 files changed, 645 insertions(+) create mode 100644 drivers/clk/qcom/lcc-msm8960.c create mode 100644 include/dt-bindings/clock/qcom,lcc-msm8960.h
drivers/clk/qcom/Kconfig +9 -0
@@ -54,6 +54,15 @@ config MSM_GCC_8960 Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, SATA, PCIe, etc. +config MSM_LCC_8960 + tristate "APQ8064/MSM8960 LPASS Clock Controller" + select MSM_GCC_8960 + depends on COMMON_CLK_QCOM + help + Support for the LPASS clock controller on apq8064/msm8960 devices. + Say Y if you want to use audio devices such as i2s, pcm, + SLIMBus, etc. + config MSM_MMCC_8960 tristate "MSM8960 Multimedia Clock Controller" select MSM_GCC_8960
drivers/clk/qcom/Makefile +1 -0
@@ -16,6 +16,7 @@ obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o +obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.o
drivers/clk/qcom/lcc-msm8960.c new file +585 -0
585 lines not on this page · raw message
include/dt-bindings/clock/qcom,lcc-msm8960.h new file +50 -0
50 lines not on this page · raw message
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v3 7/8] clk: qcom: Add MSM8960/APQ8064 LPASS clock controller (LCC) driver
Quoting Stephen Boyd (2015-01-19 18:05:34)
Quoted 4 lines
Add an LCC driver for MSM8960/APQ8064 that supports the i2s, slimbus, and pcm clocks.
Change-Id: I2549b821f7bf467c1bd80d4827a1a7621e725659
Removed above line while applying.
Regards,
Mike
Quoted 693 lines
Signed-off-by Stephen Boyd <[email protected]>--- drivers/clk/qcom/Kconfig | 9 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/lcc-msm8960.c | 585 +++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,lcc-msm8960.h | 50 +++ 4 files changed, 645 insertions(+) create mode 100644 drivers/clk/qcom/lcc-msm8960.c create mode 100644 include/dt-bindings/clock/qcom,lcc-msm8960.hdrivers/clk/qcom/Kconfig +9
-0@@ -54,6 +54,15 @@ config MSM_GCC_8960 Say Y if you want to use peripheral devices such as UART, SPI, i2c, USB, SD/eMMC, SATA, PCIe, etc. +config MSM_LCC_8960 + tristate "APQ8064/MSM8960 LPASS Clock Controller" + select MSM_GCC_8960 + depends on COMMON_CLK_QCOM + help + Support for the LPASS clock controller on apq8064/msm8960 devices. + Say Y if you want to use audio devices such as i2s, pcm, + SLIMBus, etc. + config MSM_MMCC_8960 tristate "MSM8960 Multimedia Clock Controller" select MSM_GCC_8960drivers/clk/qcom/Makefile +1
-0@@ -16,6 +16,7 @@ obj-$(CONFIG_IPQ_GCC_806X) += gcc-ipq806x.o obj-$(CONFIG_IPQ_LCC_806X) += lcc-ipq806x.o obj-$(CONFIG_MSM_GCC_8660) += gcc-msm8660.o obj-$(CONFIG_MSM_GCC_8960) += gcc-msm8960.o +obj-$(CONFIG_MSM_LCC_8960) += lcc-msm8960.o obj-$(CONFIG_MSM_GCC_8974) += gcc-msm8974.o obj-$(CONFIG_MSM_MMCC_8960) += mmcc-msm8960.o obj-$(CONFIG_MSM_MMCC_8974) += mmcc-msm8974.odrivers/clk/qcom/lcc-msm8960.c new file +585
-0@@ -0,0 +1,585 @@ +/* +* Copyright (c) 2014, The Linux Foundation. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/clk-provider.h> +#include <linux/regmap.h> + +#include <dt-bindings/clock/qcom,lcc-msm8960.h> + +#include "common.h" +#include "clk-regmap.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-branch.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" + +static struct clk_pll pll4 = { + .l_reg =0x4 , + .m_reg =0x8 , + .n_reg =0xc , + .config_reg =0x14 , + .mode_reg =0x0 , + .status_reg =0x18 , + .status_bit =16 , + .clkr.hw.init = &(struct clk_init_data ){ + .name ="pll4" , + .parent_names = (const char *[]){"pxo" }, + .num_parents =1 , + .ops = &clk_pll_ops, + }, +}; + +#define P_PXO 0 +#define P_PLL4 1 + +static const u8 lcc_pxo_pll4_map[] = { + [P_PXO ] =0 , + [P_PLL4 ] =2 , +}; + +static const char *lcc_pxo_pll4[] = { +"pxo" , +"pll4_vote" , +}; + +static struct freq_tbl clk_tbl_aif_osr_492[] = { + {512000 ,P_PLL4 ,4 ,1 ,240 }, + {768000 ,P_PLL4 ,4 ,1 ,160 }, + {1024000 ,P_PLL4 ,4 ,1 ,120 }, + {1536000 ,P_PLL4 ,4 ,1 ,80 }, + {2048000 ,P_PLL4 ,4 ,1 ,60 }, + {3072000 ,P_PLL4 ,4 ,1 ,40 }, + {4096000 ,P_PLL4 ,4 ,1 ,30 }, + {6144000 ,P_PLL4 ,4 ,1 ,20 }, + {8192000 ,P_PLL4 ,4 ,1 ,15 }, + {12288000 ,P_PLL4 ,4 ,1 ,10 }, + {24576000 ,P_PLL4 ,4 ,1 ,5 }, + {27000000 ,P_PXO ,1 ,0 ,0 }, + { } +}; + +static struct freq_tbl clk_tbl_aif_osr_393[] = { + {512000 ,P_PLL4 ,4 ,1 ,192 }, + {768000 ,P_PLL4 ,4 ,1 ,128 }, + {1024000 ,P_PLL4 ,4 ,1 ,96 }, + {1536000 ,P_PLL4 ,4 ,1 ,64 }, + {2048000 ,P_PLL4 ,4 ,1 ,48 }, + {3072000 ,P_PLL4 ,4 ,1 ,32 }, + {4096000 ,P_PLL4 ,4 ,1 ,24 }, + {6144000 ,P_PLL4 ,4 ,1 ,16 }, + {8192000 ,P_PLL4 ,4 ,1 ,12 }, + {12288000 ,P_PLL4 ,4 ,1 ,8 }, + {24576000 ,P_PLL4 ,4 ,1 ,4 }, + {27000000 ,P_PXO ,1 ,0 ,0 }, + { } +}; + +static struct clk_rcg mi2s_osr_src = { + .ns_reg =0x48 , + .md_reg =0x4c , + .mn = { + .mnctr_en_bit =8 , + .mnctr_reset_bit =7 , + .mnctr_mode_shift =5 , + .n_val_shift =24 , + .m_val_shift =8 , + .width =8 , + }, + .p = { + .pre_div_shift =3 , + .pre_div_width =2 , + }, + .s = { + .src_sel_shift =0 , + .parent_map = lcc_pxo_pll4_map, + }, + .freq_tbl = clk_tbl_aif_osr_393, + .clkr = { + .enable_reg =0x48 , + .enable_mask =BIT (9 ), + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_osr_src" , + .parent_names = lcc_pxo_pll4, + .num_parents =2 , + .ops = &clk_rcg_ops, + .flags =CLK_SET_RATE_GATE , + }, + }, +}; + +static const char *lcc_mi2s_parents[] = { +"mi2s_osr_src" , +}; + +static struct clk_branch mi2s_osr_clk = { + .halt_reg =0x50 , + .halt_bit =1 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0x48 , + .enable_mask =BIT (17 ), + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_osr_clk" , + .parent_names = lcc_mi2s_parents, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_regmap_div mi2s_div_clk = { + .reg =0x48 , + .shift =10 , + .width =4 , + .clkr = { + .enable_reg =0x48 , + .enable_mask =BIT (15 ), + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_div_clk" , + .parent_names = lcc_mi2s_parents, + .num_parents =1 , + .ops = &clk_regmap_div_ops, + }, + }, +}; + +static struct clk_branch mi2s_bit_div_clk = { + .halt_reg =0x50 , + .halt_bit =0 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0x48 , + .enable_mask =BIT (15 ), + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_bit_div_clk" , + .parent_names = (const char *[]){"mi2s_div_clk" }, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_regmap_mux mi2s_bit_clk = { + .reg =0x48 , + .shift =14 , + .width =1 , + .clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="mi2s_bit_clk" , + .parent_names = (const char *[]){ +"mi2s_bit_div_clk" , +"mi2s_codec_clk" , + }, + .num_parents =2 , + .ops = &clk_regmap_mux_closest_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +#define CLK_AIF_OSR_DIV (prefix, _ns, _md, hr) \ +static struct clk_rcg prefix##_osr_src = { \ + .ns_reg = _ns, \ + .md_reg = _md, \ + .mn = { \ + .mnctr_en_bit = 8, \ + .mnctr_reset_bit = 7, \ + .mnctr_mode_shift = 5, \ + .n_val_shift = 24, \ + .m_val_shift = 8, \ + .width = 8, \ + }, \ + .p = { \ + .pre_div_shift = 3, \ + .pre_div_width = 2, \ + }, \ + .s = { \ + .src_sel_shift = 0, \ + .parent_map = lcc_pxo_pll4_map, \ + }, \ + .freq_tbl = clk_tbl_aif_osr_393, \ + .clkr = { \ + .enable_reg = _ns, \ + .enable_mask = BIT(9), \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_osr_src", \ + .parent_names = lcc_pxo_pll4, \ + .num_parents = 2, \ + .ops = &clk_rcg_ops, \ + .flags = CLK_SET_RATE_GATE, \ + }, \ + }, \ +}; \ + \ +static const char *lcc_##prefix##_parents[] = { \ + #prefix "_osr_src", \ +}; \ + \ +static struct clk_branch prefix##_osr_clk = { \ + .halt_reg = hr, \ + .halt_bit = 1, \ + .halt_check = BRANCH_HALT_ENABLE, \ + .clkr = { \ + .enable_reg = _ns, \ + .enable_mask = BIT(21), \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_osr_clk", \ + .parent_names = lcc_##prefix##_parents, \ + .num_parents = 1, \ + .ops = &clk_branch_ops, \ + .flags = CLK_SET_RATE_PARENT, \ + }, \ + }, \ +}; \ + \ +static struct clk_regmap_div prefix##_div_clk = { \ + .reg = _ns, \ + .shift = 10, \ + .width = 8, \ + .clkr = { \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_div_clk", \ + .parent_names = lcc_##prefix##_parents, \ + .num_parents = 1, \ + .ops = &clk_regmap_div_ops, \ + }, \ + }, \ +}; \ + \ +static struct clk_branch prefix##_bit_div_clk = { \ + .halt_reg = hr, \ + .halt_bit = 0, \ + .halt_check = BRANCH_HALT_ENABLE, \ + .clkr = { \ + .enable_reg = _ns, \ + .enable_mask = BIT(19), \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_bit_div_clk", \ + .parent_names = (const char *[]){ \ + #prefix "_div_clk" \ + }, \ + .num_parents = 1, \ + .ops = &clk_branch_ops, \ + .flags = CLK_SET_RATE_PARENT, \ + }, \ + }, \ +}; \ + \ +static struct clk_regmap_mux prefix##_bit_clk = { \ + .reg = _ns, \ + .shift = 18, \ + .width = 1, \ + .clkr = { \ + .hw.init = &(struct clk_init_data){ \ + .name = #prefix "_bit_clk", \ + .parent_names = (const char *[]){ \ + #prefix "_bit_div_clk", \ + #prefix "_codec_clk", \ + }, \ + .num_parents = 2, \ + .ops = &clk_regmap_mux_closest_ops, \ + .flags = CLK_SET_RATE_PARENT, \ + }, \ + }, \ +} + +CLK_AIF_OSR_DIV (codec_i2s_mic,0x60 ,0x64 ,0x68 ); +CLK_AIF_OSR_DIV (spare_i2s_mic,0x78 ,0x7c ,0x80 ); +CLK_AIF_OSR_DIV (codec_i2s_spkr,0x6c ,0x70 ,0x74 ); +CLK_AIF_OSR_DIV (spare_i2s_spkr,0x84 ,0x88 ,0x8c ); + +static struct freq_tbl clk_tbl_pcm_492[] = { + {256000 ,P_PLL4 ,4 ,1 ,480 }, + {512000 ,P_PLL4 ,4 ,1 ,240 }, + {768000 ,P_PLL4 ,4 ,1 ,160 }, + {1024000 ,P_PLL4 ,4 ,1 ,120 }, + {1536000 ,P_PLL4 ,4 ,1 ,80 }, + {2048000 ,P_PLL4 ,4 ,1 ,60 }, + {3072000 ,P_PLL4 ,4 ,1 ,40 }, + {4096000 ,P_PLL4 ,4 ,1 ,30 }, + {6144000 ,P_PLL4 ,4 ,1 ,20 }, + {8192000 ,P_PLL4 ,4 ,1 ,15 }, + {12288000 ,P_PLL4 ,4 ,1 ,10 }, + {24576000 ,P_PLL4 ,4 ,1 ,5 }, + {27000000 ,P_PXO ,1 ,0 ,0 }, + { } +}; + +static struct freq_tbl clk_tbl_pcm_393[] = { + {256000 ,P_PLL4 ,4 ,1 ,384 }, + {512000 ,P_PLL4 ,4 ,1 ,192 }, + {768000 ,P_PLL4 ,4 ,1 ,128 }, + {1024000 ,P_PLL4 ,4 ,1 ,96 }, + {1536000 ,P_PLL4 ,4 ,1 ,64 }, + {2048000 ,P_PLL4 ,4 ,1 ,48 }, + {3072000 ,P_PLL4 ,4 ,1 ,32 }, + {4096000 ,P_PLL4 ,4 ,1 ,24 }, + {6144000 ,P_PLL4 ,4 ,1 ,16 }, + {8192000 ,P_PLL4 ,4 ,1 ,12 }, + {12288000 ,P_PLL4 ,4 ,1 ,8 }, + {24576000 ,P_PLL4 ,4 ,1 ,4 }, + {27000000 ,P_PXO ,1 ,0 ,0 }, + { } +}; + +static struct clk_rcg pcm_src = { + .ns_reg =0x54 , + .md_reg =0x58 , + .mn = { + .mnctr_en_bit =8 , + .mnctr_reset_bit =7 , + .mnctr_mode_shift =5 , + .n_val_shift =16 , + .m_val_shift =16 , + .width =16 , + }, + .p = { + .pre_div_shift =3 , + .pre_div_width =2 , + }, + .s = { + .src_sel_shift =0 , + .parent_map = lcc_pxo_pll4_map, + }, + .freq_tbl = clk_tbl_pcm_393, + .clkr = { + .enable_reg =0x54 , + .enable_mask =BIT (9 ), + .hw.init = &(struct clk_init_data ){ + .name ="pcm_src" , + .parent_names = lcc_pxo_pll4, + .num_parents =2 , + .ops = &clk_rcg_ops, + .flags =CLK_SET_RATE_GATE , + }, + }, +}; + +static struct clk_branch pcm_clk_out = { + .halt_reg =0x5c , + .halt_bit =0 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0x54 , + .enable_mask =BIT (11 ), + .hw.init = &(struct clk_init_data ){ + .name ="pcm_clk_out" , + .parent_names = (const char *[]){"pcm_src" }, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_regmap_mux pcm_clk = { + .reg =0x54 , + .shift =10 , + .width =1 , + .clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="pcm_clk" , + .parent_names = (const char *[]){ +"pcm_clk_out" , +"pcm_codec_clk" , + }, + .num_parents =2 , + .ops = &clk_regmap_mux_closest_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_rcg slimbus_src = { + .ns_reg =0xcc , + .md_reg =0xd0 , + .mn = { + .mnctr_en_bit =8 , + .mnctr_reset_bit =7 , + .mnctr_mode_shift =5 , + .n_val_shift =16 , + .m_val_shift =16 , + .width =8 , + }, + .p = { + .pre_div_shift =3 , + .pre_div_width =2 , + }, + .s = { + .src_sel_shift =0 , + .parent_map = lcc_pxo_pll4_map, + }, + .freq_tbl = clk_tbl_aif_osr_393, + .clkr = { + .enable_reg =0xcc , + .enable_mask =BIT (9 ), + .hw.init = &(struct clk_init_data ){ + .name ="slimbus_src" , + .parent_names = lcc_pxo_pll4, + .num_parents =2 , + .ops = &clk_rcg_ops, + .flags =CLK_SET_RATE_GATE , + }, + }, +}; + +static const char *lcc_slimbus_parents[] = { +"slimbus_src" , +}; + +static struct clk_branch audio_slimbus_clk = { + .halt_reg =0xd4 , + .halt_bit =0 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0xcc , + .enable_mask =BIT (10 ), + .hw.init = &(struct clk_init_data ){ + .name ="audio_slimbus_clk" , + .parent_names = lcc_slimbus_parents, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_branch sps_slimbus_clk = { + .halt_reg =0xd4 , + .halt_bit =1 , + .halt_check =BRANCH_HALT_ENABLE , + .clkr = { + .enable_reg =0xcc , + .enable_mask =BIT (12 ), + .hw.init = &(struct clk_init_data ){ + .name ="sps_slimbus_clk" , + .parent_names = lcc_slimbus_parents, + .num_parents =1 , + .ops = &clk_branch_ops, + .flags =CLK_SET_RATE_PARENT , + }, + }, +}; + +static struct clk_regmap *lcc_msm8960_clks[] = { + [PLL4 ] = &pll4.clkr, + [MI2S_OSR_SRC ] = &mi2s_osr_src.clkr, + [MI2S_OSR_CLK ] = &mi2s_osr_clk.clkr, + [MI2S_DIV_CLK ] = &mi2s_div_clk.clkr, + [MI2S_BIT_DIV_CLK ] = &mi2s_bit_div_clk.clkr, + [MI2S_BIT_CLK ] = &mi2s_bit_clk.clkr, + [PCM_SRC ] = &pcm_src.clkr, + [PCM_CLK_OUT ] = &pcm_clk_out.clkr, + [PCM_CLK ] = &pcm_clk.clkr, + [SLIMBUS_SRC ] = &slimbus_src.clkr, + [AUDIO_SLIMBUS_CLK ] = &audio_slimbus_clk.clkr, + [SPS_SLIMBUS_CLK ] = &sps_slimbus_clk.clkr, + [CODEC_I2S_MIC_OSR_SRC ] = &codec_i2s_mic_osr_src.clkr, + [CODEC_I2S_MIC_OSR_CLK ] = &codec_i2s_mic_osr_clk.clkr, + [CODEC_I2S_MIC_DIV_CLK ] = &codec_i2s_mic_div_clk.clkr, + [CODEC_I2S_MIC_BIT_DIV_CLK ] = &codec_i2s_mic_bit_div_clk.clkr, + [CODEC_I2S_MIC_BIT_CLK ] = &codec_i2s_mic_bit_clk.clkr, + [SPARE_I2S_MIC_OSR_SRC ] = &spare_i2s_mic_osr_src.clkr, + [SPARE_I2S_MIC_OSR_CLK ] = &spare_i2s_mic_osr_clk.clkr, + [SPARE_I2S_MIC_DIV_CLK ] = &spare_i2s_mic_div_clk.clkr, + [SPARE_I2S_MIC_BIT_DIV_CLK ] = &spare_i2s_mic_bit_div_clk.clkr, + [SPARE_I2S_MIC_BIT_CLK ] = &spare_i2s_mic_bit_clk.clkr, + [CODEC_I2S_SPKR_OSR_SRC ] = &codec_i2s_spkr_osr_src.clkr, + [CODEC_I2S_SPKR_OSR_CLK ] = &codec_i2s_spkr_osr_clk.clkr, + [CODEC_I2S_SPKR_DIV_CLK ] = &codec_i2s_spkr_div_clk.clkr, + [CODEC_I2S_SPKR_BIT_DIV_CLK ] = &codec_i2s_spkr_bit_div_clk.clkr, + [CODEC_I2S_SPKR_BIT_CLK ] = &codec_i2s_spkr_bit_clk.clkr, + [SPARE_I2S_SPKR_OSR_SRC ] = &spare_i2s_spkr_osr_src.clkr, + [SPARE_I2S_SPKR_OSR_CLK ] = &spare_i2s_spkr_osr_clk.clkr, + [SPARE_I2S_SPKR_DIV_CLK ] = &spare_i2s_spkr_div_clk.clkr, + [SPARE_I2S_SPKR_BIT_DIV_CLK ] = &spare_i2s_spkr_bit_div_clk.clkr, + [SPARE_I2S_SPKR_BIT_CLK ] = &spare_i2s_spkr_bit_clk.clkr, +}; + +static const struct regmap_config lcc_msm8960_regmap_config = { + .reg_bits =32 , + .reg_stride =4 , + .val_bits =32 , + .max_register =0xfc , + .fast_io = true, +}; + +static const struct qcom_cc_desc lcc_msm8960_desc = { + .config = &lcc_msm8960_regmap_config, + .clks = lcc_msm8960_clks, + .num_clks =ARRAY_SIZE (lcc_msm8960_clks), +}; + +static const struct of_device_id lcc_msm8960_match_table[] = { + { .compatible ="qcom,lcc-msm8960" }, + { .compatible ="qcom,lcc-apq8064" }, + { } +}; +MODULE_DEVICE_TABLE (of, lcc_msm8960_match_table); + +static int lcc_msm8960_probe (struct platform_device *pdev) +{ +u32 val; +struct regmap *regmap; + + regmap =qcom_cc_map (pdev, &lcc_msm8960_desc); +if (IS_ERR (regmap)) +return PTR_ERR (regmap); + +/* Use the correct frequency plan depending on speed of PLL4 */ + val =regmap_read (regmap,0x4 , &val); +if (val ==0x12 ) { + slimbus_src.freq_tbl = clk_tbl_aif_osr_492; + mi2s_osr_src.freq_tbl = clk_tbl_aif_osr_492; + codec_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492; + spare_i2s_mic_osr_src.freq_tbl = clk_tbl_aif_osr_492; + codec_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492; + spare_i2s_spkr_osr_src.freq_tbl = clk_tbl_aif_osr_492; + pcm_src.freq_tbl = clk_tbl_pcm_492; + } +/* Enable PLL4 source on the LPASS Primary PLL Mux */ +regmap_write (regmap,0xc4 ,0x1 ); + +return qcom_cc_really_probe (pdev, &lcc_msm8960_desc, regmap); +} + +static int lcc_msm8960_remove (struct platform_device *pdev) +{ +qcom_cc_remove (pdev); +return 0 ; +} + +static struct platform_driver lcc_msm8960_driver = { + .probe = lcc_msm8960_probe, + .remove = lcc_msm8960_remove, + .driver = { + .name ="lcc-msm8960" , + .owner =THIS_MODULE , + .of_match_table = lcc_msm8960_match_table, + }, +}; +module_platform_driver (lcc_msm8960_driver); + +MODULE_DESCRIPTION ("QCOM LCC MSM8960 Driver" ); +MODULE_LICENSE ("GPL v2" ); +MODULE_ALIAS ("platform:lcc-msm8960" );include/dt-bindings/clock/qcom,lcc-msm8960.h new file +50
-0@@ -0,0 +1,50 @@ +/* +* Copyright (c) 2014, The Linux Foundation. All rights reserved. +* +* This software is licensed under the terms of the GNU General Public +* License version 2, as published by the Free Software Foundation, and +* may be copied, distributed, and modified under those terms. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +*/ + +#ifndef _DT_BINDINGS_CLK_LCC_MSM8960_H +#define _DT_BINDINGS_CLK_LCC_MSM8960_H + +#define PLL4 0 +#define MI2S_OSR_SRC 1 +#define MI2S_OSR_CLK 2 +#define MI2S_DIV_CLK 3 +#define MI2S_BIT_DIV_CLK 4 +#define MI2S_BIT_CLK 5 +#define PCM_SRC 6 +#define PCM_CLK_OUT 7 +#define PCM_CLK 8 +#define SLIMBUS_SRC 9 +#define AUDIO_SLIMBUS_CLK 10 +#define SPS_SLIMBUS_CLK 11 +#define CODEC_I2S_MIC_OSR_SRC 12 +#define CODEC_I2S_MIC_OSR_CLK 13 +#define CODEC_I2S_MIC_DIV_CLK 14 +#define CODEC_I2S_MIC_BIT_DIV_CLK 15 +#define CODEC_I2S_MIC_BIT_CLK 16 +#define SPARE_I2S_MIC_OSR_SRC 17 +#define SPARE_I2S_MIC_OSR_CLK 18 +#define SPARE_I2S_MIC_DIV_CLK 19 +#define SPARE_I2S_MIC_BIT_DIV_CLK 20 +#define SPARE_I2S_MIC_BIT_CLK 21 +#define CODEC_I2S_SPKR_OSR_SRC 22 +#define CODEC_I2S_SPKR_OSR_CLK 23 +#define CODEC_I2S_SPKR_DIV_CLK 24 +#define CODEC_I2S_SPKR_BIT_DIV_CLK 25 +#define CODEC_I2S_SPKR_BIT_CLK 26 +#define SPARE_I2S_SPKR_OSR_SRC 27 +#define SPARE_I2S_SPKR_OSR_CLK 28 +#define SPARE_I2S_SPKR_DIV_CLK 29 +#define SPARE_I2S_SPKR_BIT_DIV_CLK 30 +#define SPARE_I2S_SPKR_BIT_CLK 31 + +#endif Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v3 7/8] clk: qcom: Add MSM8960/APQ8064 LPASS clock controller (LCC) driver
In reply to Stephen BoydWhen looking at this and trying to adapt it to MSM8660/APQ8060 like Neil did with MDM9615 I get pretty confused.
Quoted 5 lines · Stephen Boyd
Add an LCC driver for MSM8960/APQ8064 that supports the i2s, slimbus, and pcm clocks.
Change-Id I2549b821f7bf467c1bd80d4827a1a7621e725659Signed-off-by Stephen Boyd <[email protected]>
(...)
So this:
Quoted 25 lines
drivers/clk/qcom/lcc-msm8960.c new file +20
-0+static const u8 lcc_pxo_pll4_map[] = { + [P_PXO ] =0 , + [P_PLL4 ] =2 , +}; + +static const char *lcc_pxo_pll4[] = { +"pxo" , +"pll4_vote" , +}; +static struct clk_rcg mi2s_osr_src = { + .parent_map = lcc_pxo_pll4_map, + .parent_names = lcc_pxo_pll4, +static const char *lcc_mi2s_parents[] = { + "mi2s_osr_src", +static struct clk_branch mi2s_osr_clk = { + .parent_names = lcc_mi2s_parents, + .num_parents = 1, +static struct clk_regmap_div mi2s_div_clk = { + .parent_names = lcc_mi2s_parents, + .num_parents = 1,
drivers/clk/qcom/lcc-msm8960.c line 193
Quoted 10 lines
drivers/clk/qcom/lcc-msm8960.c new file +9
-0+static struct clk_branch mi2s_bit_div_clk = { + .parent_names = (const char *[]){"mi2s_div_clk" }, + .num_parents =1 , +static struct clk_regmap_mux mi2s_bit_clk = { + .parent_names = (const char *[]){ +"mi2s_bit_div_clk" , +"mi2s_codec_clk" , + }, + .num_parents =2 ,
So:
PXO | PLL4
-> mi2s_osr_src
-> mi2s_osr_clk
-> mi2s_div_clk
-> mi2s_bit_div_clk
-> mi2s_bit_clkIf I select the only existing parent.
This funky mi2s_codec_clk parent does not seem to exist in this or any of the drivers copying this. What is that? Do we have a orphan clock parent?
Anyway: when I look in the vendor tree, this parent selection etc does not seem to exist. Am I looking at the wrong code? I'm on android-msm-3.4 arch/arm/mach-msm/clock-8960.c
It more seems like the reparenting is a later invention, and I can't figure out where to learn about this.
Does MSM8660 even have the same structure? I would guess so but it looks simpler somewhat when I inspect the vendor tree.
The MSM8960 does this:
static CLK_AIF_OSR(mi2s_osr, LCC_MI2S_NS_REG, LCC_MI2S_MD_REG,
LCC_MI2S_STATUS_REG);
static CLK_AIF_BIT(mi2s_bit, LCC_MI2S_NS_REG, LCC_MI2S_STATUS_REG);
static CLK_AIF_OSR_DIV(codec_i2s_mic_osr, LCC_CODEC_I2S_MIC_NS_REG,
LCC_CODEC_I2S_MIC_MD_REG, LCC_CODEC_I2S_MIC_STATUS_REG);
static CLK_AIF_BIT_DIV(codec_i2s_mic_bit, LCC_CODEC_I2S_MIC_NS_REG,
LCC_CODEC_I2S_MIC_STATUS_REG);static CLK_AIF_OSR_DIV(spare_i2s_mic_osr, LCC_SPARE_I2S_MIC_NS_REG,
LCC_SPARE_I2S_MIC_MD_REG, LCC_SPARE_I2S_MIC_STATUS_REG);
static CLK_AIF_BIT_DIV(spare_i2s_mic_bit, LCC_SPARE_I2S_MIC_NS_REG,
LCC_SPARE_I2S_MIC_STATUS_REG);static CLK_AIF_OSR_DIV(codec_i2s_spkr_osr, LCC_CODEC_I2S_SPKR_NS_REG,
LCC_CODEC_I2S_SPKR_MD_REG, LCC_CODEC_I2S_SPKR_STATUS_REG);
static CLK_AIF_BIT_DIV(codec_i2s_spkr_bit, LCC_CODEC_I2S_SPKR_NS_REG,
LCC_CODEC_I2S_SPKR_STATUS_REG);static CLK_AIF_OSR_DIV(spare_i2s_spkr_osr, LCC_SPARE_I2S_SPKR_NS_REG,
LCC_SPARE_I2S_SPKR_MD_REG, LCC_SPARE_I2S_SPKR_STATUS_REG);
static CLK_AIF_BIT_DIV(spare_i2s_spkr_bit, LCC_SPARE_I2S_SPKR_NS_REG,
LCC_SPARE_I2S_SPKR_STATUS_REG);While the MSM8x60 does this:
static CLK_AIF_OSR(mi2s_osr, LCC_MI2S_NS_REG, LCC_MI2S_MD_REG,
LCC_MI2S_STATUS_REG);
static CLK_AIF_BIT(mi2s_bit, LCC_MI2S_NS_REG, LCC_MI2S_STATUS_REG);
static CLK_AIF_OSR(codec_i2s_mic_osr, LCC_CODEC_I2S_MIC_NS_REG,
LCC_CODEC_I2S_MIC_MD_REG, LCC_CODEC_I2S_MIC_STATUS_REG);
static CLK_AIF_BIT(codec_i2s_mic_bit, LCC_CODEC_I2S_MIC_NS_REG,
LCC_CODEC_I2S_MIC_STATUS_REG);static CLK_AIF_OSR(spare_i2s_mic_osr, LCC_SPARE_I2S_MIC_NS_REG,
LCC_SPARE_I2S_MIC_MD_REG, LCC_SPARE_I2S_MIC_STATUS_REG);
static CLK_AIF_BIT(spare_i2s_mic_bit, LCC_SPARE_I2S_MIC_NS_REG,
LCC_SPARE_I2S_MIC_STATUS_REG);static CLK_AIF_OSR(codec_i2s_spkr_osr, LCC_CODEC_I2S_SPKR_NS_REG,
LCC_CODEC_I2S_SPKR_MD_REG, LCC_CODEC_I2S_SPKR_STATUS_REG);
static CLK_AIF_BIT(codec_i2s_spkr_bit, LCC_CODEC_I2S_SPKR_NS_REG,
LCC_CODEC_I2S_SPKR_STATUS_REG);static CLK_AIF_OSR(spare_i2s_spkr_osr, LCC_SPARE_I2S_SPKR_NS_REG,
LCC_SPARE_I2S_SPKR_MD_REG, LCC_SPARE_I2S_SPKR_STATUS_REG);
static CLK_AIF_BIT(spare_i2s_spkr_bit, LCC_SPARE_I2S_SPKR_NS_REG,
LCC_SPARE_I2S_SPKR_STATUS_REG);So seemingly this "BIT_DIV" whatever that is, does not exist on the MSM8660, yet the first two clocks look like that for MSM8960 yet translates into that complex reparenting mechanism with 5 clocks instead of just 2 like in the vendor tree.
It's a bit hard to follow this...
Yours,
Linus Walleij
Re: [PATCH v3 7/8] clk: qcom: Add MSM8960/APQ8064 LPASS clock controller (LCC) driver
Quoted 2 lines · Linus Walleij
When looking at this and trying to adapt it to MSM8660/APQ8060 like Neil did with MDM9615 I get pretty confused.
Nevermind, I think I have it figured out after staring at the code and branch/divider/mux clocks a bit more. I'll send out my MSM8660/APQ8060 LCC driver for review.
Yours,
Linus Walleij
PATCH v3 8/8 devicetree: bindings: Document qcom,lcc
In reply to Stephen BoydFrom: Rajendra Nayak <[email protected]>
Document the LPASS (low power audio subsystem) clock controller found on Qualcomm devices.
--- .../devicetree/bindings/clock/qcom,lcc.txt | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,lcc.txt
Documentation/devicetree/bindings/clock/qcom,lcc.txt new file +21 -0
@@ -0,0 +1,21 @@ +Qualcomm LPASS Clock & Reset Controller Binding +------------------------------------------------ + +Required properties : +- compatible : shall contain only one of the following: + + "qcom,lcc-msm8960" + "qcom,lcc-apq8064" + "qcom,lcc-ipq8064" + +- reg : shall contain base register location and length +- #clock-cells : shall contain 1 +- #reset-cells : shall contain 1 + +Example: + clock-controller at 28000000 { + compatible = "qcom,lcc-ipq8064"; + reg = <0x28000000 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + };
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Quoted 4 lines · Stephen Boyd
This patchset adds support for the low power audio subsystem (LPASS) clock controller hardware. I split out the #define patch for IPQ so that it can go through the clock tree and the arm-soc tree in parallel if desired.
Verified all supported bit clock frequencies for audio playback work on the ipq806x SOC with max98357a codec.
Signature · 4 lines
Kenneth Westfield Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Mike Turquette
AppliedQuoting Ken Westfield (2015-01-22 13:41:22)
Quoted 10 lines · Stephen Boyd
Quoted 4 lines
This patchset adds support for the low power audio subsystem (LPASS) clock controller hardware. I split out the #define patch for IPQ so that it can go through the clock tree and the arm-soc tree in parallel if desired.
Verified all supported bit clock frequencies for audio playback work on the ipq806x SOC with max98357a codec.
Tested-by Kenneth Westfield <[email protected]>
Applied to clk-next.
Regards,
Mike
Quoted 6 lines
Signature · 5 lines
-- Kenneth Westfield Qualcomm Innovation Center, Inc. The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project