diff mbox

[1/2] clk: hisilicon: add hi3620_mmc_clks

Message ID 1389604469-8064-2-git-send-email-zhangfei.gao@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Zhangfei Gao Jan. 13, 2014, 9:14 a.m. UTC
Suggest by Arnd: abstract mmc tuning as clock behavior,
also because different soc have different tuning method and registers.
hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.

Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
---
 .../bindings/arm/hisilicon/hisilicon.txt           |   14 +
 .../devicetree/bindings/clock/hi3620-clock.txt     |    1 +
 drivers/clk/hisilicon/clk-hi3620.c                 |  267 ++++++++++++++++++++
 include/dt-bindings/clock/hi3620-clock.h           |    5 +
 4 files changed, 287 insertions(+)

Comments

Seungwon Jeon Jan. 14, 2014, 9:47 a.m. UTC | #1
On Mon, January 13, 2014, Zhangfei Gao wrote:
> Suggest by Arnd: abstract mmc tuning as clock behavior,
> also because different soc have different tuning method and registers.
> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
> 
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
> ---
>  .../bindings/arm/hisilicon/hisilicon.txt           |   14 +
>  .../devicetree/bindings/clock/hi3620-clock.txt     |    1 +
>  drivers/clk/hisilicon/clk-hi3620.c                 |  267 ++++++++++++++++++++
>  include/dt-bindings/clock/hi3620-clock.h           |    5 +
>  4 files changed, 287 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> index 8c7a4653508d..df0a452b8526 100644
> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> @@ -30,3 +30,17 @@ Example:
>  		resume-offset = <0x308>;
>  		reboot-offset = <0x4>;
>  	};
> +
> +PCTRL: Peripheral misc control register
> +
> +Required Properties:
> +- compatible: "hisilicon,pctrl"
> +- reg: Address and size of pctrl.
> +
> +Example:
> +
> +	/* for Hi3620 */
> +	pctrl: pctrl@fca09000 {
> +		compatible = "hisilicon,pctrl";
> +		reg = <0xfca09000 0x1000>;
> +	};
> diff --git a/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> index 4b71ab41be53..dad6269f52c5 100644
> --- a/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> @@ -7,6 +7,7 @@ Required Properties:
> 
>  - compatible: should be one of the following.
>    - "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
> +  - "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc.
> 
>  - reg: physical base address of the controller and length of memory mapped
>    region.
> diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c
> index f24ad6a3a797..54cc3475ec36 100644
> --- a/drivers/clk/hisilicon/clk-hi3620.c
> +++ b/drivers/clk/hisilicon/clk-hi3620.c
> @@ -240,3 +240,270 @@ static void __init hi3620_clk_init(struct device_node *np)
>  				   base);
>  }
>  CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
> +
> +struct hisi_mmc_clock {
> +	unsigned int		id;
> +	const char		*name;
> +	const char		*parent_name;
> +	unsigned long		flags;
> +	u32			clken_reg;
> +	u32			clken_bit;
> +	u32			div_reg;
> +	u32			div_off;
> +	u32			div_bits;
> +	u32			drv_reg;
> +	u32			drv_off;
> +	u32			drv_bits;
> +	u32			sam_reg;
> +	u32			sam_off;
> +	u32			sam_bits;
> +};
> +
> +struct clk_mmc {
> +	struct clk_hw	hw;
> +	u32		id;
> +	void __iomem	*clken_reg;
> +	u32		clken_bit;
> +	void __iomem	*div_reg;
> +	u32		div_off;
> +	u32		div_bits;
> +	void __iomem	*drv_reg;
> +	u32		drv_off;
> +	u32		drv_bits;
> +	void __iomem	*sam_reg;
> +	u32		sam_off;
> +	u32		sam_bits;
> +};
> +
> +#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
> +
> +static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
> +	{ HI3620_SD_CIUCLK,	"sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4,
> 4, 0x1f8, 8, 4},
> +	{ HI3620_MMC_CIUCLK1,   "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3,
> 0x1f8, 16, 4, 0x1f8, 20, 4},
> +	{ HI3620_MMC_CIUCLK2,   "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3,
> 0x1f8, 28, 4, 0x1fc, 0, 4},
> +	{ HI3620_MMC_CIUCLK3,   "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3,
> 0x1fc, 8, 4, 0x1fc, 12, 4},
> +};
> +
> +static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
> +		       unsigned long parent_rate)
> +{
> +	switch (parent_rate) {
> +	case 26000000:
> +		return 13000000;
> +	case 180000000:
> +		return 25000000;
> +	case 360000000:
> +		return 50000000;
> +	case 720000000:
> +		return 100000000;
> +	default:
> +		return parent_rate;
> +	}
> +}
> +
> +static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
> +			      unsigned long *best_parent_rate,
> +			      struct clk **best_parent_p)
> +{
> +	struct clk_mmc *mclk = to_mmc(hw);
> +	unsigned long best = 0;
> +
> +	if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
No need to check HI3620_MMC_CIUCLK2 and HI3620_MMC_CIUCLK3?

> +		rate = 13000000;
> +		best = 26000000;
> +	} else if (rate <= 26000000) {
> +		rate = 25000000;
> +		best = 180000000;
> +	} else if (rate <= 52000000) {
> +		rate = 50000000;
> +		best = 360000000;
> +	} else if (rate <= 100000000) {
> +		rate = 100000000;
> +		best = 720000000;
> +	} else {
> +		/* max is 180M */
> +		rate = 180000000;
> +		best = 1440000000;
> +	}
> +	*best_parent_rate = best;
> +	return rate;
> +}
> +
> +static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
> +{
> +	u32 i;
> +
> +	if (para >= 0) {
> +		for (i = 0; i < len; i++) {
> +			if (para % 2)
> +				val |= 1 << (off + i);
> +			else
> +				val &= ~(1 << (off + i));
> +			para = para >> 1;
> +		}
> +	}
> +	return val;
> +}
> +
> +static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
> +{
> +	struct clk_mmc *mclk = to_mmc(hw);
> +	unsigned long flags;
> +	u32 sam, drv, div, val;
> +	static DEFINE_SPINLOCK(mmc_clk_lock);
> +
> +	switch (rate) {
> +	case 13000000:
> +		sam = 3;
> +		drv = 1;
> +		div = 1;
> +		break;
> +	case 25000000:
> +		sam = 13;
> +		drv = 6;
> +		div = 6;
> +		break;
> +	case 50000000:
> +		sam = 3;
> +		drv = 6;
> +		div = 6;
> +		break;
> +	case 100000000:
> +		sam = 6;
> +		drv = 4;
> +		div = 6;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	spin_lock_irqsave(&mmc_clk_lock, flags);
> +
> +	val = readl_relaxed(mclk->clken_reg);
> +	val &= ~(1 << mclk->clken_bit);
> +	writel_relaxed(val, mclk->clken_reg);
> +
> +	val = readl_relaxed(mclk->sam_reg);
> +	val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
> +	writel_relaxed(val, mclk->sam_reg);
> +
> +	val = readl_relaxed(mclk->drv_reg);
> +	val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
> +	writel_relaxed(val, mclk->drv_reg);
> +
> +	val = readl_relaxed(mclk->div_reg);
> +	val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
> +	writel_relaxed(val, mclk->div_reg);
> +
> +	val = readl_relaxed(mclk->clken_reg);
> +	val |= 1 << mclk->clken_bit;
> +	writel_relaxed(val, mclk->clken_reg);
> +
> +	spin_unlock_irqrestore(&mmc_clk_lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mmc_clk_prepare(struct clk_hw *hw)
> +{
> +	struct clk_mmc *mclk = to_mmc(hw);
> +	unsigned long rate;
> +
> +	if (mclk->id == HI3620_MMC_CIUCLK1)
HI3620_SD_CIUCLK was used in previous version.
Is it fixed with HI3620_MMC_CIUCLK1?
And, please clarify in case of HI3620_MMC_CIUCLK2 and HI3620_MMC_CIUCLK3 as well.

Thanks,
Seungwon Jeon

> +		rate = 13000000;
> +	else
> +		rate = 25000000;
> +
> +	return mmc_clk_set_timing(hw, rate);
> +}
> +
> +static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +			     unsigned long parent_rate)
> +{
> +	return mmc_clk_set_timing(hw, rate);
> +}
> +
> +static struct clk_ops clk_mmc_ops = {
> +	.prepare = mmc_clk_prepare,
> +	.determine_rate = mmc_clk_determine_rate,
> +	.set_rate = mmc_clk_set_rate,
> +	.recalc_rate = mmc_clk_recalc_rate,
> +};
> +
> +static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
> +			void __iomem *base, struct device_node *np)
> +{
> +	struct clk_mmc *mclk;
> +	struct clk *clk;
> +	struct clk_init_data init;
> +
> +	mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
> +	if (!mclk) {
> +		pr_err("%s: fail to allocate mmc clk\n", __func__);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	init.name = mmc_clk->name;
> +	init.ops = &clk_mmc_ops;
> +	init.flags = mmc_clk->flags | CLK_IS_BASIC;
> +	init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
> +	init.num_parents = (mmc_clk->parent_name ? 1 : 0);
> +	mclk->hw.init = &init;
> +
> +	mclk->id = mmc_clk->id;
> +	mclk->clken_reg = base + mmc_clk->clken_reg;
> +	mclk->clken_bit = mmc_clk->clken_bit;
> +	mclk->div_reg = base + mmc_clk->div_reg;
> +	mclk->div_off = mmc_clk->div_off;
> +	mclk->div_bits = mmc_clk->div_bits;
> +	mclk->drv_reg = base + mmc_clk->drv_reg;
> +	mclk->drv_off = mmc_clk->drv_off;
> +	mclk->drv_bits = mmc_clk->drv_bits;
> +	mclk->sam_reg = base + mmc_clk->sam_reg;
> +	mclk->sam_off = mmc_clk->sam_off;
> +	mclk->sam_bits = mmc_clk->sam_bits;
> +
> +	clk = clk_register(NULL, &mclk->hw);
> +	if (WARN_ON(IS_ERR(clk)))
> +		kfree(mclk);
> +	return clk;
> +}
> +
> +static void __init hi3620_mmc_clk_init(struct device_node *node)
> +{
> +	void __iomem *base;
> +	int i, num = ARRAY_SIZE(hi3620_mmc_clks);
> +	struct clk_onecell_data *clk_data;
> +
> +	if (!node) {
> +		pr_err("failed to find pctrl node in DTS\n");
> +		return;
> +	}
> +
> +	base = of_iomap(node, 0);
> +	if (!base) {
> +		pr_err("failed to map pctrl\n");
> +		return;
> +	}
> +
> +	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
> +	if (WARN_ON(!clk_data))
> +		return;
> +
> +	clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
> +	if (!clk_data->clks) {
> +		pr_err("%s: fail to allocate mmc clk\n", __func__);
> +		return;
> +	}
> +
> +	for (i = 0; i < num; i++) {
> +		struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
> +		clk_data->clks[mmc_clk->id] =
> +			hisi_register_clk_mmc(mmc_clk, base, node);
> +	}
> +
> +	clk_data->clk_num = num;
> +	of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
> +}
> +
> +CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
> diff --git a/include/dt-bindings/clock/hi3620-clock.h b/include/dt-bindings/clock/hi3620-clock.h
> index 6eaa6a45e110..21b9d0e2eb0c 100644
> --- a/include/dt-bindings/clock/hi3620-clock.h
> +++ b/include/dt-bindings/clock/hi3620-clock.h
> @@ -147,6 +147,11 @@
>  #define HI3620_MMC_CLK3		217
>  #define HI3620_MCU_CLK		218
> 
> +#define HI3620_SD_CIUCLK	0
> +#define HI3620_MMC_CIUCLK1	1
> +#define HI3620_MMC_CIUCLK2	2
> +#define HI3620_MMC_CIUCLK3	3
> +
>  #define HI3620_NR_CLKS		219
> 
>  #endif	/* __DTS_HI3620_CLOCK_H */
> --
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Zhangfei Gao Jan. 14, 2014, 10:04 a.m. UTC | #2
>> +static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
>> +                           unsigned long *best_parent_rate,
>> +                           struct clk **best_parent_p)
>> +{
>> +     struct clk_mmc *mclk = to_mmc(hw);
>> +     unsigned long best = 0;
>> +
>> +     if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
> No need to check HI3620_MMC_CIUCLK2 and HI3620_MMC_CIUCLK3?

Yes, only emmc, id=HI3620_MMC_CIUCLK1, using init clk 13M, while
others use init clock 25M.
Add this to handle init mmc init clk 400K and return source clock rate
13M, from your suggestion in fact.

>
>> +             rate = 13000000;
>> +             best = 26000000;
>> +     } else if (rate <= 26000000) {
>> +             rate = 25000000;
>> +             best = 180000000;
>> +     } else if (rate <= 52000000) {
>> +             rate = 50000000;
>> +             best = 360000000;
>> +     } else if (rate <= 100000000) {
>> +             rate = 100000000;
>> +             best = 720000000;
>> +     } else {
>> +             /* max is 180M */
>> +             rate = 180000000;
>> +             best = 1440000000;
>> +     }
>> +     *best_parent_rate = best;
>> +     return rate;
>> +}

>> +static int mmc_clk_prepare(struct clk_hw *hw)
>> +{
>> +     struct clk_mmc *mclk = to_mmc(hw);
>> +     unsigned long rate;
>> +
>> +     if (mclk->id == HI3620_MMC_CIUCLK1)
> HI3620_SD_CIUCLK was used in previous version.
Yes, fixed, though it can work on the dev board.

> Is it fixed with HI3620_MMC_CIUCLK1?
> And, please clarify in case of HI3620_MMC_CIUCLK2 and HI3620_MMC_CIUCLK3 as well.

Only HI3620_MMC_CIUCLK1 use 13M for init, all others use 25M on hi3620.

>
> Thanks,
> Seungwon Jeon
>
>> +             rate = 13000000;
>> +     else
>> +             rate = 25000000;
>> +
>> +     return mmc_clk_set_timing(hw, rate);
>> +}
>> +

Thanks
Seungwon Jeon Jan. 14, 2014, 10:40 a.m. UTC | #3
On Mon, January 13, 2014, Zhangfei Gao wrote:
> Suggest by Arnd: abstract mmc tuning as clock behavior,
> also because different soc have different tuning method and registers.
> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
> 
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>

I add my ack.
Please run 'checkpatch' and remove some warnings.

Acked-by: Seungwon Jeon <tgih.jun@samsung.com>

Thanks,
Seungwon Jeon

> ---
>  .../bindings/arm/hisilicon/hisilicon.txt           |   14 +
>  .../devicetree/bindings/clock/hi3620-clock.txt     |    1 +
>  drivers/clk/hisilicon/clk-hi3620.c                 |  267 ++++++++++++++++++++
>  include/dt-bindings/clock/hi3620-clock.h           |    5 +
>  4 files changed, 287 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> index 8c7a4653508d..df0a452b8526 100644
> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> @@ -30,3 +30,17 @@ Example:
>  		resume-offset = <0x308>;
>  		reboot-offset = <0x4>;
>  	};
> +
> +PCTRL: Peripheral misc control register
> +
> +Required Properties:
> +- compatible: "hisilicon,pctrl"
> +- reg: Address and size of pctrl.
> +
> +Example:
> +
> +	/* for Hi3620 */
> +	pctrl: pctrl@fca09000 {
> +		compatible = "hisilicon,pctrl";
> +		reg = <0xfca09000 0x1000>;
> +	};
> diff --git a/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> index 4b71ab41be53..dad6269f52c5 100644
> --- a/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> @@ -7,6 +7,7 @@ Required Properties:
> 
>  - compatible: should be one of the following.
>    - "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
> +  - "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc.
> 
>  - reg: physical base address of the controller and length of memory mapped
>    region.
> diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c
> index f24ad6a3a797..54cc3475ec36 100644
> --- a/drivers/clk/hisilicon/clk-hi3620.c
> +++ b/drivers/clk/hisilicon/clk-hi3620.c
> @@ -240,3 +240,270 @@ static void __init hi3620_clk_init(struct device_node *np)
>  				   base);
>  }
>  CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
> +
> +struct hisi_mmc_clock {
> +	unsigned int		id;
> +	const char		*name;
> +	const char		*parent_name;
> +	unsigned long		flags;
> +	u32			clken_reg;
> +	u32			clken_bit;
> +	u32			div_reg;
> +	u32			div_off;
> +	u32			div_bits;
> +	u32			drv_reg;
> +	u32			drv_off;
> +	u32			drv_bits;
> +	u32			sam_reg;
> +	u32			sam_off;
> +	u32			sam_bits;
> +};
> +
> +struct clk_mmc {
> +	struct clk_hw	hw;
> +	u32		id;
> +	void __iomem	*clken_reg;
> +	u32		clken_bit;
> +	void __iomem	*div_reg;
> +	u32		div_off;
> +	u32		div_bits;
> +	void __iomem	*drv_reg;
> +	u32		drv_off;
> +	u32		drv_bits;
> +	void __iomem	*sam_reg;
> +	u32		sam_off;
> +	u32		sam_bits;
> +};
> +
> +#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
> +
> +static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
> +	{ HI3620_SD_CIUCLK,	"sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4,
> 4, 0x1f8, 8, 4},
> +	{ HI3620_MMC_CIUCLK1,   "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3,
> 0x1f8, 16, 4, 0x1f8, 20, 4},
> +	{ HI3620_MMC_CIUCLK2,   "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3,
> 0x1f8, 28, 4, 0x1fc, 0, 4},
> +	{ HI3620_MMC_CIUCLK3,   "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3,
> 0x1fc, 8, 4, 0x1fc, 12, 4},
> +};
> +
> +static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
> +		       unsigned long parent_rate)
> +{
> +	switch (parent_rate) {
> +	case 26000000:
> +		return 13000000;
> +	case 180000000:
> +		return 25000000;
> +	case 360000000:
> +		return 50000000;
> +	case 720000000:
> +		return 100000000;
> +	default:
> +		return parent_rate;
> +	}
> +}
> +
> +static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
> +			      unsigned long *best_parent_rate,
> +			      struct clk **best_parent_p)
> +{
> +	struct clk_mmc *mclk = to_mmc(hw);
> +	unsigned long best = 0;
> +
> +	if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
> +		rate = 13000000;
> +		best = 26000000;
> +	} else if (rate <= 26000000) {
> +		rate = 25000000;
> +		best = 180000000;
> +	} else if (rate <= 52000000) {
> +		rate = 50000000;
> +		best = 360000000;
> +	} else if (rate <= 100000000) {
> +		rate = 100000000;
> +		best = 720000000;
> +	} else {
> +		/* max is 180M */
> +		rate = 180000000;
> +		best = 1440000000;
> +	}
> +	*best_parent_rate = best;
> +	return rate;
> +}
> +
> +static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
> +{
> +	u32 i;
> +
> +	if (para >= 0) {
> +		for (i = 0; i < len; i++) {
> +			if (para % 2)
> +				val |= 1 << (off + i);
> +			else
> +				val &= ~(1 << (off + i));
> +			para = para >> 1;
> +		}
> +	}
> +	return val;
> +}
> +
> +static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
> +{
> +	struct clk_mmc *mclk = to_mmc(hw);
> +	unsigned long flags;
> +	u32 sam, drv, div, val;
> +	static DEFINE_SPINLOCK(mmc_clk_lock);
> +
> +	switch (rate) {
> +	case 13000000:
> +		sam = 3;
> +		drv = 1;
> +		div = 1;
> +		break;
> +	case 25000000:
> +		sam = 13;
> +		drv = 6;
> +		div = 6;
> +		break;
> +	case 50000000:
> +		sam = 3;
> +		drv = 6;
> +		div = 6;
> +		break;
> +	case 100000000:
> +		sam = 6;
> +		drv = 4;
> +		div = 6;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	spin_lock_irqsave(&mmc_clk_lock, flags);
> +
> +	val = readl_relaxed(mclk->clken_reg);
> +	val &= ~(1 << mclk->clken_bit);
> +	writel_relaxed(val, mclk->clken_reg);
> +
> +	val = readl_relaxed(mclk->sam_reg);
> +	val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
> +	writel_relaxed(val, mclk->sam_reg);
> +
> +	val = readl_relaxed(mclk->drv_reg);
> +	val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
> +	writel_relaxed(val, mclk->drv_reg);
> +
> +	val = readl_relaxed(mclk->div_reg);
> +	val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
> +	writel_relaxed(val, mclk->div_reg);
> +
> +	val = readl_relaxed(mclk->clken_reg);
> +	val |= 1 << mclk->clken_bit;
> +	writel_relaxed(val, mclk->clken_reg);
> +
> +	spin_unlock_irqrestore(&mmc_clk_lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mmc_clk_prepare(struct clk_hw *hw)
> +{
> +	struct clk_mmc *mclk = to_mmc(hw);
> +	unsigned long rate;
> +
> +	if (mclk->id == HI3620_MMC_CIUCLK1)
> +		rate = 13000000;
> +	else
> +		rate = 25000000;
> +
> +	return mmc_clk_set_timing(hw, rate);
> +}
> +
> +static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +			     unsigned long parent_rate)
> +{
> +	return mmc_clk_set_timing(hw, rate);
> +}
> +
> +static struct clk_ops clk_mmc_ops = {
> +	.prepare = mmc_clk_prepare,
> +	.determine_rate = mmc_clk_determine_rate,
> +	.set_rate = mmc_clk_set_rate,
> +	.recalc_rate = mmc_clk_recalc_rate,
> +};
> +
> +static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
> +			void __iomem *base, struct device_node *np)
> +{
> +	struct clk_mmc *mclk;
> +	struct clk *clk;
> +	struct clk_init_data init;
> +
> +	mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
> +	if (!mclk) {
> +		pr_err("%s: fail to allocate mmc clk\n", __func__);
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	init.name = mmc_clk->name;
> +	init.ops = &clk_mmc_ops;
> +	init.flags = mmc_clk->flags | CLK_IS_BASIC;
> +	init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
> +	init.num_parents = (mmc_clk->parent_name ? 1 : 0);
> +	mclk->hw.init = &init;
> +
> +	mclk->id = mmc_clk->id;
> +	mclk->clken_reg = base + mmc_clk->clken_reg;
> +	mclk->clken_bit = mmc_clk->clken_bit;
> +	mclk->div_reg = base + mmc_clk->div_reg;
> +	mclk->div_off = mmc_clk->div_off;
> +	mclk->div_bits = mmc_clk->div_bits;
> +	mclk->drv_reg = base + mmc_clk->drv_reg;
> +	mclk->drv_off = mmc_clk->drv_off;
> +	mclk->drv_bits = mmc_clk->drv_bits;
> +	mclk->sam_reg = base + mmc_clk->sam_reg;
> +	mclk->sam_off = mmc_clk->sam_off;
> +	mclk->sam_bits = mmc_clk->sam_bits;
> +
> +	clk = clk_register(NULL, &mclk->hw);
> +	if (WARN_ON(IS_ERR(clk)))
> +		kfree(mclk);
> +	return clk;
> +}
> +
> +static void __init hi3620_mmc_clk_init(struct device_node *node)
> +{
> +	void __iomem *base;
> +	int i, num = ARRAY_SIZE(hi3620_mmc_clks);
> +	struct clk_onecell_data *clk_data;
> +
> +	if (!node) {
> +		pr_err("failed to find pctrl node in DTS\n");
> +		return;
> +	}
> +
> +	base = of_iomap(node, 0);
> +	if (!base) {
> +		pr_err("failed to map pctrl\n");
> +		return;
> +	}
> +
> +	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
> +	if (WARN_ON(!clk_data))
> +		return;
> +
> +	clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
> +	if (!clk_data->clks) {
> +		pr_err("%s: fail to allocate mmc clk\n", __func__);
> +		return;
> +	}
> +
> +	for (i = 0; i < num; i++) {
> +		struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
> +		clk_data->clks[mmc_clk->id] =
> +			hisi_register_clk_mmc(mmc_clk, base, node);
> +	}
> +
> +	clk_data->clk_num = num;
> +	of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
> +}
> +
> +CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
> diff --git a/include/dt-bindings/clock/hi3620-clock.h b/include/dt-bindings/clock/hi3620-clock.h
> index 6eaa6a45e110..21b9d0e2eb0c 100644
> --- a/include/dt-bindings/clock/hi3620-clock.h
> +++ b/include/dt-bindings/clock/hi3620-clock.h
> @@ -147,6 +147,11 @@
>  #define HI3620_MMC_CLK3		217
>  #define HI3620_MCU_CLK		218
> 
> +#define HI3620_SD_CIUCLK	0
> +#define HI3620_MMC_CIUCLK1	1
> +#define HI3620_MMC_CIUCLK2	2
> +#define HI3620_MMC_CIUCLK3	3
> +
>  #define HI3620_NR_CLKS		219
> 
>  #endif	/* __DTS_HI3620_CLOCK_H */
> --
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-mmc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
Zhangfei Gao Jan. 14, 2014, 11:52 a.m. UTC | #4
On 01/14/2014 06:40 PM, Seungwon Jeon wrote:
> On Mon, January 13, 2014, Zhangfei Gao wrote:
>> Suggest by Arnd: abstract mmc tuning as clock behavior,
>> also because different soc have different tuning method and registers.
>> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
>>
>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>> Acked-by: Arnd Bergmann <arnd@arndb.de>
>> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
> 
> I add my ack.
> Please run 'checkpatch' and remove some warnings.
> 
> Acked-by: Seungwon Jeon <tgih.jun@samsung.com>
> 

Thanks Seungwon.

Have checked before, and ignoring warnings of "line over 80 characters"
seems more neat.

Thanks
Mike Turquette Jan. 14, 2014, 8:17 p.m. UTC | #5
Quoting Zhangfei Gao (2014-01-13 01:14:28)
> Suggest by Arnd: abstract mmc tuning as clock behavior,
> also because different soc have different tuning method and registers.
> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
> 
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>

Patch looks good to me with one exception. I do not have
Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt in the
clk-next branch. Is there a stable branch I can pull in as a dependency?

Thanks,
Mike

> ---
>  .../bindings/arm/hisilicon/hisilicon.txt           |   14 +
>  .../devicetree/bindings/clock/hi3620-clock.txt     |    1 +
>  drivers/clk/hisilicon/clk-hi3620.c                 |  267 ++++++++++++++++++++
>  include/dt-bindings/clock/hi3620-clock.h           |    5 +
>  4 files changed, 287 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> index 8c7a4653508d..df0a452b8526 100644
> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> @@ -30,3 +30,17 @@ Example:
>                 resume-offset = <0x308>;
>                 reboot-offset = <0x4>;
>         };
> +
> +PCTRL: Peripheral misc control register
> +
> +Required Properties:
> +- compatible: "hisilicon,pctrl"
> +- reg: Address and size of pctrl.
> +
> +Example:
> +
> +       /* for Hi3620 */
> +       pctrl: pctrl@fca09000 {
> +               compatible = "hisilicon,pctrl";
> +               reg = <0xfca09000 0x1000>;
> +       };
> diff --git a/Documentation/devicetree/bindings/clock/hi3620-clock.txt b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> index 4b71ab41be53..dad6269f52c5 100644
> --- a/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
> @@ -7,6 +7,7 @@ Required Properties:
>  
>  - compatible: should be one of the following.
>    - "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
> +  - "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc.
>  
>  - reg: physical base address of the controller and length of memory mapped
>    region.
> diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c
> index f24ad6a3a797..54cc3475ec36 100644
> --- a/drivers/clk/hisilicon/clk-hi3620.c
> +++ b/drivers/clk/hisilicon/clk-hi3620.c
> @@ -240,3 +240,270 @@ static void __init hi3620_clk_init(struct device_node *np)
>                                    base);
>  }
>  CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
> +
> +struct hisi_mmc_clock {
> +       unsigned int            id;
> +       const char              *name;
> +       const char              *parent_name;
> +       unsigned long           flags;
> +       u32                     clken_reg;
> +       u32                     clken_bit;
> +       u32                     div_reg;
> +       u32                     div_off;
> +       u32                     div_bits;
> +       u32                     drv_reg;
> +       u32                     drv_off;
> +       u32                     drv_bits;
> +       u32                     sam_reg;
> +       u32                     sam_off;
> +       u32                     sam_bits;
> +};
> +
> +struct clk_mmc {
> +       struct clk_hw   hw;
> +       u32             id;
> +       void __iomem    *clken_reg;
> +       u32             clken_bit;
> +       void __iomem    *div_reg;
> +       u32             div_off;
> +       u32             div_bits;
> +       void __iomem    *drv_reg;
> +       u32             drv_off;
> +       u32             drv_bits;
> +       void __iomem    *sam_reg;
> +       u32             sam_off;
> +       u32             sam_bits;
> +};
> +
> +#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
> +
> +static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
> +       { HI3620_SD_CIUCLK,     "sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4},
> +       { HI3620_MMC_CIUCLK1,   "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4},
> +       { HI3620_MMC_CIUCLK2,   "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4},
> +       { HI3620_MMC_CIUCLK3,   "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4},
> +};
> +
> +static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
> +                      unsigned long parent_rate)
> +{
> +       switch (parent_rate) {
> +       case 26000000:
> +               return 13000000;
> +       case 180000000:
> +               return 25000000;
> +       case 360000000:
> +               return 50000000;
> +       case 720000000:
> +               return 100000000;
> +       default:
> +               return parent_rate;
> +       }
> +}
> +
> +static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
> +                             unsigned long *best_parent_rate,
> +                             struct clk **best_parent_p)
> +{
> +       struct clk_mmc *mclk = to_mmc(hw);
> +       unsigned long best = 0;
> +
> +       if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
> +               rate = 13000000;
> +               best = 26000000;
> +       } else if (rate <= 26000000) {
> +               rate = 25000000;
> +               best = 180000000;
> +       } else if (rate <= 52000000) {
> +               rate = 50000000;
> +               best = 360000000;
> +       } else if (rate <= 100000000) {
> +               rate = 100000000;
> +               best = 720000000;
> +       } else {
> +               /* max is 180M */
> +               rate = 180000000;
> +               best = 1440000000;
> +       }
> +       *best_parent_rate = best;
> +       return rate;
> +}
> +
> +static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
> +{
> +       u32 i;
> +
> +       if (para >= 0) {
> +               for (i = 0; i < len; i++) {
> +                       if (para % 2)
> +                               val |= 1 << (off + i);
> +                       else
> +                               val &= ~(1 << (off + i));
> +                       para = para >> 1;
> +               }
> +       }
> +       return val;
> +}
> +
> +static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
> +{
> +       struct clk_mmc *mclk = to_mmc(hw);
> +       unsigned long flags;
> +       u32 sam, drv, div, val;
> +       static DEFINE_SPINLOCK(mmc_clk_lock);
> +
> +       switch (rate) {
> +       case 13000000:
> +               sam = 3;
> +               drv = 1;
> +               div = 1;
> +               break;
> +       case 25000000:
> +               sam = 13;
> +               drv = 6;
> +               div = 6;
> +               break;
> +       case 50000000:
> +               sam = 3;
> +               drv = 6;
> +               div = 6;
> +               break;
> +       case 100000000:
> +               sam = 6;
> +               drv = 4;
> +               div = 6;
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       spin_lock_irqsave(&mmc_clk_lock, flags);
> +
> +       val = readl_relaxed(mclk->clken_reg);
> +       val &= ~(1 << mclk->clken_bit);
> +       writel_relaxed(val, mclk->clken_reg);
> +
> +       val = readl_relaxed(mclk->sam_reg);
> +       val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
> +       writel_relaxed(val, mclk->sam_reg);
> +
> +       val = readl_relaxed(mclk->drv_reg);
> +       val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
> +       writel_relaxed(val, mclk->drv_reg);
> +
> +       val = readl_relaxed(mclk->div_reg);
> +       val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
> +       writel_relaxed(val, mclk->div_reg);
> +
> +       val = readl_relaxed(mclk->clken_reg);
> +       val |= 1 << mclk->clken_bit;
> +       writel_relaxed(val, mclk->clken_reg);
> +
> +       spin_unlock_irqrestore(&mmc_clk_lock, flags);
> +
> +       return 0;
> +}
> +
> +static int mmc_clk_prepare(struct clk_hw *hw)
> +{
> +       struct clk_mmc *mclk = to_mmc(hw);
> +       unsigned long rate;
> +
> +       if (mclk->id == HI3620_MMC_CIUCLK1)
> +               rate = 13000000;
> +       else
> +               rate = 25000000;
> +
> +       return mmc_clk_set_timing(hw, rate);
> +}
> +
> +static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +                            unsigned long parent_rate)
> +{
> +       return mmc_clk_set_timing(hw, rate);
> +}
> +
> +static struct clk_ops clk_mmc_ops = {
> +       .prepare = mmc_clk_prepare,
> +       .determine_rate = mmc_clk_determine_rate,
> +       .set_rate = mmc_clk_set_rate,
> +       .recalc_rate = mmc_clk_recalc_rate,
> +};
> +
> +static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
> +                       void __iomem *base, struct device_node *np)
> +{
> +       struct clk_mmc *mclk;
> +       struct clk *clk;
> +       struct clk_init_data init;
> +
> +       mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
> +       if (!mclk) {
> +               pr_err("%s: fail to allocate mmc clk\n", __func__);
> +               return ERR_PTR(-ENOMEM);
> +       }
> +
> +       init.name = mmc_clk->name;
> +       init.ops = &clk_mmc_ops;
> +       init.flags = mmc_clk->flags | CLK_IS_BASIC;
> +       init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
> +       init.num_parents = (mmc_clk->parent_name ? 1 : 0);
> +       mclk->hw.init = &init;
> +
> +       mclk->id = mmc_clk->id;
> +       mclk->clken_reg = base + mmc_clk->clken_reg;
> +       mclk->clken_bit = mmc_clk->clken_bit;
> +       mclk->div_reg = base + mmc_clk->div_reg;
> +       mclk->div_off = mmc_clk->div_off;
> +       mclk->div_bits = mmc_clk->div_bits;
> +       mclk->drv_reg = base + mmc_clk->drv_reg;
> +       mclk->drv_off = mmc_clk->drv_off;
> +       mclk->drv_bits = mmc_clk->drv_bits;
> +       mclk->sam_reg = base + mmc_clk->sam_reg;
> +       mclk->sam_off = mmc_clk->sam_off;
> +       mclk->sam_bits = mmc_clk->sam_bits;
> +
> +       clk = clk_register(NULL, &mclk->hw);
> +       if (WARN_ON(IS_ERR(clk)))
> +               kfree(mclk);
> +       return clk;
> +}
> +
> +static void __init hi3620_mmc_clk_init(struct device_node *node)
> +{
> +       void __iomem *base;
> +       int i, num = ARRAY_SIZE(hi3620_mmc_clks);
> +       struct clk_onecell_data *clk_data;
> +
> +       if (!node) {
> +               pr_err("failed to find pctrl node in DTS\n");
> +               return;
> +       }
> +
> +       base = of_iomap(node, 0);
> +       if (!base) {
> +               pr_err("failed to map pctrl\n");
> +               return;
> +       }
> +
> +       clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
> +       if (WARN_ON(!clk_data))
> +               return;
> +
> +       clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
> +       if (!clk_data->clks) {
> +               pr_err("%s: fail to allocate mmc clk\n", __func__);
> +               return;
> +       }
> +
> +       for (i = 0; i < num; i++) {
> +               struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
> +               clk_data->clks[mmc_clk->id] =
> +                       hisi_register_clk_mmc(mmc_clk, base, node);
> +       }
> +
> +       clk_data->clk_num = num;
> +       of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
> +}
> +
> +CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
> diff --git a/include/dt-bindings/clock/hi3620-clock.h b/include/dt-bindings/clock/hi3620-clock.h
> index 6eaa6a45e110..21b9d0e2eb0c 100644
> --- a/include/dt-bindings/clock/hi3620-clock.h
> +++ b/include/dt-bindings/clock/hi3620-clock.h
> @@ -147,6 +147,11 @@
>  #define HI3620_MMC_CLK3                217
>  #define HI3620_MCU_CLK         218
>  
> +#define HI3620_SD_CIUCLK       0
> +#define HI3620_MMC_CIUCLK1     1
> +#define HI3620_MMC_CIUCLK2     2
> +#define HI3620_MMC_CIUCLK3     3
> +
>  #define HI3620_NR_CLKS         219
>  
>  #endif /* __DTS_HI3620_CLOCK_H */
> -- 
> 1.7.9.5
>
Zhangfei Gao Jan. 15, 2014, 1:40 a.m. UTC | #6
Dear Mike

On 01/15/2014 04:17 AM, Mike Turquette wrote:
> Quoting Zhangfei Gao (2014-01-13 01:14:28)
>> Suggest by Arnd: abstract mmc tuning as clock behavior,
>> also because different soc have different tuning method and registers.
>> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
>>
>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>> Acked-by: Arnd Bergmann <arnd@arndb.de>
>> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
>
> Patch looks good to me with one exception. I do not have
> Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt in the
> clk-next branch. Is there a stable branch I can pull in as a dependency?

Mach-hisi just have been uploaeded.
Have tried next-20140114, the patch can be applied successfully.
While v3.13-rc8 still can not.

Is this fine?

Thanks
Mike Turquette Jan. 15, 2014, 3:53 a.m. UTC | #7
Quoting zhangfei (2014-01-14 17:40:25)
> Dear Mike
> 
> On 01/15/2014 04:17 AM, Mike Turquette wrote:
> > Quoting Zhangfei Gao (2014-01-13 01:14:28)
> >> Suggest by Arnd: abstract mmc tuning as clock behavior,
> >> also because different soc have different tuning method and registers.
> >> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
> >>
> >> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> >> Acked-by: Arnd Bergmann <arnd@arndb.de>
> >> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
> >
> > Patch looks good to me with one exception. I do not have
> > Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt in the
> > clk-next branch. Is there a stable branch I can pull in as a dependency?
> 
> Mach-hisi just have been uploaeded.
> Have tried next-20140114, the patch can be applied successfully.
> While v3.13-rc8 still can not.
> 
> Is this fine?

Can you give me a link to the branch that introduces
Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt?

I guess the patch introducing it is going through arm-soc. Is this going
in for 3.14? If so then perhaps the clk tree and the arm-soc tree can
share a stable branch that introduces it.

Regards,
Mike

> 
> Thanks
Haojian Zhuang Jan. 15, 2014, 5:59 a.m. UTC | #8
On 01/15/2014 11:53 AM, Mike Turquette wrote:
> Quoting zhangfei (2014-01-14 17:40:25)
>> Dear Mike
>>
>> On 01/15/2014 04:17 AM, Mike Turquette wrote:
>>> Quoting Zhangfei Gao (2014-01-13 01:14:28)
>>>> Suggest by Arnd: abstract mmc tuning as clock behavior,
>>>> also because different soc have different tuning method and registers.
>>>> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
>>>>
>>>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>>>> Acked-by: Arnd Bergmann <arnd@arndb.de>
>>>> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
>>> Patch looks good to me with one exception. I do not have
>>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt in the
>>> clk-next branch. Is there a stable branch I can pull in as a dependency?
>> Mach-hisi just have been uploaeded.
>> Have tried next-20140114, the patch can be applied successfully.
>> While v3.13-rc8 still can not.
>>
>> Is this fine?
> Can you give me a link to the branch that introduces
> Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt?
>
> I guess the patch introducing it is going through arm-soc. Is this going
> in for 3.14? If so then perhaps the clk tree and the arm-soc tree can
> share a stable branch that introduces it.
>
> Regards,
> Mike
>
Some patches are merged into arm-soc, and others are in clk tree.
If sharing a stable branch between arm-soc and clk tree, it only means
that we need to revert all commits that are in arm-soc and clk tree.
I think it's too complex.

How about split the patch? The patch on document should enter in arm-soc.

Regards
Haojian
Mike Turquette Jan. 15, 2014, 8:29 a.m. UTC | #9
Quoting Haojian Zhuang (2014-01-14 21:59:40)
> 
> On 01/15/2014 11:53 AM, Mike Turquette wrote:
> > Quoting zhangfei (2014-01-14 17:40:25)
> >> Dear Mike
> >>
> >> On 01/15/2014 04:17 AM, Mike Turquette wrote:
> >>> Quoting Zhangfei Gao (2014-01-13 01:14:28)
> >>>> Suggest by Arnd: abstract mmc tuning as clock behavior,
> >>>> also because different soc have different tuning method and registers.
> >>>> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
> >>>>
> >>>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> >>>> Acked-by: Arnd Bergmann <arnd@arndb.de>
> >>>> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
> >>> Patch looks good to me with one exception. I do not have
> >>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt in the
> >>> clk-next branch. Is there a stable branch I can pull in as a dependency?
> >> Mach-hisi just have been uploaeded.
> >> Have tried next-20140114, the patch can be applied successfully.
> >> While v3.13-rc8 still can not.
> >>
> >> Is this fine?
> > Can you give me a link to the branch that introduces
> > Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt?
> >
> > I guess the patch introducing it is going through arm-soc. Is this going
> > in for 3.14? If so then perhaps the clk tree and the arm-soc tree can
> > share a stable branch that introduces it.
> >
> > Regards,
> > Mike
> >
> Some patches are merged into arm-soc, and others are in clk tree.
> If sharing a stable branch between arm-soc and clk tree, it only means
> that we need to revert all commits that are in arm-soc and clk tree.
> I think it's too complex.

I'm suggesting reverting any patches that are applied to arm-soc. I'm
only suggesting that there might be a common branch that both the clk
and arm-soc trees can depend on to fix this problem.

> 
> How about split the patch? The patch on document should enter in arm-soc.

That is one approach. You might want to run it past the arm-soc folks
first to see if they will take in the binding definition for 3.14.

Regards,
Mike

> 
> Regards
> Haojian
Haojian Zhuang Jan. 15, 2014, 9:34 a.m. UTC | #10
On 01/15/2014 04:29 PM, Mike Turquette wrote:
> Quoting Haojian Zhuang (2014-01-14 21:59:40)
>> On 01/15/2014 11:53 AM, Mike Turquette wrote:
>>> Quoting zhangfei (2014-01-14 17:40:25)
>>>> Dear Mike
>>>>
>>>> On 01/15/2014 04:17 AM, Mike Turquette wrote:
>>>>> Quoting Zhangfei Gao (2014-01-13 01:14:28)
>>>>>> Suggest by Arnd: abstract mmc tuning as clock behavior,
>>>>>> also because different soc have different tuning method and registers.
>>>>>> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
>>>>>>
>>>>>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>>>>>> Acked-by: Arnd Bergmann <arnd@arndb.de>
>>>>>> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
>>>>> Patch looks good to me with one exception. I do not have
>>>>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt in the
>>>>> clk-next branch. Is there a stable branch I can pull in as a dependency?
>>>> Mach-hisi just have been uploaeded.
>>>> Have tried next-20140114, the patch can be applied successfully.
>>>> While v3.13-rc8 still can not.
>>>>
>>>> Is this fine?
>>> Can you give me a link to the branch that introduces
>>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt?
>>>
>>> I guess the patch introducing it is going through arm-soc. Is this going
>>> in for 3.14? If so then perhaps the clk tree and the arm-soc tree can
>>> share a stable branch that introduces it.
>>>
>>> Regards,
>>> Mike
>>>
>> Some patches are merged into arm-soc, and others are in clk tree.
>> If sharing a stable branch between arm-soc and clk tree, it only means
>> that we need to revert all commits that are in arm-soc and clk tree.
>> I think it's too complex.
> I'm suggesting reverting any patches that are applied to arm-soc. I'm
> only suggesting that there might be a common branch that both the clk
> and arm-soc trees can depend on to fix this problem.
>
>> How about split the patch? The patch on document should enter in arm-soc.
> That is one approach. You might want to run it past the arm-soc folks
> first to see if they will take in the binding definition for 3.14.
>
> Regards,
> Mike
>
Yes, so I already made Kevin in this loop. Let's see whether he has
any comments on it.

Regards
Haojian
Kevin Hilman Jan. 15, 2014, 4:14 p.m. UTC | #11
Mike Turquette <mturquette@linaro.org> writes:

> Quoting Haojian Zhuang (2014-01-14 21:59:40)
>> 
>> On 01/15/2014 11:53 AM, Mike Turquette wrote:
>> > Quoting zhangfei (2014-01-14 17:40:25)
>> >> Dear Mike
>> >>
>> >> On 01/15/2014 04:17 AM, Mike Turquette wrote:
>> >>> Quoting Zhangfei Gao (2014-01-13 01:14:28)
>> >>>> Suggest by Arnd: abstract mmc tuning as clock behavior,
>> >>>> also because different soc have different tuning method and registers.
>> >>>> hi3620_mmc_clks is added to handle mmc clock specifically on hi3620.
>> >>>>
>> >>>> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
>> >>>> Acked-by: Arnd Bergmann <arnd@arndb.de>
>> >>>> Acked-by: Jaehoon Chung <jh80.chung@samsung.com>
>> >>> Patch looks good to me with one exception. I do not have
>> >>> Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt in the
>> >>> clk-next branch. Is there a stable branch I can pull in as a dependency?
>> >> Mach-hisi just have been uploaeded.
>> >> Have tried next-20140114, the patch can be applied successfully.
>> >> While v3.13-rc8 still can not.
>> >>
>> >> Is this fine?
>> > Can you give me a link to the branch that introduces
>> > Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt?
>> >
>> > I guess the patch introducing it is going through arm-soc. Is this going
>> > in for 3.14? If so then perhaps the clk tree and the arm-soc tree can
>> > share a stable branch that introduces it.
>> >
>> > Regards,
>> > Mike
>> >
>> Some patches are merged into arm-soc, and others are in clk tree.
>> If sharing a stable branch between arm-soc and clk tree, it only means
>> that we need to revert all commits that are in arm-soc and clk tree.
>> I think it's too complex.
>
> I'm suggesting reverting any patches that are applied to arm-soc. I'm
> only suggesting that there might be a common branch that both the clk
> and arm-soc trees can depend on to fix this problem.

This one is stable, and is where the binding is introduced:

  git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc.git hisi/soc

>> How about split the patch? The patch on document should enter in arm-soc.
>
> That is one approach. You might want to run it past the arm-soc folks
> first to see if they will take in the binding definition for 3.14.

We're not taking anything new for 3.14.

Kevin
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
index 8c7a4653508d..df0a452b8526 100644
--- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
@@ -30,3 +30,17 @@  Example:
 		resume-offset = <0x308>;
 		reboot-offset = <0x4>;
 	};
+
+PCTRL: Peripheral misc control register
+
+Required Properties:
+- compatible: "hisilicon,pctrl"
+- reg: Address and size of pctrl.
+
+Example:
+
+	/* for Hi3620 */
+	pctrl: pctrl@fca09000 {
+		compatible = "hisilicon,pctrl";
+		reg = <0xfca09000 0x1000>;
+	};
diff --git a/Documentation/devicetree/bindings/clock/hi3620-clock.txt b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
index 4b71ab41be53..dad6269f52c5 100644
--- a/Documentation/devicetree/bindings/clock/hi3620-clock.txt
+++ b/Documentation/devicetree/bindings/clock/hi3620-clock.txt
@@ -7,6 +7,7 @@  Required Properties:
 
 - compatible: should be one of the following.
   - "hisilicon,hi3620-clock" - controller compatible with Hi3620 SoC.
+  - "hisilicon,hi3620-mmc-clock" - controller specific for Hi3620 mmc.
 
 - reg: physical base address of the controller and length of memory mapped
   region.
diff --git a/drivers/clk/hisilicon/clk-hi3620.c b/drivers/clk/hisilicon/clk-hi3620.c
index f24ad6a3a797..54cc3475ec36 100644
--- a/drivers/clk/hisilicon/clk-hi3620.c
+++ b/drivers/clk/hisilicon/clk-hi3620.c
@@ -240,3 +240,270 @@  static void __init hi3620_clk_init(struct device_node *np)
 				   base);
 }
 CLK_OF_DECLARE(hi3620_clk, "hisilicon,hi3620-clock", hi3620_clk_init);
+
+struct hisi_mmc_clock {
+	unsigned int		id;
+	const char		*name;
+	const char		*parent_name;
+	unsigned long		flags;
+	u32			clken_reg;
+	u32			clken_bit;
+	u32			div_reg;
+	u32			div_off;
+	u32			div_bits;
+	u32			drv_reg;
+	u32			drv_off;
+	u32			drv_bits;
+	u32			sam_reg;
+	u32			sam_off;
+	u32			sam_bits;
+};
+
+struct clk_mmc {
+	struct clk_hw	hw;
+	u32		id;
+	void __iomem	*clken_reg;
+	u32		clken_bit;
+	void __iomem	*div_reg;
+	u32		div_off;
+	u32		div_bits;
+	void __iomem	*drv_reg;
+	u32		drv_off;
+	u32		drv_bits;
+	void __iomem	*sam_reg;
+	u32		sam_off;
+	u32		sam_bits;
+};
+
+#define to_mmc(_hw) container_of(_hw, struct clk_mmc, hw)
+
+static struct hisi_mmc_clock hi3620_mmc_clks[] __initdata = {
+	{ HI3620_SD_CIUCLK,	"sd_bclk1", "sd_clk", CLK_SET_RATE_PARENT, 0x1f8, 0, 0x1f8, 1, 3, 0x1f8, 4, 4, 0x1f8, 8, 4},
+	{ HI3620_MMC_CIUCLK1,   "mmc_bclk1", "mmc_clk1", CLK_SET_RATE_PARENT, 0x1f8, 12, 0x1f8, 13, 3, 0x1f8, 16, 4, 0x1f8, 20, 4},
+	{ HI3620_MMC_CIUCLK2,   "mmc_bclk2", "mmc_clk2", CLK_SET_RATE_PARENT, 0x1f8, 24, 0x1f8, 25, 3, 0x1f8, 28, 4, 0x1fc, 0, 4},
+	{ HI3620_MMC_CIUCLK3,   "mmc_bclk3", "mmc_clk3", CLK_SET_RATE_PARENT, 0x1fc, 4, 0x1fc, 5, 3, 0x1fc, 8, 4, 0x1fc, 12, 4},
+};
+
+static unsigned long mmc_clk_recalc_rate(struct clk_hw *hw,
+		       unsigned long parent_rate)
+{
+	switch (parent_rate) {
+	case 26000000:
+		return 13000000;
+	case 180000000:
+		return 25000000;
+	case 360000000:
+		return 50000000;
+	case 720000000:
+		return 100000000;
+	default:
+		return parent_rate;
+	}
+}
+
+static long mmc_clk_determine_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long *best_parent_rate,
+			      struct clk **best_parent_p)
+{
+	struct clk_mmc *mclk = to_mmc(hw);
+	unsigned long best = 0;
+
+	if ((rate <= 13000000) && (mclk->id == HI3620_MMC_CIUCLK1)) {
+		rate = 13000000;
+		best = 26000000;
+	} else if (rate <= 26000000) {
+		rate = 25000000;
+		best = 180000000;
+	} else if (rate <= 52000000) {
+		rate = 50000000;
+		best = 360000000;
+	} else if (rate <= 100000000) {
+		rate = 100000000;
+		best = 720000000;
+	} else {
+		/* max is 180M */
+		rate = 180000000;
+		best = 1440000000;
+	}
+	*best_parent_rate = best;
+	return rate;
+}
+
+static u32 mmc_clk_delay(u32 val, u32 para, u32 off, u32 len)
+{
+	u32 i;
+
+	if (para >= 0) {
+		for (i = 0; i < len; i++) {
+			if (para % 2)
+				val |= 1 << (off + i);
+			else
+				val &= ~(1 << (off + i));
+			para = para >> 1;
+		}
+	}
+	return val;
+}
+
+static int mmc_clk_set_timing(struct clk_hw *hw, unsigned long rate)
+{
+	struct clk_mmc *mclk = to_mmc(hw);
+	unsigned long flags;
+	u32 sam, drv, div, val;
+	static DEFINE_SPINLOCK(mmc_clk_lock);
+
+	switch (rate) {
+	case 13000000:
+		sam = 3;
+		drv = 1;
+		div = 1;
+		break;
+	case 25000000:
+		sam = 13;
+		drv = 6;
+		div = 6;
+		break;
+	case 50000000:
+		sam = 3;
+		drv = 6;
+		div = 6;
+		break;
+	case 100000000:
+		sam = 6;
+		drv = 4;
+		div = 6;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&mmc_clk_lock, flags);
+
+	val = readl_relaxed(mclk->clken_reg);
+	val &= ~(1 << mclk->clken_bit);
+	writel_relaxed(val, mclk->clken_reg);
+
+	val = readl_relaxed(mclk->sam_reg);
+	val = mmc_clk_delay(val, sam, mclk->sam_off, mclk->sam_bits);
+	writel_relaxed(val, mclk->sam_reg);
+
+	val = readl_relaxed(mclk->drv_reg);
+	val = mmc_clk_delay(val, drv, mclk->drv_off, mclk->drv_bits);
+	writel_relaxed(val, mclk->drv_reg);
+
+	val = readl_relaxed(mclk->div_reg);
+	val = mmc_clk_delay(val, div, mclk->div_off, mclk->div_bits);
+	writel_relaxed(val, mclk->div_reg);
+
+	val = readl_relaxed(mclk->clken_reg);
+	val |= 1 << mclk->clken_bit;
+	writel_relaxed(val, mclk->clken_reg);
+
+	spin_unlock_irqrestore(&mmc_clk_lock, flags);
+
+	return 0;
+}
+
+static int mmc_clk_prepare(struct clk_hw *hw)
+{
+	struct clk_mmc *mclk = to_mmc(hw);
+	unsigned long rate;
+
+	if (mclk->id == HI3620_MMC_CIUCLK1)
+		rate = 13000000;
+	else
+		rate = 25000000;
+
+	return mmc_clk_set_timing(hw, rate);
+}
+
+static int mmc_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+			     unsigned long parent_rate)
+{
+	return mmc_clk_set_timing(hw, rate);
+}
+
+static struct clk_ops clk_mmc_ops = {
+	.prepare = mmc_clk_prepare,
+	.determine_rate = mmc_clk_determine_rate,
+	.set_rate = mmc_clk_set_rate,
+	.recalc_rate = mmc_clk_recalc_rate,
+};
+
+static struct clk *hisi_register_clk_mmc(struct hisi_mmc_clock *mmc_clk,
+			void __iomem *base, struct device_node *np)
+{
+	struct clk_mmc *mclk;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	mclk = kzalloc(sizeof(*mclk), GFP_KERNEL);
+	if (!mclk) {
+		pr_err("%s: fail to allocate mmc clk\n", __func__);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	init.name = mmc_clk->name;
+	init.ops = &clk_mmc_ops;
+	init.flags = mmc_clk->flags | CLK_IS_BASIC;
+	init.parent_names = (mmc_clk->parent_name ? &mmc_clk->parent_name : NULL);
+	init.num_parents = (mmc_clk->parent_name ? 1 : 0);
+	mclk->hw.init = &init;
+
+	mclk->id = mmc_clk->id;
+	mclk->clken_reg = base + mmc_clk->clken_reg;
+	mclk->clken_bit = mmc_clk->clken_bit;
+	mclk->div_reg = base + mmc_clk->div_reg;
+	mclk->div_off = mmc_clk->div_off;
+	mclk->div_bits = mmc_clk->div_bits;
+	mclk->drv_reg = base + mmc_clk->drv_reg;
+	mclk->drv_off = mmc_clk->drv_off;
+	mclk->drv_bits = mmc_clk->drv_bits;
+	mclk->sam_reg = base + mmc_clk->sam_reg;
+	mclk->sam_off = mmc_clk->sam_off;
+	mclk->sam_bits = mmc_clk->sam_bits;
+
+	clk = clk_register(NULL, &mclk->hw);
+	if (WARN_ON(IS_ERR(clk)))
+		kfree(mclk);
+	return clk;
+}
+
+static void __init hi3620_mmc_clk_init(struct device_node *node)
+{
+	void __iomem *base;
+	int i, num = ARRAY_SIZE(hi3620_mmc_clks);
+	struct clk_onecell_data *clk_data;
+
+	if (!node) {
+		pr_err("failed to find pctrl node in DTS\n");
+		return;
+	}
+
+	base = of_iomap(node, 0);
+	if (!base) {
+		pr_err("failed to map pctrl\n");
+		return;
+	}
+
+	clk_data = kzalloc(sizeof(*clk_data), GFP_KERNEL);
+	if (WARN_ON(!clk_data))
+		return;
+
+	clk_data->clks = kzalloc(sizeof(struct clk *) * num, GFP_KERNEL);
+	if (!clk_data->clks) {
+		pr_err("%s: fail to allocate mmc clk\n", __func__);
+		return;
+	}
+
+	for (i = 0; i < num; i++) {
+		struct hisi_mmc_clock *mmc_clk = &hi3620_mmc_clks[i];
+		clk_data->clks[mmc_clk->id] =
+			hisi_register_clk_mmc(mmc_clk, base, node);
+	}
+
+	clk_data->clk_num = num;
+	of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+CLK_OF_DECLARE(hi3620_mmc_clk, "hisilicon,hi3620-mmc-clock", hi3620_mmc_clk_init);
diff --git a/include/dt-bindings/clock/hi3620-clock.h b/include/dt-bindings/clock/hi3620-clock.h
index 6eaa6a45e110..21b9d0e2eb0c 100644
--- a/include/dt-bindings/clock/hi3620-clock.h
+++ b/include/dt-bindings/clock/hi3620-clock.h
@@ -147,6 +147,11 @@ 
 #define HI3620_MMC_CLK3		217
 #define HI3620_MCU_CLK		218
 
+#define HI3620_SD_CIUCLK	0
+#define HI3620_MMC_CIUCLK1	1
+#define HI3620_MMC_CIUCLK2	2
+#define HI3620_MMC_CIUCLK3	3
+
 #define HI3620_NR_CLKS		219
 
 #endif	/* __DTS_HI3620_CLOCK_H */