diff mbox

[2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform

Message ID 1386987174-1788-3-git-send-email-zhangfei.gao@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Zhangfei Gao Dec. 14, 2013, 2:12 a.m. UTC
Add dw_mmc-k3.c for k3v2, support sd/emmc

Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
Signed-off-by: Zhigang Wang <brooke.wangzhigang@huawei.com>
---
 .../devicetree/bindings/mmc/k3-dw-mshc.txt         |   60 +++++++++
 drivers/mmc/host/Kconfig                           |   10 ++
 drivers/mmc/host/Makefile                          |    1 +
 drivers/mmc/host/dw_mmc-k3.c                       |  133 ++++++++++++++++++++
 4 files changed, 204 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
 create mode 100644 drivers/mmc/host/dw_mmc-k3.c

Comments

Seungwon Jeon Dec. 16, 2013, 3:50 a.m. UTC | #1
On Sat, December 14, 2013, Zhangfei Gao wrote:
> Add dw_mmc-k3.c for k3v2, support sd/emmc
> 
> Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
> Signed-off-by: Zhigang Wang <brooke.wangzhigang@huawei.com>
> ---
>  .../devicetree/bindings/mmc/k3-dw-mshc.txt         |   60 +++++++++
>  drivers/mmc/host/Kconfig                           |   10 ++
>  drivers/mmc/host/Makefile                          |    1 +
>  drivers/mmc/host/dw_mmc-k3.c                       |  133 ++++++++++++++++++++
>  4 files changed, 204 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
>  create mode 100644 drivers/mmc/host/dw_mmc-k3.c
> 
> diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
> b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
> new file mode 100644
> index 000000000000..d7e2d7f159bb
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
> @@ -0,0 +1,60 @@
> +* Hisilicon specific extensions to the Synopsys Designware Mobile
> +  Storage Host Controller
> +
> +Read synopsys-dw-mshc.txt for more details
> +
> +The Synopsys designware mobile storage host controller is used to interface
> +a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
> +differences between the core Synopsys dw mshc controller properties described
> +by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific
> +extensions to the Synopsys Designware Mobile Storage Host Controller.
> +
> +Required Properties:
> +
> +* compatible: should be one of the following.
> +  - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extentions.
> +
> +* clock-freq-table: should be the frequency (in Hz) array of the ciu clock
> +	in each	supported mode.
> +	0. CIU clock rate in Hz for DS mode
> +	1. CIU clock rate in Hz for MMC HS mode
> +	2. CIU clock rate in Hz for SD HS mode
> +	3. CIU clock rate in Hz for SDR12 mode
> +	4. CIU clock rate in Hz for SDR25 mode
> +	5. CIU clock rate in Hz for SDR50 mode
> +	6. CIU clock rate in Hz for SDR104 mode
> +	7. CIU clock rate in Hz for DDR50 mode
> +	8. CIU clock rate in Hz for HS200 mode
> +
> +Example:
> +
> +	/* for Hi3620 */
> +
> +	/* SoC portion */
> +	dwmmc_0: dwmmc0@fcd03000 {
> +		compatible = "hisilicon,hi4511-dw-mshc";
> +		reg = <0xfcd03000 0x1000>;
> +		interrupts = <0 16 4>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
> +		clock-names = "ciu", "biu";
> +		clock-freq-table =
> +		<25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
I think it could be solved with mmc->f_min and mmc->f_max(from clock-freq-min-max property)
if there is no limitation per each speed mode. As seeing described table, it looks that.

> +	};
> +
> +	/* Board portion */
> +	dwmmc0@fcd03000 {
> +		num-slots = <1>;
> +		vmmc-supply = <&ldo12>;
> +		fifo-depth = <0x100>;
> +		supports-highspeed;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
> +		slot@0 {
> +			reg = <0>;
> +			bus-width = <4>;
> +			disable-wp;
> +			cd-gpios = <&gpio10 3 0>;
> +		};
> +	};
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 7fc5099e44b2..45aaa2de0f58 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -575,6 +575,16 @@ config MMC_DW_SOCFPGA
>  	  This selects support for Altera SoCFPGA specific extensions to the
>  	  Synopsys DesignWare Memory Card Interface driver.
> 
> +config MMC_DW_K3
> +	tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
> +	depends on MMC_DW
> +	select MMC_DW_PLTFM
> +	select MMC_DW_IDMAC
> +	help
> +	  This selects support for Hisilicon K3 SoC specific extensions to the
> +	  Synopsys DesignWare Memory Card Interface driver. Select this option
> +	  for platforms based on Hisilicon K3 SoC's.
> +
>  config MMC_DW_PCI
>  	tristate "Synopsys Designware MCI support on PCI bus"
>  	depends on MMC_DW && PCI
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index c41d0c364509..64f5f8d35839 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -43,6 +43,7 @@ obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
>  obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o
>  obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
>  obj-$(CONFIG_MMC_DW_SOCFPGA)	+= dw_mmc-socfpga.o
> +obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o
>  obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
>  obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
>  obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
> diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
> new file mode 100644
> index 000000000000..90ebee0ce5ef
> --- /dev/null
> +++ b/drivers/mmc/host/dw_mmc-k3.c
> @@ -0,0 +1,133 @@
> +/*
> + * Copyright (c) 2013 Linaro Ltd.
> + * Copyright (c) 2013 Hisilicon Limited.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/clk.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/dw_mmc.h>
> +#include <linux/of_address.h>
> +
> +#include "dw_mmc.h"
> +#include "dw_mmc-pltfm.h"
> +
> +#define MAX_NUMS	10
> +struct dw_mci_k3_priv_data {
> +	u32	clk_table[MAX_NUMS];
> +};
> +
> +static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
> +{
> +	struct dw_mci_k3_priv_data *priv = host->priv;
> +	u32 rate = priv->clk_table[ios->timing];
> +	int ret;
> +
> +	ret = clk_set_rate(host->ciu_clk, rate);
> +	if (ret)
> +		dev_warn(host->dev, "failed to set clock rate %uHz\n", rate);
> +
> +	host->bus_hz = clk_get_rate(host->ciu_clk);
> +}
> +
> +static int dw_mci_k3_parse_dt(struct dw_mci *host)
> +{
> +	struct dw_mci_k3_priv_data *priv;
> +	struct device_node *node = host->dev->of_node;
> +	struct property *prop;
> +	const __be32 *cur;
> +	u32 val, num = 0;
> +
> +	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv) {
> +		dev_err(host->dev, "mem alloc failed for private data\n");
> +		return -ENOMEM;
> +	}
> +	host->priv = priv;
> +
> +	of_property_for_each_u32(node, "clock-freq-table", prop, cur, val) {
> +		if (num >= MAX_NUMS)
> +			break;
> +		priv->clk_table[num++] = val;
> +	}
> +	return 0;
> +}
> +
> +static unsigned long k3_dwmmc_caps[4] = {
> +	MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED,
> +	MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED,
> +	0,
> +	0,
> +};
> +
> +static const struct dw_mci_drv_data k3_drv_data = {
> +	.caps			= k3_dwmmc_caps,
> +	.set_ios		= dw_mci_k3_set_ios,
> +	.parse_dt		= dw_mci_k3_parse_dt,
> +};
> +
> +static const struct of_device_id dw_mci_k3_match[] = {
> +	{ .compatible = "hisilicon,hi4511-dw-mshc",
> +			.data = &k3_drv_data, },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
> +
> +static int dw_mci_k3_probe(struct platform_device *pdev)
> +{
> +	const struct dw_mci_drv_data *drv_data;
> +	const struct of_device_id *match;
> +
> +	match = of_match_node(dw_mci_k3_match, pdev->dev.of_node);
> +	drv_data = match->data;
> +
> +	return dw_mci_pltfm_register(pdev, drv_data);
> +}
> +
> +static int dw_mci_k3_suspend(struct device *dev)
> +{
> +	struct dw_mci *host = dev_get_drvdata(dev);
> +
> +	if (!IS_ERR(host->ciu_clk))
> +		clk_disable_unprepare(host->ciu_clk);
> +
> +	return dw_mci_suspend(host);
If k3 needs clk_disable here, dw_mci_suspend()is expected to be called prior to clk_disable_unprepare()?
Of course, it's ok at present though. 

Thanks,
Seungwon Jeon
> +}
> +
> +static int dw_mci_k3_resume(struct device *dev)
> +{
> +	struct dw_mci *host = dev_get_drvdata(dev);
> +	int ret = 0;
> +
> +	ret = clk_prepare_enable(host->ciu_clk);
> +	if (ret) {
> +		dev_err(host->dev, "failed to enable ciu clock\n");
> +		return ret;
> +	}
> +
> +	return dw_mci_resume(host);
> +}
> +
> +SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
> +
> +static struct platform_driver dw_mci_k3_pltfm_driver = {
> +	.probe		= dw_mci_k3_probe,
> +	.remove		= dw_mci_pltfm_remove,
> +	.driver		= {
> +		.name		= "dwmmc_k3",
> +		.of_match_table	= dw_mci_k3_match,
> +		.pm		= &dw_mci_k3_pmops,
> +	},
> +};
> +
> +module_platform_driver(dw_mci_k3_pltfm_driver);
> +
> +MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:dwmmc-k3");
> --
> 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 Dec. 16, 2013, 5:05 a.m. UTC | #2
Dear Seungwon

On 12/16/2013 11:50 AM, Seungwon Jeon wrote:
> On Sat, December 14, 2013, Zhangfei Gao wrote:

>> +	/* SoC portion */
>> +	dwmmc_0: dwmmc0@fcd03000 {
>> +		compatible = "hisilicon,hi4511-dw-mshc";
>> +		reg = <0xfcd03000 0x1000>;
>> +		interrupts = <0 16 4>;
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +		clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
>> +		clock-names = "ciu", "biu";
>> +		clock-freq-table =
>> +		<25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
> I think it could be solved with mmc->f_min and mmc->f_max(from clock-freq-min-max property)
> if there is no limitation per each speed mode. As seeing described table, it looks that.

Have tried them before, but unfortunately, they are different.

The controller can not generate clock itself, while depending on the
outside clock generator, which may require to change clock source in
differnt mode.

The value we want is set the capacibility of the clock input and the max
clock freq where mmc works stable in that mode, which may be different
in different mode.

clock-freq-min-max will set the value for set_ios.
For example, we use 25M as clock capability when init, which can not be
set as freq-min, and used as set_ios, where 400K should be used,
otherwise init definitely fail.

>> +static int dw_mci_k3_suspend(struct device *dev)
>> +{
>> +	struct dw_mci *host = dev_get_drvdata(dev);
>> +
>> +	if (!IS_ERR(host->ciu_clk))
>> +		clk_disable_unprepare(host->ciu_clk);
>> +
>> +	return dw_mci_suspend(host);
> If k3 needs clk_disable here, dw_mci_suspend()is expected to be called prior to clk_disable_unprepare()?
> Of course, it's ok at present though.
> 

Sure, it can be switched, though dw_mci_suspend does nothing related
with clock now.

Thanks
Seungwon Jeon Dec. 16, 2013, 7:29 a.m. UTC | #3
On Mon, December 16, 2013, Zhangfei Gao wrote: 
> Dear Seungwon
> 
> On 12/16/2013 11:50 AM, Seungwon Jeon wrote:
> > On Sat, December 14, 2013, Zhangfei Gao wrote:
> 
> >> +	/* SoC portion */
> >> +	dwmmc_0: dwmmc0@fcd03000 {
> >> +		compatible = "hisilicon,hi4511-dw-mshc";
> >> +		reg = <0xfcd03000 0x1000>;
> >> +		interrupts = <0 16 4>;
> >> +		#address-cells = <1>;
> >> +		#size-cells = <0>;
> >> +		clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
> >> +		clock-names = "ciu", "biu";
> >> +		clock-freq-table =
> >> +		<25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
> > I think it could be solved with mmc->f_min and mmc->f_max(from clock-freq-min-max property)
> > if there is no limitation per each speed mode. As seeing described table, it looks that.
> 
> Have tried them before, but unfortunately, they are different.
> 
> The controller can not generate clock itself, while depending on the
> outside clock generator, which may require to change clock source in
> differnt mode.
> 
> The value we want is set the capacibility of the clock input and the max
> clock freq where mmc works stable in that mode, which may be different
> in different mode.
> 
> clock-freq-min-max will set the value for set_ios.
> For example, we use 25M as clock capability when init, which can not be
> set as freq-min, and used as set_ios, where 400K should be used,
> otherwise init definitely fail.
Can you check 'f_init' within 'drivers/mmc/core/core.c' file?
If you set 'f_min = 25000000' for init-sequence, mmc_set_ios() will pass that rate through 'ios.clock'
Then, dw_mmc-k3 can do clk_set_rate() with 25MHz at dw_mci_k3_set_ios().
And other required speeds for each mode seem not specific compared with standard spec.
Also clk_set_rate() would be possible with 'ios.clock' instead of specific table.
I just referred your example's clock-freq-table above.

> 
> >> +static int dw_mci_k3_suspend(struct device *dev)
> >> +{
> >> +	struct dw_mci *host = dev_get_drvdata(dev);
> >> +
> >> +	if (!IS_ERR(host->ciu_clk))
> >> +		clk_disable_unprepare(host->ciu_clk);
> >> +
> >> +	return dw_mci_suspend(host);
> > If k3 needs clk_disable here, dw_mci_suspend()is expected to be called prior to
> clk_disable_unprepare()?
> > Of course, it's ok at present though.
> >
> 
> Sure, it can be switched, though dw_mci_suspend does nothing related
> with clock now.
Yes, but if some works with ciu_clk is added in dw_mci_suspend(), it will affect.

Thanks,
Seungwon Jeon
Zhangfei Gao Dec. 16, 2013, 8:08 a.m. UTC | #4
On 12/16/2013 03:29 PM, Seungwon Jeon wrote:
> On Mon, December 16, 2013, Zhangfei Gao wrote:
>> Dear Seungwon
>>
>> On 12/16/2013 11:50 AM, Seungwon Jeon wrote:
>>> On Sat, December 14, 2013, Zhangfei Gao wrote:
>>
>>>> +	/* SoC portion */
>>>> +	dwmmc_0: dwmmc0@fcd03000 {
>>>> +		compatible = "hisilicon,hi4511-dw-mshc";
>>>> +		reg = <0xfcd03000 0x1000>;
>>>> +		interrupts = <0 16 4>;
>>>> +		#address-cells = <1>;
>>>> +		#size-cells = <0>;
>>>> +		clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
>>>> +		clock-names = "ciu", "biu";
>>>> +		clock-freq-table =
>>>> +		<25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
>>> I think it could be solved with mmc->f_min and mmc->f_max(from clock-freq-min-max property)
>>> if there is no limitation per each speed mode. As seeing described table, it looks that.
>>
>> Have tried them before, but unfortunately, they are different.
>>
>> The controller can not generate clock itself, while depending on the
>> outside clock generator, which may require to change clock source in
>> differnt mode.
>>
>> The value we want is set the capacibility of the clock input and the max
>> clock freq where mmc works stable in that mode, which may be different
>> in different mode.
>>
>> clock-freq-min-max will set the value for set_ios.
>> For example, we use 25M as clock capability when init, which can not be
>> set as freq-min, and used as set_ios, where 400K should be used,
>> otherwise init definitely fail.
> Can you check 'f_init' within 'drivers/mmc/core/core.c' file?
> If you set 'f_min = 25000000' for init-sequence, mmc_set_ios() will pass that rate through 'ios.clock'
> Then, dw_mmc-k3 can do clk_set_rate() with 25MHz at dw_mci_k3_set_ios().

This means controller directly use 25M init emmc and sd card, it will fail.
We still need 400K init freq, while the input clock source is 25M,
divided by the controller.

> And other required speeds for each mode seem not specific compared with standard spec.
> Also clk_set_rate() would be possible with 'ios.clock' instead of specific table.
> I just referred your example's clock-freq-table above.
There also some controller limitation currently.
In some mode, the controller only works well in the limited freq.
For example UHS_SDR104_MAX_DTR 208000000 can not be used, only half may
be reached, at least currently

> 
>>
>>>> +static int dw_mci_k3_suspend(struct device *dev)
>>>> +{
>>>> +	struct dw_mci *host = dev_get_drvdata(dev);
>>>> +
>>>> +	if (!IS_ERR(host->ciu_clk))
>>>> +		clk_disable_unprepare(host->ciu_clk);
>>>> +
>>>> +	return dw_mci_suspend(host);
>>> If k3 needs clk_disable here, dw_mci_suspend()is expected to be called prior to
>> clk_disable_unprepare()?
>>> Of course, it's ok at present though.
>>>
>>
>> Sure, it can be switched, though dw_mci_suspend does nothing related
>> with clock now.
> Yes, but if some works with ciu_clk is added in dw_mci_suspend(), it will affect.
> 
Sorry for the misunderstood, I mean it make sense and will change.
Thanks the advice.
Seungwon Jeon Dec. 16, 2013, 9:18 a.m. UTC | #5
On Mon, December 16, 2013, Zhangfei Gao wrote:
> On 12/16/2013 03:29 PM, Seungwon Jeon wrote:
> > On Mon, December 16, 2013, Zhangfei Gao wrote:
> >> Dear Seungwon
> >>
> >> On 12/16/2013 11:50 AM, Seungwon Jeon wrote:
> >>> On Sat, December 14, 2013, Zhangfei Gao wrote:
> >>
> >>>> +	/* SoC portion */
> >>>> +	dwmmc_0: dwmmc0@fcd03000 {
> >>>> +		compatible = "hisilicon,hi4511-dw-mshc";
> >>>> +		reg = <0xfcd03000 0x1000>;
> >>>> +		interrupts = <0 16 4>;
> >>>> +		#address-cells = <1>;
> >>>> +		#size-cells = <0>;
> >>>> +		clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
> >>>> +		clock-names = "ciu", "biu";
> >>>> +		clock-freq-table =
> >>>> +		<25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
> >>> I think it could be solved with mmc->f_min and mmc->f_max(from clock-freq-min-max property)
> >>> if there is no limitation per each speed mode. As seeing described table, it looks that.
> >>
> >> Have tried them before, but unfortunately, they are different.
> >>
> >> The controller can not generate clock itself, while depending on the
> >> outside clock generator, which may require to change clock source in
> >> differnt mode.
> >>
> >> The value we want is set the capacibility of the clock input and the max
> >> clock freq where mmc works stable in that mode, which may be different
> >> in different mode.
> >>
> >> clock-freq-min-max will set the value for set_ios.
> >> For example, we use 25M as clock capability when init, which can not be
> >> set as freq-min, and used as set_ios, where 400K should be used,
> >> otherwise init definitely fail.
> > Can you check 'f_init' within 'drivers/mmc/core/core.c' file?
> > If you set 'f_min = 25000000' for init-sequence, mmc_set_ios() will pass that rate through
> 'ios.clock'
> > Then, dw_mmc-k3 can do clk_set_rate() with 25MHz at dw_mci_k3_set_ios().
> 
> This means controller directly use 25M init emmc and sd card, it will fail.
> We still need 400K init freq, while the input clock source is 25M,
> divided by the controller.
> 
> > And other required speeds for each mode seem not specific compared with standard spec.
> > Also clk_set_rate() would be possible with 'ios.clock' instead of specific table.
> > I just referred your example's clock-freq-table above.
> There also some controller limitation currently.
> In some mode, the controller only works well in the limited freq.
> For example UHS_SDR104_MAX_DTR 208000000 can not be used, only half may
> be reached, at least currently
Ok. I see your condition. I misunderstood a little bit.
I wonder whether limitation is only max frequency(SDR104 or HS200) except for init-freq.
How about other mode? Working is fine with normal clock rate(from clock-freq-table)?

Thanks,
Seungwon Jeon

> 
> >
> >>
> >>>> +static int dw_mci_k3_suspend(struct device *dev)
> >>>> +{
> >>>> +	struct dw_mci *host = dev_get_drvdata(dev);
> >>>> +
> >>>> +	if (!IS_ERR(host->ciu_clk))
> >>>> +		clk_disable_unprepare(host->ciu_clk);
> >>>> +
> >>>> +	return dw_mci_suspend(host);
> >>> If k3 needs clk_disable here, dw_mci_suspend()is expected to be called prior to
> >> clk_disable_unprepare()?
> >>> Of course, it's ok at present though.
> >>>
> >>
> >> Sure, it can be switched, though dw_mci_suspend does nothing related
> >> with clock now.
> > Yes, but if some works with ciu_clk is added in dw_mci_suspend(), it will affect.
> >
> Sorry for the misunderstood, I mean it make sense and will change.
> Thanks the advice.
> 
> 
> --
> 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 Dec. 16, 2013, 11:07 a.m. UTC | #6
On 12/16/2013 05:18 PM, Seungwon Jeon wrote:
> On Mon, December 16, 2013, Zhangfei Gao wrote:
>> On 12/16/2013 03:29 PM, Seungwon Jeon wrote:
>>> On Mon, December 16, 2013, Zhangfei Gao wrote:
>>>> Dear Seungwon
>>>>
>>>> On 12/16/2013 11:50 AM, Seungwon Jeon wrote:
>>>>> On Sat, December 14, 2013, Zhangfei Gao wrote:
>>>>
>>>>>> +	/* SoC portion */
>>>>>> +	dwmmc_0: dwmmc0@fcd03000 {
>>>>>> +		compatible = "hisilicon,hi4511-dw-mshc";
>>>>>> +		reg = <0xfcd03000 0x1000>;
>>>>>> +		interrupts = <0 16 4>;
>>>>>> +		#address-cells = <1>;
>>>>>> +		#size-cells = <0>;
>>>>>> +		clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
>>>>>> +		clock-names = "ciu", "biu";
>>>>>> +		clock-freq-table =
>>>>>> +		<25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
>>>>> I think it could be solved with mmc->f_min and mmc->f_max(from clock-freq-min-max property)
>>>>> if there is no limitation per each speed mode. As seeing described table, it looks that.
>>>>
>>>> Have tried them before, but unfortunately, they are different.
>>>>
>>>> The controller can not generate clock itself, while depending on the
>>>> outside clock generator, which may require to change clock source in
>>>> differnt mode.
>>>>
>>>> The value we want is set the capacibility of the clock input and the max
>>>> clock freq where mmc works stable in that mode, which may be different
>>>> in different mode.
>>>>
>>>> clock-freq-min-max will set the value for set_ios.
>>>> For example, we use 25M as clock capability when init, which can not be
>>>> set as freq-min, and used as set_ios, where 400K should be used,
>>>> otherwise init definitely fail.
>>> Can you check 'f_init' within 'drivers/mmc/core/core.c' file?
>>> If you set 'f_min = 25000000' for init-sequence, mmc_set_ios() will pass that rate through
>> 'ios.clock'
>>> Then, dw_mmc-k3 can do clk_set_rate() with 25MHz at dw_mci_k3_set_ios().
>>
>> This means controller directly use 25M init emmc and sd card, it will fail.
>> We still need 400K init freq, while the input clock source is 25M,
>> divided by the controller.
>>
>>> And other required speeds for each mode seem not specific compared with standard spec.
>>> Also clk_set_rate() would be possible with 'ios.clock' instead of specific table.
>>> I just referred your example's clock-freq-table above.
>> There also some controller limitation currently.
>> In some mode, the controller only works well in the limited freq.
>> For example UHS_SDR104_MAX_DTR 208000000 can not be used, only half may
>> be reached, at least currently
> Ok. I see your condition. I misunderstood a little bit.
> I wonder whether limitation is only max frequency(SDR104 or HS200) except for init-freq.
> How about other mode? Working is fine with normal clock rate(from clock-freq-table)?
> 

The root cause is the controller can not generate stable clock itself,
but depends on outside clock source, which is varying, and driver need
to decide input clock rate / choose clock source according to the
working mode.

Three cases here:
1. Input rate for init are diferent for different controller, not the
init 400K, some are 13M, others are 25M, since different clock source.
This can be easily solved by clock-freq-init = <25000000>

2. There is maxmum limit, also can be easily solved by define CLK_MAX.
For example, CLK_MAX = 180M.

3. However some mode can not use the max speed from ios->clock
for example UHS_SDR104_MAX_DTR 208000000 can not be used, only half may
be reached, at least currently.

The third case is current limitation, after this limitation is solved,
we can switch to ios->clock directly.

Considering all these cases, we think using table is more suitable now.

Thanks
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
new file mode 100644
index 000000000000..d7e2d7f159bb
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt
@@ -0,0 +1,60 @@ 
+* Hisilicon specific extensions to the Synopsys Designware Mobile
+  Storage Host Controller
+
+Read synopsys-dw-mshc.txt for more details
+
+The Synopsys designware mobile storage host controller is used to interface
+a SoC with storage medium such as eMMC or SD/MMC cards. This file documents
+differences between the core Synopsys dw mshc controller properties described
+by synopsys-dw-mshc.txt and the properties used by the Hisilicon specific
+extensions to the Synopsys Designware Mobile Storage Host Controller.
+
+Required Properties:
+
+* compatible: should be one of the following.
+  - "hisilicon,hi4511-dw-mshc": for controllers with hi4511 specific extentions.
+
+* clock-freq-table: should be the frequency (in Hz) array of the ciu clock
+	in each	supported mode.
+	0. CIU clock rate in Hz for DS mode
+	1. CIU clock rate in Hz for MMC HS mode
+	2. CIU clock rate in Hz for SD HS mode
+	3. CIU clock rate in Hz for SDR12 mode
+	4. CIU clock rate in Hz for SDR25 mode
+	5. CIU clock rate in Hz for SDR50 mode
+	6. CIU clock rate in Hz for SDR104 mode
+	7. CIU clock rate in Hz for DDR50 mode
+	8. CIU clock rate in Hz for HS200 mode
+
+Example:
+
+	/* for Hi3620 */
+
+	/* SoC portion */
+	dwmmc_0: dwmmc0@fcd03000 {
+		compatible = "hisilicon,hi4511-dw-mshc";
+		reg = <0xfcd03000 0x1000>;
+		interrupts = <0 16 4>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		clocks = <&mmc_clock HI3620_SD_CIUCLK>, <&clock HI3620_DDRC_PER_CLK>;
+		clock-names = "ciu", "biu";
+		clock-freq-table =
+		<25000000 0 50000000 25000000 50000000 100000000 0 50000000>;
+	};
+
+	/* Board portion */
+	dwmmc0@fcd03000 {
+		num-slots = <1>;
+		vmmc-supply = <&ldo12>;
+		fifo-depth = <0x100>;
+		supports-highspeed;
+		pinctrl-names = "default";
+		pinctrl-0 = <&sd_pmx_pins &sd_cfg_func1 &sd_cfg_func2>;
+		slot@0 {
+			reg = <0>;
+			bus-width = <4>;
+			disable-wp;
+			cd-gpios = <&gpio10 3 0>;
+		};
+	};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 7fc5099e44b2..45aaa2de0f58 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -575,6 +575,16 @@  config MMC_DW_SOCFPGA
 	  This selects support for Altera SoCFPGA specific extensions to the
 	  Synopsys DesignWare Memory Card Interface driver.
 
+config MMC_DW_K3
+	tristate "K3 specific extensions for Synopsys DW Memory Card Interface"
+	depends on MMC_DW
+	select MMC_DW_PLTFM
+	select MMC_DW_IDMAC
+	help
+	  This selects support for Hisilicon K3 SoC specific extensions to the
+	  Synopsys DesignWare Memory Card Interface driver. Select this option
+	  for platforms based on Hisilicon K3 SoC's.
+
 config MMC_DW_PCI
 	tristate "Synopsys Designware MCI support on PCI bus"
 	depends on MMC_DW && PCI
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index c41d0c364509..64f5f8d35839 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -43,6 +43,7 @@  obj-$(CONFIG_MMC_DW)		+= dw_mmc.o
 obj-$(CONFIG_MMC_DW_PLTFM)	+= dw_mmc-pltfm.o
 obj-$(CONFIG_MMC_DW_EXYNOS)	+= dw_mmc-exynos.o
 obj-$(CONFIG_MMC_DW_SOCFPGA)	+= dw_mmc-socfpga.o
+obj-$(CONFIG_MMC_DW_K3)		+= dw_mmc-k3.o
 obj-$(CONFIG_MMC_DW_PCI)	+= dw_mmc-pci.o
 obj-$(CONFIG_MMC_SH_MMCIF)	+= sh_mmcif.o
 obj-$(CONFIG_MMC_JZ4740)	+= jz4740_mmc.o
diff --git a/drivers/mmc/host/dw_mmc-k3.c b/drivers/mmc/host/dw_mmc-k3.c
new file mode 100644
index 000000000000..90ebee0ce5ef
--- /dev/null
+++ b/drivers/mmc/host/dw_mmc-k3.c
@@ -0,0 +1,133 @@ 
+/*
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/dw_mmc.h>
+#include <linux/of_address.h>
+
+#include "dw_mmc.h"
+#include "dw_mmc-pltfm.h"
+
+#define MAX_NUMS	10
+struct dw_mci_k3_priv_data {
+	u32	clk_table[MAX_NUMS];
+};
+
+static void dw_mci_k3_set_ios(struct dw_mci *host, struct mmc_ios *ios)
+{
+	struct dw_mci_k3_priv_data *priv = host->priv;
+	u32 rate = priv->clk_table[ios->timing];
+	int ret;
+
+	ret = clk_set_rate(host->ciu_clk, rate);
+	if (ret)
+		dev_warn(host->dev, "failed to set clock rate %uHz\n", rate);
+
+	host->bus_hz = clk_get_rate(host->ciu_clk);
+}
+
+static int dw_mci_k3_parse_dt(struct dw_mci *host)
+{
+	struct dw_mci_k3_priv_data *priv;
+	struct device_node *node = host->dev->of_node;
+	struct property *prop;
+	const __be32 *cur;
+	u32 val, num = 0;
+
+	priv = devm_kzalloc(host->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv) {
+		dev_err(host->dev, "mem alloc failed for private data\n");
+		return -ENOMEM;
+	}
+	host->priv = priv;
+
+	of_property_for_each_u32(node, "clock-freq-table", prop, cur, val) {
+		if (num >= MAX_NUMS)
+			break;
+		priv->clk_table[num++] = val;
+	}
+	return 0;
+}
+
+static unsigned long k3_dwmmc_caps[4] = {
+	MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED,
+	MMC_CAP_8_BIT_DATA | MMC_CAP_MMC_HIGHSPEED,
+	0,
+	0,
+};
+
+static const struct dw_mci_drv_data k3_drv_data = {
+	.caps			= k3_dwmmc_caps,
+	.set_ios		= dw_mci_k3_set_ios,
+	.parse_dt		= dw_mci_k3_parse_dt,
+};
+
+static const struct of_device_id dw_mci_k3_match[] = {
+	{ .compatible = "hisilicon,hi4511-dw-mshc",
+			.data = &k3_drv_data, },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_mci_k3_match);
+
+static int dw_mci_k3_probe(struct platform_device *pdev)
+{
+	const struct dw_mci_drv_data *drv_data;
+	const struct of_device_id *match;
+
+	match = of_match_node(dw_mci_k3_match, pdev->dev.of_node);
+	drv_data = match->data;
+
+	return dw_mci_pltfm_register(pdev, drv_data);
+}
+
+static int dw_mci_k3_suspend(struct device *dev)
+{
+	struct dw_mci *host = dev_get_drvdata(dev);
+
+	if (!IS_ERR(host->ciu_clk))
+		clk_disable_unprepare(host->ciu_clk);
+
+	return dw_mci_suspend(host);
+}
+
+static int dw_mci_k3_resume(struct device *dev)
+{
+	struct dw_mci *host = dev_get_drvdata(dev);
+	int ret = 0;
+
+	ret = clk_prepare_enable(host->ciu_clk);
+	if (ret) {
+		dev_err(host->dev, "failed to enable ciu clock\n");
+		return ret;
+	}
+
+	return dw_mci_resume(host);
+}
+
+SIMPLE_DEV_PM_OPS(dw_mci_k3_pmops, dw_mci_k3_suspend, dw_mci_k3_resume);
+
+static struct platform_driver dw_mci_k3_pltfm_driver = {
+	.probe		= dw_mci_k3_probe,
+	.remove		= dw_mci_pltfm_remove,
+	.driver		= {
+		.name		= "dwmmc_k3",
+		.of_match_table	= dw_mci_k3_match,
+		.pm		= &dw_mci_k3_pmops,
+	},
+};
+
+module_platform_driver(dw_mci_k3_pltfm_driver);
+
+MODULE_DESCRIPTION("K3 Specific DW-MSHC Driver Extension");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:dwmmc-k3");