* [PATCH v5 0/3] mmc: dw_mmc: add dw_mmc-k3 @ 2013-12-14 2:12 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao V5: 0002: Follow advice from Arnd, Update dt descirption and use of_property_for_each_u32 to get table number. v4: Follow Arnd's suggestion abstracting specific tuning to clock, also because new version ip use different method and not use same tuning registers. 0001 acked by Jaehoon v3: 0001: Put set/clear_bit DW_MMC_CARD_PRESENT in dw_mci_get_cd, Since dw_mci_request will check DW_MMC_CARD_PRESENT before sending cmd 0002: Follow suggestion from Chris, Kumar and Seungwon Sync to latest mmc-next, which is 3.12-rc2 Remove enum dw_mci_k3_type etc v2: Follow Jaehoon's suggestion Use slot-gpio.c handle cd pin Move table out to dts other suggestion Zhangfei Gao (3): mmc: dw_mmc: use slot-gpio to handle cd pin mmc: dw_mmc: add dw_mmc-k3 for k3 platform clk: hisilicon: add hi3620_mmc_clks .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + .../devicetree/bindings/mmc/k3-dw-mshc.txt | 59 +++++ drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ drivers/mmc/host/Kconfig | 10 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-k3.c | 133 ++++++++++ drivers/mmc/host/dw_mmc.c | 48 +++- include/dt-bindings/clock/hi3620-clock.h | 5 + 9 files changed, 520 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt create mode 100644 drivers/mmc/host/dw_mmc-k3.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v5 0/3] mmc: dw_mmc: add dw_mmc-k3 @ 2013-12-14 2:12 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: linux-arm-kernel V5: 0002: Follow advice from Arnd, Update dt descirption and use of_property_for_each_u32 to get table number. v4: Follow Arnd's suggestion abstracting specific tuning to clock, also because new version ip use different method and not use same tuning registers. 0001 acked by Jaehoon v3: 0001: Put set/clear_bit DW_MMC_CARD_PRESENT in dw_mci_get_cd, Since dw_mci_request will check DW_MMC_CARD_PRESENT before sending cmd 0002: Follow suggestion from Chris, Kumar and Seungwon Sync to latest mmc-next, which is 3.12-rc2 Remove enum dw_mci_k3_type etc v2: Follow Jaehoon's suggestion Use slot-gpio.c handle cd pin Move table out to dts other suggestion Zhangfei Gao (3): mmc: dw_mmc: use slot-gpio to handle cd pin mmc: dw_mmc: add dw_mmc-k3 for k3 platform clk: hisilicon: add hi3620_mmc_clks .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + .../devicetree/bindings/mmc/k3-dw-mshc.txt | 59 +++++ drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ drivers/mmc/host/Kconfig | 10 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-k3.c | 133 ++++++++++ drivers/mmc/host/dw_mmc.c | 48 +++- include/dt-bindings/clock/hi3620-clock.h | 5 + 9 files changed, 520 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt create mode 100644 drivers/mmc/host/dw_mmc-k3.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 1/3] mmc: dw_mmc: use slot-gpio to handle cd pin 2013-12-14 2:12 ` Zhangfei Gao @ 2013-12-14 2:12 ` Zhangfei Gao -1 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao Suggested by Jaehoon: Use slot-gpio to handle cd-gpio Add function dw_mci_of_get_cd_gpio to check "cd-gpios" from dts. mmc_gpio_request_cd and mmc_gpio_get_cd are used to handle cd pin Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org> Acked-by: Jaehoon Chung <jh80.chung@samsung.com> --- drivers/mmc/host/dw_mmc.c | 48 +++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4bce0deec362..a776f24f4311 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -36,6 +36,7 @@ #include <linux/workqueue.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/mmc/slot-gpio.h> #include "dw_mmc.h" @@ -1032,20 +1033,26 @@ static int dw_mci_get_cd(struct mmc_host *mmc) int present; struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_board *brd = slot->host->pdata; + int gpio_cd = !mmc_gpio_get_cd(mmc); /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) present = 1; else if (brd->get_cd) present = !brd->get_cd(slot->id); + else if (!IS_ERR_VALUE(gpio_cd)) + present = !!gpio_cd; else present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) == 0 ? 1 : 0; - if (present) + if (present) { + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is present\n"); - else + } else { + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is not present\n"); + } return present; } @@ -1926,10 +1933,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Card change detected */ slot->last_detect_state = present; - /* Mark card as present if applicable */ - if (present != 0) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* Clean up queue if present */ mrq = slot->mrq; if (mrq) { @@ -1977,8 +1980,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Power down slot */ if (present == 0) { - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* Clear down the FIFO */ dw_mci_fifo_reset(host); #ifdef CONFIG_MMC_DW_IDMAC @@ -2079,6 +2080,26 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) return gpio; } + +/* find the cd gpio for a given slot; or -1 if none specified */ +static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, + struct mmc_host *mmc) +{ + struct device_node *np = dw_mci_of_find_slot_node(dev, slot); + int gpio; + + if (!np) + return; + + gpio = of_get_named_gpio(np, "cd-gpios", 0); + + /* Having a missing entry is valid; return silently */ + if (!gpio_is_valid(gpio)) + return; + + if (mmc_gpio_request_cd(mmc, gpio, 0)) + dev_warn(dev, "gpio [%d] request failed\n", gpio); +} #else /* CONFIG_OF */ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) { @@ -2096,6 +2117,11 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) { return -EINVAL; } +static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, + struct mmc_host *mmc) +{ + return; +} #endif /* CONFIG_OF */ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) @@ -2197,12 +2223,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) #endif /* CONFIG_MMC_DW_IDMAC */ } - if (dw_mci_get_cd(mmc)) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - else - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id); + dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc); ret = mmc_add_host(mmc); if (ret) -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 1/3] mmc: dw_mmc: use slot-gpio to handle cd pin @ 2013-12-14 2:12 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: linux-arm-kernel Suggested by Jaehoon: Use slot-gpio to handle cd-gpio Add function dw_mci_of_get_cd_gpio to check "cd-gpios" from dts. mmc_gpio_request_cd and mmc_gpio_get_cd are used to handle cd pin Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org> Acked-by: Jaehoon Chung <jh80.chung@samsung.com> --- drivers/mmc/host/dw_mmc.c | 48 +++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index 4bce0deec362..a776f24f4311 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -36,6 +36,7 @@ #include <linux/workqueue.h> #include <linux/of.h> #include <linux/of_gpio.h> +#include <linux/mmc/slot-gpio.h> #include "dw_mmc.h" @@ -1032,20 +1033,26 @@ static int dw_mci_get_cd(struct mmc_host *mmc) int present; struct dw_mci_slot *slot = mmc_priv(mmc); struct dw_mci_board *brd = slot->host->pdata; + int gpio_cd = !mmc_gpio_get_cd(mmc); /* Use platform get_cd function, else try onboard card detect */ if (brd->quirks & DW_MCI_QUIRK_BROKEN_CARD_DETECTION) present = 1; else if (brd->get_cd) present = !brd->get_cd(slot->id); + else if (!IS_ERR_VALUE(gpio_cd)) + present = !!gpio_cd; else present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) == 0 ? 1 : 0; - if (present) + if (present) { + set_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is present\n"); - else + } else { + clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); dev_dbg(&mmc->class_dev, "card is not present\n"); + } return present; } @@ -1926,10 +1933,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Card change detected */ slot->last_detect_state = present; - /* Mark card as present if applicable */ - if (present != 0) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* Clean up queue if present */ mrq = slot->mrq; if (mrq) { @@ -1977,8 +1980,6 @@ static void dw_mci_work_routine_card(struct work_struct *work) /* Power down slot */ if (present == 0) { - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - /* Clear down the FIFO */ dw_mci_fifo_reset(host); #ifdef CONFIG_MMC_DW_IDMAC @@ -2079,6 +2080,26 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) return gpio; } + +/* find the cd gpio for a given slot; or -1 if none specified */ +static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, + struct mmc_host *mmc) +{ + struct device_node *np = dw_mci_of_find_slot_node(dev, slot); + int gpio; + + if (!np) + return; + + gpio = of_get_named_gpio(np, "cd-gpios", 0); + + /* Having a missing entry is valid; return silently */ + if (!gpio_is_valid(gpio)) + return; + + if (mmc_gpio_request_cd(mmc, gpio, 0)) + dev_warn(dev, "gpio [%d] request failed\n", gpio); +} #else /* CONFIG_OF */ static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) { @@ -2096,6 +2117,11 @@ static int dw_mci_of_get_wp_gpio(struct device *dev, u8 slot) { return -EINVAL; } +static void dw_mci_of_get_cd_gpio(struct device *dev, u8 slot, + struct mmc_host *mmc) +{ + return; +} #endif /* CONFIG_OF */ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) @@ -2197,12 +2223,8 @@ static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) #endif /* CONFIG_MMC_DW_IDMAC */ } - if (dw_mci_get_cd(mmc)) - set_bit(DW_MMC_CARD_PRESENT, &slot->flags); - else - clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); - slot->wp_gpio = dw_mci_of_get_wp_gpio(host->dev, slot->id); + dw_mci_of_get_cd_gpio(host->dev, slot->id, mmc); ret = mmc_add_host(mmc); if (ret) -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-14 2:12 ` Zhangfei Gao @ 2013-12-14 2:12 ` Zhangfei Gao -1 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao, Zhigang Wang 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>; + }; + + /* 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"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-14 2:12 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: linux-arm-kernel 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 at 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 at 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 at 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"); -- 1.7.9.5 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* RE: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-14 2:12 ` Zhangfei Gao @ 2013-12-16 3:50 ` Seungwon Jeon -1 siblings, 0 replies; 40+ messages in thread From: Seungwon Jeon @ 2013-12-16 3:50 UTC (permalink / raw) To: 'Zhangfei Gao', 'Chris Ball', 'Arnd Bergmann', 'Mike Turquette', 'Rob Herring', 'Jaehoon Chung', 'Kumar Gala', 'Haojian Zhuang' Cc: linux-mmc, linux-arm-kernel, patches, devicetree, 'Zhigang Wang' 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-16 3:50 ` Seungwon Jeon 0 siblings, 0 replies; 40+ messages in thread From: Seungwon Jeon @ 2013-12-16 3:50 UTC (permalink / raw) To: linux-arm-kernel 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 at 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 at 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 at 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 at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-16 3:50 ` Seungwon Jeon @ 2013-12-16 5:05 ` zhangfei -1 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-16 5:05 UTC (permalink / raw) To: Seungwon Jeon, 'Chris Ball', 'Arnd Bergmann', 'Mike Turquette', 'Rob Herring', 'Jaehoon Chung', 'Kumar Gala', 'Haojian Zhuang' Cc: linux-mmc, linux-arm-kernel, patches, devicetree, 'Zhigang Wang' 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-16 5:05 ` zhangfei 0 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-16 5:05 UTC (permalink / raw) To: linux-arm-kernel 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 at 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-16 5:05 ` zhangfei @ 2013-12-16 7:29 ` Seungwon Jeon -1 siblings, 0 replies; 40+ messages in thread From: Seungwon Jeon @ 2013-12-16 7:29 UTC (permalink / raw) To: 'zhangfei', 'Chris Ball', 'Arnd Bergmann', 'Mike Turquette', 'Rob Herring', 'Jaehoon Chung', 'Kumar Gala', 'Haojian Zhuang' Cc: linux-mmc, linux-arm-kernel, patches, devicetree, 'Zhigang Wang' 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-16 7:29 ` Seungwon Jeon 0 siblings, 0 replies; 40+ messages in thread From: Seungwon Jeon @ 2013-12-16 7:29 UTC (permalink / raw) To: linux-arm-kernel 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 at 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-16 7:29 ` Seungwon Jeon @ 2013-12-16 8:08 ` zhangfei -1 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-16 8:08 UTC (permalink / raw) To: Seungwon Jeon, 'Chris Ball', 'Arnd Bergmann', 'Mike Turquette', 'Rob Herring', 'Jaehoon Chung', 'Kumar Gala', 'Haojian Zhuang' Cc: linux-mmc, linux-arm-kernel, patches, devicetree, 'Zhigang Wang' 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. ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-16 8:08 ` zhangfei 0 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-16 8:08 UTC (permalink / raw) To: linux-arm-kernel 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 at 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. ^ permalink raw reply [flat|nested] 40+ messages in thread
* RE: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-16 8:08 ` zhangfei @ 2013-12-16 9:18 ` Seungwon Jeon -1 siblings, 0 replies; 40+ messages in thread From: Seungwon Jeon @ 2013-12-16 9:18 UTC (permalink / raw) To: 'zhangfei', 'Chris Ball', 'Arnd Bergmann', 'Mike Turquette', 'Rob Herring', 'Jaehoon Chung', 'Kumar Gala', 'Haojian Zhuang' Cc: linux-mmc, linux-arm-kernel, patches, devicetree, 'Zhigang Wang' 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-16 9:18 ` Seungwon Jeon 0 siblings, 0 replies; 40+ messages in thread From: Seungwon Jeon @ 2013-12-16 9:18 UTC (permalink / raw) To: linux-arm-kernel 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 at 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 at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-16 9:18 ` Seungwon Jeon @ 2013-12-16 11:07 ` zhangfei -1 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-16 11:07 UTC (permalink / raw) To: Seungwon Jeon, 'Chris Ball', 'Arnd Bergmann', 'Mike Turquette', 'Rob Herring', 'Jaehoon Chung', 'Kumar Gala', 'Haojian Zhuang' Cc: linux-mmc, linux-arm-kernel, patches, devicetree, 'Zhigang Wang' 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-16 11:07 ` zhangfei 0 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-16 11:07 UTC (permalink / raw) To: linux-arm-kernel 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 at 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 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-14 2:12 ` Zhangfei Gao @ 2013-12-16 13:12 ` Zhangfei Gao -1 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-16 13:12 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao, Zhigang Wang 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 | 134 ++++++++++++++++++++ 4 files changed, 205 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>; + }; + + /* 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..e3eea2883ae7 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -0,0 +1,134 @@ +/* + * 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); + int ret = 0; + + ret = dw_mci_suspend(host); + if (!ret) + clk_disable_unprepare(host->ciu_clk); + + return ret; +} + +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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-16 13:12 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-16 13:12 UTC (permalink / raw) To: linux-arm-kernel 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 | 134 ++++++++++++++++++++ 4 files changed, 205 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 at 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 at 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 at 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..e3eea2883ae7 --- /dev/null +++ b/drivers/mmc/host/dw_mmc-k3.c @@ -0,0 +1,134 @@ +/* + * 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); + int ret = 0; + + ret = dw_mci_suspend(host); + if (!ret) + clk_disable_unprepare(host->ciu_clk); + + return ret; +} + +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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-16 13:12 ` Zhangfei Gao @ 2013-12-20 2:31 ` zhangfei -1 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-20 2:31 UTC (permalink / raw) To: Zhangfei Gao, Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhigang Wang On 12/16/2013 09:12 PM, 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 | 134 ++++++++++++++++++++ > 4 files changed, 205 insertions(+) > create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt > create mode 100644 drivers/mmc/host/dw_mmc-k3.c > Any comments? Thanks ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-20 2:31 ` zhangfei 0 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-20 2:31 UTC (permalink / raw) To: linux-arm-kernel On 12/16/2013 09:12 PM, 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 | 134 ++++++++++++++++++++ > 4 files changed, 205 insertions(+) > create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt > create mode 100644 drivers/mmc/host/dw_mmc-k3.c > Any comments? Thanks ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-16 13:12 ` Zhangfei Gao @ 2013-12-26 4:33 ` Jaehoon Chung -1 siblings, 0 replies; 40+ messages in thread From: Jaehoon Chung @ 2013-12-26 4:33 UTC (permalink / raw) To: Zhangfei Gao, Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhigang Wang, cpgs Hi, Zhangfei, On 12/16/2013 10:12 PM, 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 | 134 ++++++++++++++++++++ > 4 files changed, 205 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>; > + }; > + > + /* 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 Only use the 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..e3eea2883ae7 > --- /dev/null > +++ b/drivers/mmc/host/dw_mmc-k3.c > @@ -0,0 +1,134 @@ > +/* > + * 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, > +}; If supports-highspeed is defined at dt-file, then MMC_CAP_SD_HIGHSEEPD and MMC_CAP_MMC_HIGHSPEED should set in dw-mmc.c > + > +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); > + int ret = 0; > + > + ret = dw_mci_suspend(host); > + if (!ret) > + clk_disable_unprepare(host->ciu_clk); > + > + return ret; > +} > + > +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); > +} Clock control need in suspend/resume? ciu-clk is used into dw-mmc.c. Best Regards, Jaehoon Chung > + > +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"); > ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-26 4:33 ` Jaehoon Chung 0 siblings, 0 replies; 40+ messages in thread From: Jaehoon Chung @ 2013-12-26 4:33 UTC (permalink / raw) To: linux-arm-kernel Hi, Zhangfei, On 12/16/2013 10:12 PM, 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 | 134 ++++++++++++++++++++ > 4 files changed, 205 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 at 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 at 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 at 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 Only use the 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..e3eea2883ae7 > --- /dev/null > +++ b/drivers/mmc/host/dw_mmc-k3.c > @@ -0,0 +1,134 @@ > +/* > + * 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, > +}; If supports-highspeed is defined at dt-file, then MMC_CAP_SD_HIGHSEEPD and MMC_CAP_MMC_HIGHSPEED should set in dw-mmc.c > + > +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); > + int ret = 0; > + > + ret = dw_mci_suspend(host); > + if (!ret) > + clk_disable_unprepare(host->ciu_clk); > + > + return ret; > +} > + > +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); > +} Clock control need in suspend/resume? ciu-clk is used into dw-mmc.c. Best Regards, Jaehoon Chung > + > +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"); > ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform 2013-12-26 4:33 ` Jaehoon Chung @ 2013-12-27 6:13 ` zhangfei -1 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-27 6:13 UTC (permalink / raw) To: Jaehoon Chung, Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhigang Wang, cpgs Dear Jaehoon Thanks for the reviewing. On 12/26/2013 12:33 PM, Jaehoon Chung wrote: >> +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 > > Only use the IDMAC? IDMAC has to be set here since we found the controller have some issue in non-dma mode. >> +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, >> +}; > > If supports-highspeed is defined at dt-file, > then MMC_CAP_SD_HIGHSEEPD and MMC_CAP_MMC_HIGHSPEED should set in dw-mmc.c Yes, good catch, they can be removed. > >> + >> +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); >> + int ret = 0; >> + >> + ret = dw_mci_suspend(host); >> + if (!ret) >> + clk_disable_unprepare(host->ciu_clk); >> + >> + return ret; >> +} >> + >> +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); >> +} > > Clock control need in suspend/resume? ciu-clk is used into dw-mmc.c. The ciu-clk is required here not only for power, but also clk prepare have to be triggered in the resume operation as well as init process, otherwise sd register access will fail. Some tuning registers accessing have been abstracted to clock. Thanks ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform @ 2013-12-27 6:13 ` zhangfei 0 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-27 6:13 UTC (permalink / raw) To: linux-arm-kernel Dear Jaehoon Thanks for the reviewing. On 12/26/2013 12:33 PM, Jaehoon Chung wrote: >> +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 > > Only use the IDMAC? IDMAC has to be set here since we found the controller have some issue in non-dma mode. >> +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, >> +}; > > If supports-highspeed is defined at dt-file, > then MMC_CAP_SD_HIGHSEEPD and MMC_CAP_MMC_HIGHSPEED should set in dw-mmc.c Yes, good catch, they can be removed. > >> + >> +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); >> + int ret = 0; >> + >> + ret = dw_mci_suspend(host); >> + if (!ret) >> + clk_disable_unprepare(host->ciu_clk); >> + >> + return ret; >> +} >> + >> +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); >> +} > > Clock control need in suspend/resume? ciu-clk is used into dw-mmc.c. The ciu-clk is required here not only for power, but also clk prepare have to be triggered in the resume operation as well as init process, otherwise sd register access will fail. Some tuning registers accessing have been abstracted to clock. Thanks ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks 2013-12-14 2:12 ` Zhangfei Gao @ 2013-12-14 2:12 ` Zhangfei Gao -1 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao 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> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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..e47a4a659df7 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + 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; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks @ 2013-12-14 2:12 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-14 2:12 UTC (permalink / raw) To: linux-arm-kernel 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> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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 at 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..e47a4a659df7 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + 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; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH v7 0/3] mmc: dw_mmc: add dw_mmc-k3 @ 2014-01-09 14:35 Zhangfei Gao 2014-01-09 14:35 ` Zhangfei Gao 0 siblings, 1 reply; 40+ messages in thread From: Zhangfei Gao @ 2014-01-09 14:35 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao v7: 0002: use undefined local value in suspend/resume v6: 0002: Jaehoon pointed HIGHSPEED cap can be omitted if supports-highspeed is defined. Seungwon mentioned clk operation should be called after suspend. Remove k3_dwmmc_caps V5: 0002: Follow advice from Arnd, Update dt descirption and use of_property_for_each_u32 to get table number. v4: Follow Arnd's suggestion abstracting specific tuning to clock, also because new version ip use different method and not use same tuning registers. 0001 acked by Jaehoon v3: 0001: Put set/clear_bit DW_MMC_CARD_PRESENT in dw_mci_get_cd, Since dw_mci_request will check DW_MMC_CARD_PRESENT before sending cmd 0002: Follow suggestion from Chris, Kumar and Seungwon Sync to latest mmc-next, which is 3.12-rc2 Remove enum dw_mci_k3_type etc v2: Follow Jaehoon's suggestion Use slot-gpio.c handle cd pin Move table out to dts other suggestion Zhangfei Gao (3): mmc: dw_mmc: use slot-gpio to handle cd pin mmc: dw_mmc: add dw_mmc-k3 for k3 platform clk: hisilicon: add hi3620_mmc_clks .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + .../devicetree/bindings/mmc/k3-dw-mshc.txt | 60 +++++ drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ drivers/mmc/host/Kconfig | 10 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-k3.c | 126 ++++++++++ drivers/mmc/host/dw_mmc.c | 48 +++- include/dt-bindings/clock/hi3620-clock.h | 5 + 9 files changed, 514 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt create mode 100644 drivers/mmc/host/dw_mmc-k3.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks 2014-01-09 14:35 [PATCH v7 0/3] mmc: dw_mmc: add dw_mmc-k3 Zhangfei Gao @ 2014-01-09 14:35 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2014-01-09 14:35 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao 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> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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..e47a4a659df7 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + 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; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks @ 2014-01-09 14:35 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2014-01-09 14:35 UTC (permalink / raw) To: linux-arm-kernel 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> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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 at 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..e47a4a659df7 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + 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; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks 2014-01-09 14:35 ` Zhangfei Gao @ 2014-01-09 14:38 ` Arnd Bergmann -1 siblings, 0 replies; 40+ messages in thread From: Arnd Bergmann @ 2014-01-09 14:38 UTC (permalink / raw) To: Zhangfei Gao Cc: Chris Ball, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang, linux-mmc, linux-arm-kernel, patches, devicetree On Thursday 09 January 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> ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks @ 2014-01-09 14:38 ` Arnd Bergmann 0 siblings, 0 replies; 40+ messages in thread From: Arnd Bergmann @ 2014-01-09 14:38 UTC (permalink / raw) To: linux-arm-kernel On Thursday 09 January 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> ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH v6 0/3] mmc: dw_mmc: add dw_mmc-k3 @ 2013-12-28 14:34 Zhangfei Gao 2013-12-28 14:34 ` Zhangfei Gao 0 siblings, 1 reply; 40+ messages in thread From: Zhangfei Gao @ 2013-12-28 14:34 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao v6: 0002: Jaehoon pointed HIGHSPEED cap can be omitted if supports-highspeed is defined. Seungwon mentioned clk operation should be called after suspend. Remove k3_dwmmc_caps V5: 0002: Follow advice from Arnd, Update dt descirption and use of_property_for_each_u32 to get table number. v4: Follow Arnd's suggestion abstracting specific tuning to clock, also because new version ip use different method and not use same tuning registers. 0001 acked by Jaehoon v3: 0001: Put set/clear_bit DW_MMC_CARD_PRESENT in dw_mci_get_cd, Since dw_mci_request will check DW_MMC_CARD_PRESENT before sending cmd 0002: Follow suggestion from Chris, Kumar and Seungwon Sync to latest mmc-next, which is 3.12-rc2 Remove enum dw_mci_k3_type etc v2: Follow Jaehoon's suggestion Use slot-gpio.c handle cd pin Move table out to dts other suggestion Zhangfei Gao (3): mmc: dw_mmc: use slot-gpio to handle cd pin mmc: dw_mmc: add dw_mmc-k3 for k3 platform clk: hisilicon: add hi3620_mmc_clks .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + .../devicetree/bindings/mmc/k3-dw-mshc.txt | 60 +++++ drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ drivers/mmc/host/Kconfig | 10 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-k3.c | 126 ++++++++++ drivers/mmc/host/dw_mmc.c | 48 +++- include/dt-bindings/clock/hi3620-clock.h | 5 + 9 files changed, 514 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt create mode 100644 drivers/mmc/host/dw_mmc-k3.c -- 1.7.9.5 ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks 2013-12-28 14:34 [PATCH v6 0/3] mmc: dw_mmc: add dw_mmc-k3 Zhangfei Gao @ 2013-12-28 14:34 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-28 14:34 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao 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> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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..e47a4a659df7 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + 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; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks @ 2013-12-28 14:34 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-28 14:34 UTC (permalink / raw) To: linux-arm-kernel 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> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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 at 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..e47a4a659df7 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + 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; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH v4 0/3] mmc: dw_mmc: add dw_mmc-k3 @ 2013-12-11 14:02 Zhangfei Gao 2013-12-11 14:02 ` Zhangfei Gao 0 siblings, 1 reply; 40+ messages in thread From: Zhangfei Gao @ 2013-12-11 14:02 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, patches-QSEj5FYQhm4dnm+yROfE0A, devicetree-u79uwXL29TY76Z2rM5mHXA, Zhangfei Gao v4: Follow Arnd's suggestion abstracting specific tuning to clock, also because new version ip use different method and not use same tuning registers. 0001 acked by Jaehoon v3: 0001: Put set/clear_bit DW_MMC_CARD_PRESENT in dw_mci_get_cd, Since dw_mci_request will check DW_MMC_CARD_PRESENT before sending cmd 0002: Follow suggestion from Chris, Kumar and Seungwon Sync to latest mmc-next, which is 3.12-rc2 Remove enum dw_mci_k3_type etc v2: Follow Jaehoon's suggestion Use slot-gpio.c handle cd pin Move table out to dts other suggestion Zhangfei Gao (3): mmc: dw_mmc: use slot-gpio to handle cd pin mmc: dw_mmc: add dw_mmc-k3 for k3 platform clk: hisilicon: add hi3620_mmc_clks .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + .../devicetree/bindings/mmc/k3-dw-mshc.txt | 51 ++++ drivers/clk/hisilicon/clk-hi3620.c | 266 ++++++++++++++++++++ drivers/mmc/host/Kconfig | 10 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/dw_mmc-k3.c | 136 ++++++++++ drivers/mmc/host/dw_mmc.c | 48 +++- include/dt-bindings/clock/hi3620-clock.h | 5 + 9 files changed, 519 insertions(+), 13 deletions(-) create mode 100644 Documentation/devicetree/bindings/mmc/k3-dw-mshc.txt create mode 100644 drivers/mmc/host/dw_mmc-k3.c -- 1.7.9.5 -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org More majordomo info at http://vger.kernel.org/majordomo-info.html ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks 2013-12-11 14:02 [PATCH v4 0/3] mmc: dw_mmc: add dw_mmc-k3 Zhangfei Gao @ 2013-12-11 14:02 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-11 14:02 UTC (permalink / raw) To: Chris Ball, Arnd Bergmann, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang Cc: linux-mmc, linux-arm-kernel, patches, devicetree, Zhangfei Gao hi3620_mmc_clks is added to handle mmc clock specifically on hi3620 Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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..eeb89b7a3507 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + rate = 13000000; + best = 26000000; + } else if (rate <= 25000000) { + rate = 25000000; + best = 180000000; + } else if (rate <= 50000000) { + rate = 50000000; + best = 360000000; + } else if (rate <= 100000000) { + rate = 100000000; + best = 720000000; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks @ 2013-12-11 14:02 ` Zhangfei Gao 0 siblings, 0 replies; 40+ messages in thread From: Zhangfei Gao @ 2013-12-11 14:02 UTC (permalink / raw) To: linux-arm-kernel hi3620_mmc_clks is added to handle mmc clock specifically on hi3620 Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org> --- .../bindings/arm/hisilicon/hisilicon.txt | 14 ++ .../devicetree/bindings/clock/hi3620-clock.txt | 1 + drivers/clk/hisilicon/clk-hi3620.c | 262 ++++++++++++++++++++ include/dt-bindings/clock/hi3620-clock.h | 5 + 4 files changed, 282 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 at 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..eeb89b7a3507 100644 --- a/drivers/clk/hisilicon/clk-hi3620.c +++ b/drivers/clk/hisilicon/clk-hi3620.c @@ -240,3 +240,265 @@ 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) +{ + unsigned long best = 0; + + if (rate <= 13000000) { + rate = 13000000; + best = 26000000; + } else if (rate <= 25000000) { + rate = 25000000; + best = 180000000; + } else if (rate <= 50000000) { + rate = 50000000; + best = 360000000; + } else if (rate <= 100000000) { + rate = 100000000; + best = 720000000; + } + *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_SD_CIUCLK) + 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 ^ permalink raw reply related [flat|nested] 40+ messages in thread
* Re: [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks 2013-12-11 14:02 ` Zhangfei Gao @ 2013-12-11 14:44 ` Arnd Bergmann -1 siblings, 0 replies; 40+ messages in thread From: Arnd Bergmann @ 2013-12-11 14:44 UTC (permalink / raw) To: Zhangfei Gao Cc: devicetree, Mike Turquette, patches, Seungwon Jeon, linux-mmc, Jaehoon Chung, Haojian Zhuang, Kumar Gala, Chris Ball, linux-arm-kernel On Wednesday 11 December 2013, Zhangfei Gao wrote: > +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>; > + }; It seems you are missing the clock specific parts of the binding: You should document the required value of #clock-cells as well as the possible values for the clock specifier. Arnd ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks @ 2013-12-11 14:44 ` Arnd Bergmann 0 siblings, 0 replies; 40+ messages in thread From: Arnd Bergmann @ 2013-12-11 14:44 UTC (permalink / raw) To: linux-arm-kernel On Wednesday 11 December 2013, Zhangfei Gao wrote: > +PCTRL: Peripheral misc control register > + > +Required Properties: > +- compatible: "hisilicon,pctrl" > +- reg: Address and size of pctrl. > + > +Example: > + > + /* for Hi3620 */ > + pctrl: pctrl at fca09000 { > + compatible = "hisilicon,pctrl"; > + reg = <0xfca09000 0x1000>; > + }; It seems you are missing the clock specific parts of the binding: You should document the required value of #clock-cells as well as the possible values for the clock specifier. Arnd ^ permalink raw reply [flat|nested] 40+ messages in thread
* Re: [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks 2013-12-11 14:44 ` Arnd Bergmann @ 2013-12-11 15:40 ` zhangfei -1 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-11 15:40 UTC (permalink / raw) To: Arnd Bergmann Cc: Chris Ball, Mike Turquette, Rob Herring, Jaehoon Chung, Seungwon Jeon, Kumar Gala, Haojian Zhuang, linux-mmc, linux-arm-kernel, patches, devicetree On 12/11/2013 10:44 PM, Arnd Bergmann wrote: > On Wednesday 11 December 2013, Zhangfei Gao wrote: >> +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>; >> + }; > > It seems you are missing the clock specific parts of the binding: > You should document the required value of #clock-cells as well as > the possible values for the clock specifier. > It is reuse Documentation/devicetree/bindings/clock/hi3620-clock.txt via adding hisilicon,hi3620-mmc-clock" - 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. - #clock-cells: should be 1. Thanks ^ permalink raw reply [flat|nested] 40+ messages in thread
* [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks @ 2013-12-11 15:40 ` zhangfei 0 siblings, 0 replies; 40+ messages in thread From: zhangfei @ 2013-12-11 15:40 UTC (permalink / raw) To: linux-arm-kernel On 12/11/2013 10:44 PM, Arnd Bergmann wrote: > On Wednesday 11 December 2013, Zhangfei Gao wrote: >> +PCTRL: Peripheral misc control register >> + >> +Required Properties: >> +- compatible: "hisilicon,pctrl" >> +- reg: Address and size of pctrl. >> + >> +Example: >> + >> + /* for Hi3620 */ >> + pctrl: pctrl at fca09000 { >> + compatible = "hisilicon,pctrl"; >> + reg = <0xfca09000 0x1000>; >> + }; > > It seems you are missing the clock specific parts of the binding: > You should document the required value of #clock-cells as well as > the possible values for the clock specifier. > It is reuse Documentation/devicetree/bindings/clock/hi3620-clock.txt via adding hisilicon,hi3620-mmc-clock" - 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. - #clock-cells: should be 1. Thanks ^ permalink raw reply [flat|nested] 40+ messages in thread
end of thread, other threads:[~2014-01-09 14:38 UTC | newest] Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2013-12-14 2:12 [PATCH v5 0/3] mmc: dw_mmc: add dw_mmc-k3 Zhangfei Gao 2013-12-14 2:12 ` Zhangfei Gao 2013-12-14 2:12 ` [PATCH 1/3] mmc: dw_mmc: use slot-gpio to handle cd pin Zhangfei Gao 2013-12-14 2:12 ` Zhangfei Gao 2013-12-14 2:12 ` [PATCH 2/3] mmc: dw_mmc: add dw_mmc-k3 for k3 platform Zhangfei Gao 2013-12-14 2:12 ` Zhangfei Gao 2013-12-16 3:50 ` Seungwon Jeon 2013-12-16 3:50 ` Seungwon Jeon 2013-12-16 5:05 ` zhangfei 2013-12-16 5:05 ` zhangfei 2013-12-16 7:29 ` Seungwon Jeon 2013-12-16 7:29 ` Seungwon Jeon 2013-12-16 8:08 ` zhangfei 2013-12-16 8:08 ` zhangfei 2013-12-16 9:18 ` Seungwon Jeon 2013-12-16 9:18 ` Seungwon Jeon 2013-12-16 11:07 ` zhangfei 2013-12-16 11:07 ` zhangfei 2013-12-16 13:12 ` Zhangfei Gao 2013-12-16 13:12 ` Zhangfei Gao 2013-12-20 2:31 ` zhangfei 2013-12-20 2:31 ` zhangfei 2013-12-26 4:33 ` Jaehoon Chung 2013-12-26 4:33 ` Jaehoon Chung 2013-12-27 6:13 ` zhangfei 2013-12-27 6:13 ` zhangfei 2013-12-14 2:12 ` [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks Zhangfei Gao 2013-12-14 2:12 ` Zhangfei Gao -- strict thread matches above, loose matches on Subject: below -- 2014-01-09 14:35 [PATCH v7 0/3] mmc: dw_mmc: add dw_mmc-k3 Zhangfei Gao 2014-01-09 14:35 ` [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks Zhangfei Gao 2014-01-09 14:35 ` Zhangfei Gao 2014-01-09 14:38 ` Arnd Bergmann 2014-01-09 14:38 ` Arnd Bergmann 2013-12-28 14:34 [PATCH v6 0/3] mmc: dw_mmc: add dw_mmc-k3 Zhangfei Gao 2013-12-28 14:34 ` [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks Zhangfei Gao 2013-12-28 14:34 ` Zhangfei Gao 2013-12-11 14:02 [PATCH v4 0/3] mmc: dw_mmc: add dw_mmc-k3 Zhangfei Gao 2013-12-11 14:02 ` [PATCH 3/3] clk: hisilicon: add hi3620_mmc_clks Zhangfei Gao 2013-12-11 14:02 ` Zhangfei Gao 2013-12-11 14:44 ` Arnd Bergmann 2013-12-11 14:44 ` Arnd Bergmann 2013-12-11 15:40 ` zhangfei 2013-12-11 15:40 ` zhangfei
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.