* Re: [PATCH v2] mmc: s3cmci: Use DMA slave map rather than exported DMA filter
From: Arnd Bergmann @ 2016-10-26 20:10 UTC (permalink / raw)
To: Sylwester Nawrocki
Cc: ulf.hansson, linux-samsung-soc, sam.van.den.berge, linux-mmc,
b.zolnierkie
In-Reply-To: <1477499453-22549-1-git-send-email-s.nawrocki@samsung.com>
On Wednesday, October 26, 2016 6:30:53 PM CEST Sylwester Nawrocki wrote:
> Support for DMA slave map has been added to the s3c24xx-dma
> controller in commit 34681d84a0f7cc22ded1413dc79eef8a2f23d9c3
> "dmaengine: s3c24xx: Add dma_slave_map for s3c2440 devices"
> This patch converts the s3cmci driver to also use it, so we can
> eventually get rid of the exported filter function once all
> related DMA clients are updated.
>
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
>
Reviewed-by: Arnd Bergmann <arnd@arndb.de>
^ permalink raw reply
* Re: [RFC 1/2] mmc: sdhci: dt: Add device tree property sdhci-cap-speed-modes-broken
From: Rob Herring @ 2016-10-26 21:03 UTC (permalink / raw)
To: Zach Brown
Cc: ulf.hansson, adrian.hunter, mark.rutland, linux-mmc, devicetree,
linux-kernel
In-Reply-To: <1476824736-9337-2-git-send-email-zach.brown@ni.com>
On Tue, Oct 18, 2016 at 04:05:35PM -0500, Zach Brown wrote:
> On some systems the sdhci capabilty registers are incorrect for one
> reason or another.
>
> The sdhci-cap-speed-modes-broken property will let the driver know that
> the sdhci capability registers should not be relied on for speed modes.
> Instead the driver should check the mmc generic DT bindings.
>
> Signed-off-by: Zach Brown <zach.brown@ni.com>
> ---
> Documentation/devicetree/bindings/mmc/mmc.txt | 3 +++
> 1 file changed, 3 insertions(+)
Acked-by: Rob Herring <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH v6 2/2] Documentation: DT: MMC: meson-gx: new bindings doc
From: Rob Herring @ 2016-10-26 21:46 UTC (permalink / raw)
To: Kevin Hilman
Cc: Ulf Hansson, linux-mmc-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-amlogic-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
In-Reply-To: <20161019181825.2186-2-khilman-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
On Wed, Oct 19, 2016 at 11:18:25AM -0700, Kevin Hilman wrote:
> Signed-off-by: Kevin Hilman <khilman-rdvid1DuHRBWk0Htik3J/w@public.gmane.org>
> ---
> .../devicetree/bindings/mmc/amlogic,meson-gx.txt | 33 ++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
--
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
* Re: [RFC v2 2/2] mmc: sdhci: Ignore capability register when it comes to speeds and use DT binding instead when sdhci-cap-speed-modes-broken is set.
From: Zach Brown @ 2016-10-26 22:28 UTC (permalink / raw)
To: Adrian Hunter
Cc: ulf.hansson, robh+dt, mark.rutland, linux-mmc, devicetree,
linux-kernel
In-Reply-To: <d65f018e-b20e-9e87-fc07-eb2fb4b5079e@intel.com>
On Tue, Oct 25, 2016 at 02:30:25PM +0300, Adrian Hunter wrote:
> On 24/10/16 18:48, Zach Brown wrote:
> > On Mon, Oct 24, 2016 at 10:34:46AM +0300, Adrian Hunter wrote:
> >> On 22/10/16 00:35, Zach Brown wrote:
> >>> When the sdhci-cap-speed-modes-broken DT property is set, the driver
> >>> will ignore the bits of the capability registers that correspond to
> >>> speed modes.
> >>>
> >>> Signed-off-by: Zach Brown <zach.brown@ni.com>
> >>> ---
> >>> drivers/mmc/host/sdhci.c | 10 ++++++++++
> >>> 1 file changed, 10 insertions(+)
> >>>
> >>> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> >>> index 1e25b01..59c62d3 100644
> >>> --- a/drivers/mmc/host/sdhci.c
> >>> +++ b/drivers/mmc/host/sdhci.c
> >>> @@ -22,6 +22,7 @@
> >>> #include <linux/scatterlist.h>
> >>> #include <linux/regulator/consumer.h>
> >>> #include <linux/pm_runtime.h>
> >>> +#include <linux/of.h>
> >>>
> >>> #include <linux/leds.h>
> >>>
> >>> @@ -3013,10 +3014,19 @@ void __sdhci_read_caps(struct sdhci_host *host, u16 *ver, u32 *caps, u32 *caps1)
> >>>
> >>> host->caps = caps ? *caps : sdhci_readl(host, SDHCI_CAPABILITIES);
> >>>
> >>> + if (of_property_read_bool(mmc_dev(host->mmc)->of_node,
> >>> + "sdhci-cap-speed-modes-broken"))
> >>
> >> It rather begs the question: if you are going to do something sdhci
> >> specific, why not just read the whole of the caps register from DT?
> >>
> >
> > Throwing out the whole of the caps register seems like overkill. Also
>
> What about 2 values: one for the 64-bit caps register and one for a 64-bit
> mask of bits to override. That way you can select which bits to override
> and what to override them to.
>
I like this idea. I sent a RFC of it.
> > there are some things set by the caps that are not available in the DT.
> > For example, SDHCI_CAN_64BIT is set by the cap register and is used in
> > sdhci_setup_host to set host->flags SDHCI_USE_64_BIT_DMA.
>
> Not sure what you mean.
>
Your suggestion addresses my concern. So it's not an issue.
^ permalink raw reply
* RE: [v12, 5/8] soc: fsl: add GUTS driver for QorIQ platforms
From: Y.B. Lu @ 2016-10-27 4:34 UTC (permalink / raw)
To: Scott Wood, linux-mmc-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
ulf.hansson-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org,
Arnd Bergmann
Cc: Mark Rutland, devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Russell King, Bhupesh Sharma,
netdev-u79uwXL29TY76Z2rM5mHXA@public.gmane.org, Santosh Shilimkar,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Jochen Friedrich, X.B. Xie, M.H. Lian,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA@public.gmane.org,
Rob Herring, linux-i2c-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Claudiu Manoil, Kumar Gala, Leo Li,
linuxppc-dev-uLR06cmDAlY/bJ5BZ2RsiQ@public.gmane.org,
linux-clk-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
In-Reply-To: <1477501566.6812.9.camel-fOR+EgIDQEHk1uMJSBkQmQ@public.gmane.org>
Hi Scott,
> -----Original Message-----
> From: Scott Wood [mailto:oss@buserror.net]
> Sent: Thursday, October 27, 2016 1:06 AM
> To: Y.B. Lu; linux-mmc@vger.kernel.org; ulf.hansson@linaro.org; Arnd
> Bergmann
> Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-arm-
> kernel@lists.infradead.org; linux-kernel@vger.kernel.org; linux-
> clk@vger.kernel.org; linux-i2c@vger.kernel.org; iommu@lists.linux-
> foundation.org; netdev@vger.kernel.org; Mark Rutland; Rob Herring;
> Russell King; Jochen Friedrich; Joerg Roedel; Claudiu Manoil; Bhupesh
> Sharma; Qiang Zhao; Kumar Gala; Santosh Shilimkar; Leo Li; X.B. Xie; M.H.
> Lian
> Subject: Re: [v12, 5/8] soc: fsl: add GUTS driver for QorIQ platforms
>
> On Wed, 2016-09-21 at 14:57 +0800, Yangbo Lu wrote:
> > diff --git a/drivers/soc/fsl/Kconfig b/drivers/soc/fsl/Kconfig new
> > file mode 100644 index 0000000..b99764c
> > --- /dev/null
> > +++ b/drivers/soc/fsl/Kconfig
> > @@ -0,0 +1,19 @@
> > +#
> > +# Freescale SOC drivers
> > +#
> > +
> > +source "drivers/soc/fsl/qe/Kconfig"
> > +
> > +config FSL_GUTS
> > + bool "Freescale QorIQ GUTS driver"
> > + select SOC_BUS
> > + help
> > + The global utilities block controls power management, I/O device
> > + enabling, power-onreset(POR) configuration monitoring, alternate
> > + function selection for multiplexed signals,and clock control.
> > + This driver is to manage and access global utilities block.
> > + Initially only reading SVR and registering soc device are
> > supported.
> > + Other guts accesses, such as reading RCW, should eventually be
> > moved
> > + into this driver as well.
> > +
> > + If you want GUTS driver support, you should say Y here.
>
> This is user-enablable without dependencies, which means it will break
> some randconfigs. If this is to be enabled via select then remove the
> text after "bool".
[Lu Yangbo-B47093] Will enable it via select and remove text after 'bool'.
>
> > +/* SoC die attribute definition for QorIQ platform */ static const
> > +struct fsl_soc_die_attr fsl_soc_die[] = { #ifdef CONFIG_PPC
> > + /*
> > + * Power Architecture-based SoCs T Series
> > + */
> > +
> > + /* Die: T4240, SoC: T4240/T4160/T4080 */
> > + { .die = "T4240",
> > + .svr = 0x82400000,
> > + .mask = 0xfff00000,
> > + },
> > + /* Die: T1040, SoC: T1040/T1020/T1042/T1022 */
> > + { .die = "T1040",
> > + .svr = 0x85200000,
> > + .mask = 0xfff00000,
> > + },
> > + /* Die: T2080, SoC: T2080/T2081 */
> > + { .die = "T2080",
> > + .svr = 0x85300000,
> > + .mask = 0xfff00000,
> > + },
> > + /* Die: T1024, SoC: T1024/T1014/T1023/T1013 */
> > + { .die = "T1024",
> > + .svr = 0x85400000,
> > + .mask = 0xfff00000,
> > + },
> > +#endif /* CONFIG_PPC */
> > +#if defined(CONFIG_ARCH_MXC) || defined(CONFIG_ARCH_LAYERSCAPE)
>
> Will this driver ever be probed on MXC? Why do we need these ifdefs at
> all?
[Lu Yangbo-B47093] Will remove them. In the previous version, we use too many members for soc definition, so I add #ifdef for ARCH.
CONFIG_ARCH_MXC was for ls1021a.
>
>
> > + /*
> > + * ARM-based SoCs LS Series
> > + */
> > +
> > + /* Die: LS1043A, SoC: LS1043A/LS1023A */
> > + { .die = "LS1043A",
> > + .svr = 0x87920000,
> > + .mask = 0xffff0000,
> > + },
> > + /* Die: LS2080A, SoC: LS2080A/LS2040A/LS2085A */
> > + { .die = "LS2080A",
> > + .svr = 0x87010000,
> > + .mask = 0xff3f0000,
> > + },
> > + /* Die: LS1088A, SoC: LS1088A/LS1048A/LS1084A/LS1044A */
> > + { .die = "LS1088A",
> > + .svr = 0x87030000,
> > + .mask = 0xff3f0000,
> > + },
> > + /* Die: LS1012A, SoC: LS1012A */
> > + { .die = "LS1012A",
> > + .svr = 0x87040000,
> > + .mask = 0xffff0000,
> > + },
> > + /* Die: LS1046A, SoC: LS1046A/LS1026A */
> > + { .die = "LS1046A",
> > + .svr = 0x87070000,
> > + .mask = 0xffff0000,
> > + },
> > + /* Die: LS2088A, SoC: LS2088A/LS2048A/LS2084A/LS2044A */
> > + { .die = "LS2088A",
> > + .svr = 0x87090000,
> > + .mask = 0xff3f0000,
> > + },
> > + /* Die: LS1021A, SoC: LS1021A/LS1020A/LS1022A
> > + * Note: Put this die at the end in cause of incorrect
> > identification
> > + */
> > + { .die = "LS1021A",
> > + .svr = 0x87000000,
> > + .mask = 0xfff00000,
> > + },
> > +#endif /* CONFIG_ARCH_MXC || CONFIG_ARCH_LAYERSCAPE */
>
> Instead of relying on ordering, add more bits to the mask so that there's
> no overlap. I think 0xfff70000 would work.
[Lu Yangbo-B47093] Ok, Will do that. Then we add 3 bits of 'Various Personalities' field for ls1021a die identification.
>
> > +out:
> > + kfree(soc_dev_attr.machine);
> > + kfree(soc_dev_attr.family);
> > + kfree(soc_dev_attr.soc_id);
> > + kfree(soc_dev_attr.revision);
> > + iounmap(guts->regs);
> > +out_free:
> > + kfree(guts);
> > + return ret;
> > +}
>
> Please use devm.
[Lu Yangbo-B47093] Sorry for forgetting this. Will do that and send out the new version soon.
Thanks for your comments.
>
> -Scott
_______________________________________________
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu
^ permalink raw reply
* Re: [PATCH -next] mmc: sdhci-msm: Fix error return code in sdhci_msm_probe()
From: Adrian Hunter @ 2016-10-27 6:24 UTC (permalink / raw)
To: Wei Yongjun, Ulf Hansson, Georgi Djakov; +Cc: Wei Yongjun, linux-mmc
In-Reply-To: <1477494281-24246-1-git-send-email-weiyj.lk@gmail.com>
On 26/10/16 18:04, Wei Yongjun wrote:
> From: Wei Yongjun <weiyongjun1@huawei.com>
>
> Fix to return a negative error code from the platform_get_irq_byname()
> error handling case instead of 0, as done elsewhere in this function.
>
> Fixes: ad81d3871004 ("mmc: sdhci-msm: Add support for UHS cards")
> Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
> drivers/mmc/host/sdhci-msm.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
> index 795f16f..b78d72f 100644
> --- a/drivers/mmc/host/sdhci-msm.c
> +++ b/drivers/mmc/host/sdhci-msm.c
> @@ -649,6 +649,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
> if (msm_host->pwr_irq < 0) {
> dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n",
> msm_host->pwr_irq);
> + ret = msm_host->pwr_irq;
> goto clk_disable;
> }
>
>
^ permalink raw reply
* Re: [PATCH -next] mmc: sdhci-msm: Fix error return code in sdhci_msm_probe()
From: Georgi Djakov @ 2016-10-27 7:20 UTC (permalink / raw)
To: Wei Yongjun, Adrian Hunter, Ulf Hansson; +Cc: Wei Yongjun, linux-mmc
In-Reply-To: <1477494281-24246-1-git-send-email-weiyj.lk@gmail.com>
On 10/26/2016 06:04 PM, Wei Yongjun wrote:
> From: Wei Yongjun <weiyongjun1@huawei.com>
>
> Fix to return a negative error code from the platform_get_irq_byname()
> error handling case instead of 0, as done elsewhere in this function.
>
> Fixes: ad81d3871004 ("mmc: sdhci-msm: Add support for UHS cards")
> Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Acked-by: Georgi Djakov <georgi.djakov@linaro.org>
^ permalink raw reply
* Re: [PATCH v6 1/2] MMC: meson: initial support for GX platforms
From: Ulf Hansson @ 2016-10-27 7:47 UTC (permalink / raw)
To: Kevin Hilman
Cc: devicetree@vger.kernel.org, Rob Herring, linux-mmc,
linux-arm-kernel@lists.infradead.org, linux-amlogic
In-Reply-To: <20161019181825.2186-1-khilman@baylibre.com>
On 19 October 2016 at 20:18, Kevin Hilman <khilman@baylibre.com> wrote:
> Initial support for the SD/eMMC controller in the Amlogic S905/GX*
> family of SoCs.
>
> Signed-off-by: Kevin Hilman <khilman@baylibre.com>
Thanks, applied for next!
Kind regards
Uffe
> ---
> Changes since v5:
> - dropped MODULE_ALIAS
> - renmaed file to meson-gx-mmc (for more useful module name)
> - update DRIVER_NAME: s/gxbb/gx/
>
> MAINTAINERS | 1 +
> drivers/mmc/host/Kconfig | 10 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/meson-gx-mmc.c | 852 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 864 insertions(+)
> create mode 100644 drivers/mmc/host/meson-gx-mmc.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 1cd38a7e0064..73e8d64ec28c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -1036,6 +1036,7 @@ F: arch/arm/mach-meson/
> F: arch/arm/boot/dts/meson*
> F: arch/arm64/boot/dts/amlogic/
> F: drivers/pinctrl/meson/
> +F: drivers/mmc/host/meson*
> N: meson
>
> ARM/Annapurna Labs ALPINE ARCHITECTURE
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 5274f503a39a..5cf7ebaf1e8b 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -322,6 +322,16 @@ config MMC_SDHCI_IPROC
>
> If unsure, say N.
>
> +config MMC_MESON_GX
> + tristate "Amlogic S905/GX* SD/MMC Host Controller support"
> + depends on ARCH_MESON && MMC
> + help
> + This selects support for the Amlogic SD/MMC Host Controller
> + found on the S905/GX* family of SoCs. This controller is
> + MMC 5.1 compliant and supports SD, eMMC and SDIO interfaces.
> +
> + If you have a controller with this interface, say Y here.
> +
> config MMC_MOXART
> tristate "MOXART SD/MMC Host Controller support"
> depends on ARCH_MOXART && MMC
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index e2bdaaf43184..e609bf04346b 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -53,6 +53,7 @@ obj-$(CONFIG_MMC_JZ4740) += jz4740_mmc.o
> obj-$(CONFIG_MMC_VUB300) += vub300.o
> obj-$(CONFIG_MMC_USHC) += ushc.o
> obj-$(CONFIG_MMC_WMT) += wmt-sdmmc.o
> +obj-$(CONFIG_MMC_MESON_GX) += meson-gx-mmc.o
> obj-$(CONFIG_MMC_MOXART) += moxart-mmc.o
> obj-$(CONFIG_MMC_SUNXI) += sunxi-mmc.o
> obj-$(CONFIG_MMC_USDHI6ROL0) += usdhi6rol0.o
> diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c
> new file mode 100644
> index 000000000000..1fcab7116f26
> --- /dev/null
> +++ b/drivers/mmc/host/meson-gx-mmc.c
> @@ -0,0 +1,852 @@
> +/*
> + * Amlogic SD/eMMC driver for the GX/S905 family SoCs
> + *
> + * Copyright (c) 2016 BayLibre, SAS.
> + * Author: Kevin Hilman <khilman@baylibre.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of version 2 of the GNU General Public License as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + * The full GNU General Public License is included in this distribution
> + * in the file called COPYING.
> + */
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/ioport.h>
> +#include <linux/spinlock.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/mmc.h>
> +#include <linux/mmc/sdio.h>
> +#include <linux/mmc/slot-gpio.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/regulator/consumer.h>
> +
> +#define DRIVER_NAME "meson-gx-mmc"
> +
> +#define SD_EMMC_CLOCK 0x0
> +#define CLK_DIV_SHIFT 0
> +#define CLK_DIV_WIDTH 6
> +#define CLK_DIV_MASK 0x3f
> +#define CLK_DIV_MAX 63
> +#define CLK_SRC_SHIFT 6
> +#define CLK_SRC_WIDTH 2
> +#define CLK_SRC_MASK 0x3
> +#define CLK_SRC_XTAL 0 /* external crystal */
> +#define CLK_SRC_XTAL_RATE 24000000
> +#define CLK_SRC_PLL 1 /* FCLK_DIV2 */
> +#define CLK_SRC_PLL_RATE 1000000000
> +#define CLK_PHASE_SHIFT 8
> +#define CLK_PHASE_MASK 0x3
> +#define CLK_PHASE_0 0
> +#define CLK_PHASE_90 1
> +#define CLK_PHASE_180 2
> +#define CLK_PHASE_270 3
> +#define CLK_ALWAYS_ON BIT(24)
> +
> +#define SD_EMMC_DElAY 0x4
> +#define SD_EMMC_ADJUST 0x8
> +#define SD_EMMC_CALOUT 0x10
> +#define SD_EMMC_START 0x40
> +#define START_DESC_INIT BIT(0)
> +#define START_DESC_BUSY BIT(1)
> +#define START_DESC_ADDR_SHIFT 2
> +#define START_DESC_ADDR_MASK (~0x3)
> +
> +#define SD_EMMC_CFG 0x44
> +#define CFG_BUS_WIDTH_SHIFT 0
> +#define CFG_BUS_WIDTH_MASK 0x3
> +#define CFG_BUS_WIDTH_1 0x0
> +#define CFG_BUS_WIDTH_4 0x1
> +#define CFG_BUS_WIDTH_8 0x2
> +#define CFG_DDR BIT(2)
> +#define CFG_BLK_LEN_SHIFT 4
> +#define CFG_BLK_LEN_MASK 0xf
> +#define CFG_RESP_TIMEOUT_SHIFT 8
> +#define CFG_RESP_TIMEOUT_MASK 0xf
> +#define CFG_RC_CC_SHIFT 12
> +#define CFG_RC_CC_MASK 0xf
> +#define CFG_STOP_CLOCK BIT(22)
> +#define CFG_CLK_ALWAYS_ON BIT(18)
> +#define CFG_AUTO_CLK BIT(23)
> +
> +#define SD_EMMC_STATUS 0x48
> +#define STATUS_BUSY BIT(31)
> +
> +#define SD_EMMC_IRQ_EN 0x4c
> +#define IRQ_EN_MASK 0x3fff
> +#define IRQ_RXD_ERR_SHIFT 0
> +#define IRQ_RXD_ERR_MASK 0xff
> +#define IRQ_TXD_ERR BIT(8)
> +#define IRQ_DESC_ERR BIT(9)
> +#define IRQ_RESP_ERR BIT(10)
> +#define IRQ_RESP_TIMEOUT BIT(11)
> +#define IRQ_DESC_TIMEOUT BIT(12)
> +#define IRQ_END_OF_CHAIN BIT(13)
> +#define IRQ_RESP_STATUS BIT(14)
> +#define IRQ_SDIO BIT(15)
> +
> +#define SD_EMMC_CMD_CFG 0x50
> +#define SD_EMMC_CMD_ARG 0x54
> +#define SD_EMMC_CMD_DAT 0x58
> +#define SD_EMMC_CMD_RSP 0x5c
> +#define SD_EMMC_CMD_RSP1 0x60
> +#define SD_EMMC_CMD_RSP2 0x64
> +#define SD_EMMC_CMD_RSP3 0x68
> +
> +#define SD_EMMC_RXD 0x94
> +#define SD_EMMC_TXD 0x94
> +#define SD_EMMC_LAST_REG SD_EMMC_TXD
> +
> +#define SD_EMMC_CFG_BLK_SIZE 512 /* internal buffer max: 512 bytes */
> +#define SD_EMMC_CFG_RESP_TIMEOUT 256 /* in clock cycles */
> +#define SD_EMMC_CFG_CMD_GAP 16 /* in clock cycles */
> +#define MUX_CLK_NUM_PARENTS 2
> +
> +struct meson_host {
> + struct device *dev;
> + struct mmc_host *mmc;
> + struct mmc_request *mrq;
> + struct mmc_command *cmd;
> +
> + spinlock_t lock;
> + void __iomem *regs;
> + int irq;
> + u32 ocr_mask;
> + struct clk *core_clk;
> + struct clk_mux mux;
> + struct clk *mux_clk;
> + struct clk *mux_parent[MUX_CLK_NUM_PARENTS];
> + unsigned long mux_parent_rate[MUX_CLK_NUM_PARENTS];
> +
> + struct clk_divider cfg_div;
> + struct clk *cfg_div_clk;
> +
> + unsigned int bounce_buf_size;
> + void *bounce_buf;
> + dma_addr_t bounce_dma_addr;
> +
> + bool vqmmc_enabled;
> +};
> +
> +struct sd_emmc_desc {
> + u32 cmd_cfg;
> + u32 cmd_arg;
> + u32 cmd_data;
> + u32 cmd_resp;
> +};
> +#define CMD_CFG_LENGTH_SHIFT 0
> +#define CMD_CFG_LENGTH_MASK 0x1ff
> +#define CMD_CFG_BLOCK_MODE BIT(9)
> +#define CMD_CFG_R1B BIT(10)
> +#define CMD_CFG_END_OF_CHAIN BIT(11)
> +#define CMD_CFG_TIMEOUT_SHIFT 12
> +#define CMD_CFG_TIMEOUT_MASK 0xf
> +#define CMD_CFG_NO_RESP BIT(16)
> +#define CMD_CFG_NO_CMD BIT(17)
> +#define CMD_CFG_DATA_IO BIT(18)
> +#define CMD_CFG_DATA_WR BIT(19)
> +#define CMD_CFG_RESP_NOCRC BIT(20)
> +#define CMD_CFG_RESP_128 BIT(21)
> +#define CMD_CFG_RESP_NUM BIT(22)
> +#define CMD_CFG_DATA_NUM BIT(23)
> +#define CMD_CFG_CMD_INDEX_SHIFT 24
> +#define CMD_CFG_CMD_INDEX_MASK 0x3f
> +#define CMD_CFG_ERROR BIT(30)
> +#define CMD_CFG_OWNER BIT(31)
> +
> +#define CMD_DATA_MASK (~0x3)
> +#define CMD_DATA_BIG_ENDIAN BIT(1)
> +#define CMD_DATA_SRAM BIT(0)
> +#define CMD_RESP_MASK (~0x1)
> +#define CMD_RESP_SRAM BIT(0)
> +
> +static int meson_mmc_clk_set(struct meson_host *host, unsigned long clk_rate)
> +{
> + struct mmc_host *mmc = host->mmc;
> + int ret = 0;
> + u32 cfg;
> +
> + if (clk_rate) {
> + if (WARN_ON(clk_rate > mmc->f_max))
> + clk_rate = mmc->f_max;
> + else if (WARN_ON(clk_rate < mmc->f_min))
> + clk_rate = mmc->f_min;
> + }
> +
> + if (clk_rate == mmc->actual_clock)
> + return 0;
> +
> + /* stop clock */
> + cfg = readl(host->regs + SD_EMMC_CFG);
> + if (!(cfg & CFG_STOP_CLOCK)) {
> + cfg |= CFG_STOP_CLOCK;
> + writel(cfg, host->regs + SD_EMMC_CFG);
> + }
> +
> + dev_dbg(host->dev, "change clock rate %u -> %lu\n",
> + mmc->actual_clock, clk_rate);
> +
> + if (clk_rate == 0) {
> + mmc->actual_clock = 0;
> + return 0;
> + }
> +
> + ret = clk_set_rate(host->cfg_div_clk, clk_rate);
> + if (ret)
> + dev_warn(host->dev, "Unable to set cfg_div_clk to %lu. ret=%d\n",
> + clk_rate, ret);
> + else if (clk_rate && clk_rate != clk_get_rate(host->cfg_div_clk))
> + dev_warn(host->dev, "divider requested rate %lu != actual rate %lu: ret=%d\n",
> + clk_rate, clk_get_rate(host->cfg_div_clk), ret);
> + else
> + mmc->actual_clock = clk_rate;
> +
> + /* (re)start clock, if non-zero */
> + if (!ret && clk_rate) {
> + cfg = readl(host->regs + SD_EMMC_CFG);
> + cfg &= ~CFG_STOP_CLOCK;
> + writel(cfg, host->regs + SD_EMMC_CFG);
> + }
> +
> + return ret;
> +}
> +
> +/*
> + * The SD/eMMC IP block has an internal mux and divider used for
> + * generating the MMC clock. Use the clock framework to create and
> + * manage these clocks.
> + */
> +static int meson_mmc_clk_init(struct meson_host *host)
> +{
> + struct clk_init_data init;
> + char clk_name[32];
> + int i, ret = 0;
> + const char *mux_parent_names[MUX_CLK_NUM_PARENTS];
> + unsigned int mux_parent_count = 0;
> + const char *clk_div_parents[1];
> + unsigned int f_min = UINT_MAX;
> + u32 clk_reg, cfg;
> +
> + /* get the mux parents */
> + for (i = 0; i < MUX_CLK_NUM_PARENTS; i++) {
> + char name[16];
> +
> + snprintf(name, sizeof(name), "clkin%d", i);
> + host->mux_parent[i] = devm_clk_get(host->dev, name);
> + if (IS_ERR(host->mux_parent[i])) {
> + ret = PTR_ERR(host->mux_parent[i]);
> + if (PTR_ERR(host->mux_parent[i]) != -EPROBE_DEFER)
> + dev_err(host->dev, "Missing clock %s\n", name);
> + host->mux_parent[i] = NULL;
> + return ret;
> + }
> +
> + host->mux_parent_rate[i] = clk_get_rate(host->mux_parent[i]);
> + mux_parent_names[i] = __clk_get_name(host->mux_parent[i]);
> + mux_parent_count++;
> + if (host->mux_parent_rate[i] < f_min)
> + f_min = host->mux_parent_rate[i];
> + }
> +
> + /* cacluate f_min based on input clocks, and max divider value */
> + if (f_min != UINT_MAX)
> + f_min = DIV_ROUND_UP(CLK_SRC_XTAL_RATE, CLK_DIV_MAX);
> + else
> + f_min = 4000000; /* default min: 400 MHz */
> + host->mmc->f_min = f_min;
> +
> + /* create the mux */
> + snprintf(clk_name, sizeof(clk_name), "%s#mux", dev_name(host->dev));
> + init.name = clk_name;
> + init.ops = &clk_mux_ops;
> + init.flags = 0;
> + init.parent_names = mux_parent_names;
> + init.num_parents = mux_parent_count;
> +
> + host->mux.reg = host->regs + SD_EMMC_CLOCK;
> + host->mux.shift = CLK_SRC_SHIFT;
> + host->mux.mask = CLK_SRC_MASK;
> + host->mux.flags = 0;
> + host->mux.table = NULL;
> + host->mux.hw.init = &init;
> +
> + host->mux_clk = devm_clk_register(host->dev, &host->mux.hw);
> + if (WARN_ON(IS_ERR(host->mux_clk)))
> + return PTR_ERR(host->mux_clk);
> +
> + /* create the divider */
> + snprintf(clk_name, sizeof(clk_name), "%s#div", dev_name(host->dev));
> + init.name = devm_kstrdup(host->dev, clk_name, GFP_KERNEL);
> + init.ops = &clk_divider_ops;
> + init.flags = CLK_SET_RATE_PARENT;
> + clk_div_parents[0] = __clk_get_name(host->mux_clk);
> + init.parent_names = clk_div_parents;
> + init.num_parents = ARRAY_SIZE(clk_div_parents);
> +
> + host->cfg_div.reg = host->regs + SD_EMMC_CLOCK;
> + host->cfg_div.shift = CLK_DIV_SHIFT;
> + host->cfg_div.width = CLK_DIV_WIDTH;
> + host->cfg_div.hw.init = &init;
> + host->cfg_div.flags = CLK_DIVIDER_ONE_BASED |
> + CLK_DIVIDER_ROUND_CLOSEST | CLK_DIVIDER_ALLOW_ZERO;
> +
> + host->cfg_div_clk = devm_clk_register(host->dev, &host->cfg_div.hw);
> + if (WARN_ON(PTR_ERR_OR_ZERO(host->cfg_div_clk)))
> + return PTR_ERR(host->cfg_div_clk);
> +
> + /* init SD_EMMC_CLOCK to sane defaults w/min clock rate */
> + clk_reg = 0;
> + clk_reg |= CLK_PHASE_180 << CLK_PHASE_SHIFT;
> + clk_reg |= CLK_SRC_XTAL << CLK_SRC_SHIFT;
> + clk_reg |= CLK_DIV_MAX << CLK_DIV_SHIFT;
> + clk_reg &= ~CLK_ALWAYS_ON;
> + writel(clk_reg, host->regs + SD_EMMC_CLOCK);
> +
> + /* Ensure clock starts in "auto" mode, not "always on" */
> + cfg = readl(host->regs + SD_EMMC_CFG);
> + cfg &= ~CFG_CLK_ALWAYS_ON;
> + cfg |= CFG_AUTO_CLK;
> + writel(cfg, host->regs + SD_EMMC_CFG);
> +
> + ret = clk_prepare_enable(host->cfg_div_clk);
> + if (!ret)
> + ret = meson_mmc_clk_set(host, f_min);
> +
> + if (!ret)
> + clk_disable_unprepare(host->cfg_div_clk);
> +
> + return ret;
> +}
> +
> +static void meson_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
> +{
> + struct meson_host *host = mmc_priv(mmc);
> + u32 bus_width;
> + u32 val, orig;
> +
> + /*
> + * GPIO regulator, only controls switching between 1v8 and
> + * 3v3, doesn't support MMC_POWER_OFF, MMC_POWER_ON.
> + */
> + switch (ios->power_mode) {
> + case MMC_POWER_OFF:
> + if (!IS_ERR(mmc->supply.vmmc))
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> +
> + if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
> + regulator_disable(mmc->supply.vqmmc);
> + host->vqmmc_enabled = false;
> + }
> +
> + break;
> +
> + case MMC_POWER_UP:
> + if (!IS_ERR(mmc->supply.vmmc))
> + mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
> + break;
> +
> + case MMC_POWER_ON:
> + if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
> + int ret = regulator_enable(mmc->supply.vqmmc);
> +
> + if (ret < 0)
> + dev_err(mmc_dev(mmc),
> + "failed to enable vqmmc regulator\n");
> + else
> + host->vqmmc_enabled = true;
> + }
> +
> + break;
> + }
> +
> +
> + meson_mmc_clk_set(host, ios->clock);
> +
> + /* Bus width */
> + val = readl(host->regs + SD_EMMC_CFG);
> + switch (ios->bus_width) {
> + case MMC_BUS_WIDTH_1:
> + bus_width = CFG_BUS_WIDTH_1;
> + break;
> + case MMC_BUS_WIDTH_4:
> + bus_width = CFG_BUS_WIDTH_4;
> + break;
> + case MMC_BUS_WIDTH_8:
> + bus_width = CFG_BUS_WIDTH_8;
> + break;
> + default:
> + dev_err(host->dev, "Invalid ios->bus_width: %u. Setting to 4.\n",
> + ios->bus_width);
> + bus_width = CFG_BUS_WIDTH_4;
> + return;
> + }
> +
> + val = readl(host->regs + SD_EMMC_CFG);
> + orig = val;
> +
> + val &= ~(CFG_BUS_WIDTH_MASK << CFG_BUS_WIDTH_SHIFT);
> + val |= bus_width << CFG_BUS_WIDTH_SHIFT;
> +
> + val &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
> + val |= ilog2(SD_EMMC_CFG_BLK_SIZE) << CFG_BLK_LEN_SHIFT;
> +
> + val &= ~(CFG_RESP_TIMEOUT_MASK << CFG_RESP_TIMEOUT_SHIFT);
> + val |= ilog2(SD_EMMC_CFG_RESP_TIMEOUT) << CFG_RESP_TIMEOUT_SHIFT;
> +
> + val &= ~(CFG_RC_CC_MASK << CFG_RC_CC_SHIFT);
> + val |= ilog2(SD_EMMC_CFG_CMD_GAP) << CFG_RC_CC_SHIFT;
> +
> + writel(val, host->regs + SD_EMMC_CFG);
> +
> + if (val != orig)
> + dev_dbg(host->dev, "%s: SD_EMMC_CFG: 0x%08x -> 0x%08x\n",
> + __func__, orig, val);
> +}
> +
> +static int meson_mmc_request_done(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> + struct meson_host *host = mmc_priv(mmc);
> +
> + WARN_ON(host->mrq != mrq);
> +
> + host->mrq = NULL;
> + host->cmd = NULL;
> + mmc_request_done(host->mmc, mrq);
> +
> + return 0;
> +}
> +
> +static void meson_mmc_start_cmd(struct mmc_host *mmc, struct mmc_command *cmd)
> +{
> + struct meson_host *host = mmc_priv(mmc);
> + struct sd_emmc_desc *desc, desc_tmp;
> + u32 cfg;
> + u8 blk_len, cmd_cfg_timeout;
> + unsigned int xfer_bytes = 0;
> +
> + /* Setup descriptors */
> + dma_rmb();
> + desc = &desc_tmp;
> + memset(desc, 0, sizeof(struct sd_emmc_desc));
> +
> + desc->cmd_cfg |= (cmd->opcode & CMD_CFG_CMD_INDEX_MASK) <<
> + CMD_CFG_CMD_INDEX_SHIFT;
> + desc->cmd_cfg |= CMD_CFG_OWNER; /* owned by CPU */
> + desc->cmd_arg = cmd->arg;
> +
> + /* Response */
> + if (cmd->flags & MMC_RSP_PRESENT) {
> + desc->cmd_cfg &= ~CMD_CFG_NO_RESP;
> + if (cmd->flags & MMC_RSP_136)
> + desc->cmd_cfg |= CMD_CFG_RESP_128;
> + desc->cmd_cfg |= CMD_CFG_RESP_NUM;
> + desc->cmd_resp = 0;
> +
> + if (!(cmd->flags & MMC_RSP_CRC))
> + desc->cmd_cfg |= CMD_CFG_RESP_NOCRC;
> +
> + if (cmd->flags & MMC_RSP_BUSY)
> + desc->cmd_cfg |= CMD_CFG_R1B;
> + } else {
> + desc->cmd_cfg |= CMD_CFG_NO_RESP;
> + }
> +
> + /* data? */
> + if (cmd->data) {
> + desc->cmd_cfg |= CMD_CFG_DATA_IO;
> + if (cmd->data->blocks > 1) {
> + desc->cmd_cfg |= CMD_CFG_BLOCK_MODE;
> + desc->cmd_cfg |=
> + (cmd->data->blocks & CMD_CFG_LENGTH_MASK) <<
> + CMD_CFG_LENGTH_SHIFT;
> +
> + /* check if block-size matches, if not update */
> + cfg = readl(host->regs + SD_EMMC_CFG);
> + blk_len = cfg & (CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
> + blk_len >>= CFG_BLK_LEN_SHIFT;
> + if (blk_len != ilog2(cmd->data->blksz)) {
> + dev_warn(host->dev, "%s: update blk_len %d -> %d\n",
> + __func__, blk_len,
> + ilog2(cmd->data->blksz));
> + blk_len = ilog2(cmd->data->blksz);
> + cfg &= ~(CFG_BLK_LEN_MASK << CFG_BLK_LEN_SHIFT);
> + cfg |= blk_len << CFG_BLK_LEN_SHIFT;
> + writel(cfg, host->regs + SD_EMMC_CFG);
> + }
> + } else {
> + desc->cmd_cfg &= ~CMD_CFG_BLOCK_MODE;
> + desc->cmd_cfg |=
> + (cmd->data->blksz & CMD_CFG_LENGTH_MASK) <<
> + CMD_CFG_LENGTH_SHIFT;
> + }
> +
> + cmd->data->bytes_xfered = 0;
> + xfer_bytes = cmd->data->blksz * cmd->data->blocks;
> + if (cmd->data->flags & MMC_DATA_WRITE) {
> + desc->cmd_cfg |= CMD_CFG_DATA_WR;
> + WARN_ON(xfer_bytes > host->bounce_buf_size);
> + sg_copy_to_buffer(cmd->data->sg, cmd->data->sg_len,
> + host->bounce_buf, xfer_bytes);
> + cmd->data->bytes_xfered = xfer_bytes;
> + dma_wmb();
> + } else {
> + desc->cmd_cfg &= ~CMD_CFG_DATA_WR;
> + }
> +
> + if (xfer_bytes > 0) {
> + desc->cmd_cfg &= ~CMD_CFG_DATA_NUM;
> + desc->cmd_data = host->bounce_dma_addr & CMD_DATA_MASK;
> + } else {
> + /* write data to data_addr */
> + desc->cmd_cfg |= CMD_CFG_DATA_NUM;
> + desc->cmd_data = 0;
> + }
> +
> + cmd_cfg_timeout = 12;
> + } else {
> + desc->cmd_cfg &= ~CMD_CFG_DATA_IO;
> + cmd_cfg_timeout = 10;
> + }
> + desc->cmd_cfg |= (cmd_cfg_timeout & CMD_CFG_TIMEOUT_MASK) <<
> + CMD_CFG_TIMEOUT_SHIFT;
> +
> + host->cmd = cmd;
> +
> + /* Last descriptor */
> + desc->cmd_cfg |= CMD_CFG_END_OF_CHAIN;
> + writel(desc->cmd_cfg, host->regs + SD_EMMC_CMD_CFG);
> + writel(desc->cmd_data, host->regs + SD_EMMC_CMD_DAT);
> + writel(desc->cmd_resp, host->regs + SD_EMMC_CMD_RSP);
> + wmb(); /* ensure descriptor is written before kicked */
> + writel(desc->cmd_arg, host->regs + SD_EMMC_CMD_ARG);
> +}
> +
> +static void meson_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq)
> +{
> + struct meson_host *host = mmc_priv(mmc);
> +
> + WARN_ON(host->mrq != NULL);
> +
> + /* Stop execution */
> + writel(0, host->regs + SD_EMMC_START);
> +
> + /* clear, ack, enable all interrupts */
> + writel(0, host->regs + SD_EMMC_IRQ_EN);
> + writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
> + writel(IRQ_EN_MASK, host->regs + SD_EMMC_IRQ_EN);
> +
> + host->mrq = mrq;
> +
> + if (mrq->sbc)
> + meson_mmc_start_cmd(mmc, mrq->sbc);
> + else
> + meson_mmc_start_cmd(mmc, mrq->cmd);
> +}
> +
> +static int meson_mmc_read_resp(struct mmc_host *mmc, struct mmc_command *cmd)
> +{
> + struct meson_host *host = mmc_priv(mmc);
> +
> + if (cmd->flags & MMC_RSP_136) {
> + cmd->resp[0] = readl(host->regs + SD_EMMC_CMD_RSP3);
> + cmd->resp[1] = readl(host->regs + SD_EMMC_CMD_RSP2);
> + cmd->resp[2] = readl(host->regs + SD_EMMC_CMD_RSP1);
> + cmd->resp[3] = readl(host->regs + SD_EMMC_CMD_RSP);
> + } else if (cmd->flags & MMC_RSP_PRESENT) {
> + cmd->resp[0] = readl(host->regs + SD_EMMC_CMD_RSP);
> + }
> +
> + return 0;
> +}
> +
> +static irqreturn_t meson_mmc_irq(int irq, void *dev_id)
> +{
> + struct meson_host *host = dev_id;
> + struct mmc_request *mrq;
> + struct mmc_command *cmd = host->cmd;
> + u32 irq_en, status, raw_status;
> + irqreturn_t ret = IRQ_HANDLED;
> +
> + if (WARN_ON(!host))
> + return IRQ_NONE;
> +
> + mrq = host->mrq;
> +
> + if (WARN_ON(!mrq))
> + return IRQ_NONE;
> +
> + if (WARN_ON(!cmd))
> + return IRQ_NONE;
> +
> + spin_lock(&host->lock);
> + irq_en = readl(host->regs + SD_EMMC_IRQ_EN);
> + raw_status = readl(host->regs + SD_EMMC_STATUS);
> + status = raw_status & irq_en;
> +
> + if (!status) {
> + dev_warn(host->dev, "Spurious IRQ! status=0x%08x, irq_en=0x%08x\n",
> + raw_status, irq_en);
> + ret = IRQ_NONE;
> + goto out;
> + }
> +
> + cmd->error = 0;
> + if (status & IRQ_RXD_ERR_MASK) {
> + dev_dbg(host->dev, "Unhandled IRQ: RXD error\n");
> + cmd->error = -EILSEQ;
> + }
> + if (status & IRQ_TXD_ERR) {
> + dev_dbg(host->dev, "Unhandled IRQ: TXD error\n");
> + cmd->error = -EILSEQ;
> + }
> + if (status & IRQ_DESC_ERR)
> + dev_dbg(host->dev, "Unhandled IRQ: Descriptor error\n");
> + if (status & IRQ_RESP_ERR) {
> + dev_dbg(host->dev, "Unhandled IRQ: Response error\n");
> + cmd->error = -EILSEQ;
> + }
> + if (status & IRQ_RESP_TIMEOUT) {
> + dev_dbg(host->dev, "Unhandled IRQ: Response timeout\n");
> + cmd->error = -ETIMEDOUT;
> + }
> + if (status & IRQ_DESC_TIMEOUT) {
> + dev_dbg(host->dev, "Unhandled IRQ: Descriptor timeout\n");
> + cmd->error = -ETIMEDOUT;
> + }
> + if (status & IRQ_SDIO)
> + dev_dbg(host->dev, "Unhandled IRQ: SDIO.\n");
> +
> + if (status & (IRQ_END_OF_CHAIN | IRQ_RESP_STATUS))
> + ret = IRQ_WAKE_THREAD;
> + else {
> + dev_warn(host->dev, "Unknown IRQ! status=0x%04x: MMC CMD%u arg=0x%08x flags=0x%08x stop=%d\n",
> + status, cmd->opcode, cmd->arg,
> + cmd->flags, mrq->stop ? 1 : 0);
> + if (cmd->data) {
> + struct mmc_data *data = cmd->data;
> +
> + dev_warn(host->dev, "\tblksz %u blocks %u flags 0x%08x (%s%s)",
> + data->blksz, data->blocks, data->flags,
> + data->flags & MMC_DATA_WRITE ? "write" : "",
> + data->flags & MMC_DATA_READ ? "read" : "");
> + }
> + }
> +
> +out:
> + /* ack all (enabled) interrupts */
> + writel(status, host->regs + SD_EMMC_STATUS);
> +
> + if (ret == IRQ_HANDLED) {
> + meson_mmc_read_resp(host->mmc, cmd);
> + meson_mmc_request_done(host->mmc, cmd->mrq);
> + }
> +
> + spin_unlock(&host->lock);
> + return ret;
> +}
> +
> +static irqreturn_t meson_mmc_irq_thread(int irq, void *dev_id)
> +{
> + struct meson_host *host = dev_id;
> + struct mmc_request *mrq = host->mrq;
> + struct mmc_command *cmd = host->cmd;
> + struct mmc_data *data;
> + unsigned int xfer_bytes;
> + int ret = IRQ_HANDLED;
> +
> + if (WARN_ON(!mrq))
> + ret = IRQ_NONE;
> +
> + if (WARN_ON(!cmd))
> + ret = IRQ_NONE;
> +
> + data = cmd->data;
> + if (data) {
> + xfer_bytes = data->blksz * data->blocks;
> + if (data->flags & MMC_DATA_READ) {
> + WARN_ON(xfer_bytes > host->bounce_buf_size);
> + sg_copy_from_buffer(data->sg, data->sg_len,
> + host->bounce_buf, xfer_bytes);
> + data->bytes_xfered = xfer_bytes;
> + }
> + }
> +
> + meson_mmc_read_resp(host->mmc, cmd);
> + if (!data || !data->stop || mrq->sbc)
> + meson_mmc_request_done(host->mmc, mrq);
> + else
> + meson_mmc_start_cmd(host->mmc, data->stop);
> +
> + return ret;
> +}
> +
> +/*
> + * NOTE: we only need this until the GPIO/pinctrl driver can handle
> + * interrupts. For now, the MMC core will use this for polling.
> + */
> +static int meson_mmc_get_cd(struct mmc_host *mmc)
> +{
> + int status = mmc_gpio_get_cd(mmc);
> +
> + if (status == -ENOSYS)
> + return 1; /* assume present */
> +
> + return status;
> +}
> +
> +static const struct mmc_host_ops meson_mmc_ops = {
> + .request = meson_mmc_request,
> + .set_ios = meson_mmc_set_ios,
> + .get_cd = meson_mmc_get_cd,
> +};
> +
> +static int meson_mmc_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + struct meson_host *host;
> + struct mmc_host *mmc;
> + int ret;
> +
> + mmc = mmc_alloc_host(sizeof(struct meson_host), &pdev->dev);
> + if (!mmc)
> + return -ENOMEM;
> + host = mmc_priv(mmc);
> + host->mmc = mmc;
> + host->dev = &pdev->dev;
> + dev_set_drvdata(&pdev->dev, host);
> +
> + spin_lock_init(&host->lock);
> +
> + /* Get regulators and the supported OCR mask */
> + host->vqmmc_enabled = false;
> + ret = mmc_regulator_get_supply(mmc);
> + if (ret == -EPROBE_DEFER)
> + goto free_host;
> +
> + ret = mmc_of_parse(mmc);
> + if (ret) {
> + dev_warn(&pdev->dev, "error parsing DT: %d\n", ret);
> + goto free_host;
> + }
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + host->regs = devm_ioremap_resource(&pdev->dev, res);
> + if (IS_ERR(host->regs)) {
> + ret = PTR_ERR(host->regs);
> + goto free_host;
> + }
> +
> + host->irq = platform_get_irq(pdev, 0);
> + if (host->irq == 0) {
> + dev_err(&pdev->dev, "failed to get interrupt resource.\n");
> + ret = -EINVAL;
> + goto free_host;
> + }
> +
> + host->core_clk = devm_clk_get(&pdev->dev, "core");
> + if (IS_ERR(host->core_clk)) {
> + ret = PTR_ERR(host->core_clk);
> + goto free_host;
> + }
> +
> + ret = clk_prepare_enable(host->core_clk);
> + if (ret)
> + goto free_host;
> +
> + ret = meson_mmc_clk_init(host);
> + if (ret)
> + goto free_host;
> +
> + /* Stop execution */
> + writel(0, host->regs + SD_EMMC_START);
> +
> + /* clear, ack, enable all interrupts */
> + writel(0, host->regs + SD_EMMC_IRQ_EN);
> + writel(IRQ_EN_MASK, host->regs + SD_EMMC_STATUS);
> +
> + ret = devm_request_threaded_irq(&pdev->dev, host->irq,
> + meson_mmc_irq, meson_mmc_irq_thread,
> + IRQF_SHARED, DRIVER_NAME, host);
> + if (ret)
> + goto free_host;
> +
> + /* data bounce buffer */
> + host->bounce_buf_size = SZ_512K;
> + host->bounce_buf =
> + dma_alloc_coherent(host->dev, host->bounce_buf_size,
> + &host->bounce_dma_addr, GFP_KERNEL);
> + if (host->bounce_buf == NULL) {
> + dev_err(host->dev, "Unable to map allocate DMA bounce buffer.\n");
> + ret = -ENOMEM;
> + goto free_host;
> + }
> +
> + mmc->ops = &meson_mmc_ops;
> + mmc_add_host(mmc);
> +
> + return 0;
> +
> +free_host:
> + clk_disable_unprepare(host->cfg_div_clk);
> + clk_disable_unprepare(host->core_clk);
> + mmc_free_host(mmc);
> + return ret;
> +}
> +
> +static int meson_mmc_remove(struct platform_device *pdev)
> +{
> + struct meson_host *host = dev_get_drvdata(&pdev->dev);
> +
> + if (WARN_ON(!host))
> + return 0;
> +
> + if (host->bounce_buf)
> + dma_free_coherent(host->dev, host->bounce_buf_size,
> + host->bounce_buf, host->bounce_dma_addr);
> +
> + clk_disable_unprepare(host->cfg_div_clk);
> + clk_disable_unprepare(host->core_clk);
> +
> + mmc_free_host(host->mmc);
> + return 0;
> +}
> +
> +static const struct of_device_id meson_mmc_of_match[] = {
> + { .compatible = "amlogic,meson-gx-mmc", },
> + { .compatible = "amlogic,meson-gxbb-mmc", },
> + { .compatible = "amlogic,meson-gxl-mmc", },
> + { .compatible = "amlogic,meson-gxm-mmc", },
> + {}
> +};
> +MODULE_DEVICE_TABLE(of, meson_mmc_of_match);
> +
> +static struct platform_driver meson_mmc_driver = {
> + .probe = meson_mmc_probe,
> + .remove = meson_mmc_remove,
> + .driver = {
> + .name = DRIVER_NAME,
> + .of_match_table = of_match_ptr(meson_mmc_of_match),
> + },
> +};
> +
> +module_platform_driver(meson_mmc_driver);
> +
> +MODULE_DESCRIPTION("Amlogic S905*/GX* SD/eMMC driver");
> +MODULE_AUTHOR("Kevin Hilman <khilman@baylibre.com>");
> +MODULE_LICENSE("GPL v2");
> +
> --
> 2.9.3
>
^ permalink raw reply
* Re: [PATCH v6 2/2] Documentation: DT: MMC: meson-gx: new bindings doc
From: Ulf Hansson @ 2016-10-27 7:47 UTC (permalink / raw)
To: Kevin Hilman
Cc: devicetree@vger.kernel.org, Rob Herring, linux-mmc,
linux-arm-kernel@lists.infradead.org, linux-amlogic
In-Reply-To: <20161019181825.2186-2-khilman@baylibre.com>
On 19 October 2016 at 20:18, Kevin Hilman <khilman@baylibre.com> wrote:
> Signed-off-by: Kevin Hilman <khilman@baylibre.com>
Thanks, applied for next!
Kind regards
Uffe
> ---
> .../devicetree/bindings/mmc/amlogic,meson-gx.txt | 33 ++++++++++++++++++++++
> 1 file changed, 33 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt
>
> diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt
> new file mode 100644
> index 000000000000..a2fa9a1c26ae
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx.txt
> @@ -0,0 +1,33 @@
> +Amlogic SD / eMMC controller for S905/GXBB family SoCs
> +
> +The MMC 5.1 compliant host controller on Amlogic provides the
> +interface for SD, eMMC and SDIO devices.
> +
> +This file documents the properties in addition to those available in
> +the MMC core bindings, documented by mmc.txt.
> +
> +Required properties:
> +- compatible : contains one of:
> + - "amlogic,meson-gx-mmc"
> + - "amlogic,meson-gxbb-mmc"
> + - "amlogic,meson-gxl-mmc"
> + - "amlogic,meson-gxm-mmc"
> +- clocks : A list of phandle + clock-specifier pairs for the clocks listed in clock-names.
> +- clock-names: Should contain the following:
> + "core" - Main peripheral bus clock
> + "clkin0" - Parent clock of internal mux
> + "clkin1" - Other parent clock of internal mux
> + The driver has an interal mux clock which switches between clkin0 and clkin1 depending on the
> + clock rate requested by the MMC core.
> +
> +Example:
> +
> + sd_emmc_a: mmc@70000 {
> + compatible = "amlogic,meson-gxbb-mmc";
> + reg = <0x0 0x70000 0x0 0x2000>;
> + interrupts = < GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
> + clocks = <&clkc CLKID_SD_EMMC_A>, <&xtal>, <&clkc CLKID_FCLK_DIV2>;
> + clock-names = "core", "clkin0", "clkin1";
> + pinctrl-0 = <&emmc_pins>;
> + };
> +
> --
> 2.9.3
>
^ permalink raw reply
* Re: [RFC PATCH 0/8] Attempt to clean up BUG_ONs for mmc-tree
From: Ulf Hansson @ 2016-10-27 8:19 UTC (permalink / raw)
To: Shawn Lin; +Cc: linux-mmc
In-Reply-To: <1476792192-6265-1-git-send-email-shawn.lin@rock-chips.com>
On 18 October 2016 at 14:03, Shawn Lin <shawn.lin@rock-chips.com> wrote:
>
> We have already removed some BUG_ONs recently[0][1], however there
> are still many pointless BUG_ONs for mmc core. So this patchset is
> going on removing these BUG_ONs from drivers/mmc/core/* and drivers/mmc/card/*.
> It is also possible for us to remove it from drivers/mmc/host/* in the future.
>
> No functional changes intented in theory, except for adding some error
> handle, but I held it for a while in order to test it fully and decided
> to send it after the merge window of 4.9.
>
> Please review and comment. :)
I have reviewed patch 1, patch 2 and patch 3. I think you can follow a
pattern for my review comment, which means the similar comments are
applicable the rest of series as well. Please have a look to see what
you think and perhaps re-spin this.
Thanks for working on this clean-up!
Kind regards
Uffe
>
> [0]: https://patchwork.kernel.org/patch/9300739/
> [1]: https://patchwork.kernel.org/patch/9300741/
>
>
>
> Shawn Lin (8):
> mmc: core: remove BUG_ONs from sdio
> mmc: debugfs: remove BUG_ON from mmc_ext_csd_open
> mmc: core: remove BUG_ONs from mmc
> mmc: core: remove BUG_ONs from sd
> mmc: core: remove BUG_ONs from core.c
> mmc: sdio_uart: remove meaningless BUG_ON
> mmc: queue: remove BUG_ON for bounce_sg
> mmc: mmc_test: remove BUG_ONs and deploy error handling
>
> drivers/mmc/card/mmc_test.c | 12 ++++++++----
> drivers/mmc/card/queue.c | 2 --
> drivers/mmc/card/sdio_uart.c | 2 --
> drivers/mmc/core/core.c | 40 ++++++++++++++--------------------------
> drivers/mmc/core/debugfs.c | 6 +++++-
> drivers/mmc/core/mmc.c | 24 ++++++++++++++----------
> drivers/mmc/core/mmc_ops.c | 20 ++++++--------------
> drivers/mmc/core/sd.c | 20 +++++++++-----------
> drivers/mmc/core/sd_ops.c | 30 ++++++++++--------------------
> drivers/mmc/core/sdio.c | 26 ++++++++++++++------------
> drivers/mmc/core/sdio_cis.c | 3 ++-
> drivers/mmc/core/sdio_irq.c | 12 +++++++-----
> 12 files changed, 89 insertions(+), 108 deletions(-)
>
> --
> 2.3.7
>
>
> --
> 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
* Re: Regression after "do not use CMD13 to get status after speed mode switch"
From: Ulf Hansson @ 2016-10-27 10:04 UTC (permalink / raw)
To: Chaotian Jing
Cc: Adrian Hunter, Linus Walleij, linux-mmc@vger.kernel.org,
linux-arm-msm@vger.kernel.org, Bjorn Andersson, Stephen Boyd,
Andy Gross
In-Reply-To: <CAPDyKFqo_LJ_TS158X9RQ2UeGvLcrgCC8r_4to8c7zy0zQggnQ@mail.gmail.com>
On 20 October 2016 at 09:06, Ulf Hansson <ulf.hansson@linaro.org> wrote:
> On 20 October 2016 at 04:22, Chaotian Jing <chaotian.jing@mediatek.com> wrote:
>> On Wed, 2016-10-19 at 18:41 +0200, Ulf Hansson wrote:
>>> Adrian, Linus,
>>>
>>> Thanks for looking into this and reporting!
>>>
>>> On 18 October 2016 at 15:23, Adrian Hunter <adrian.hunter@intel.com> wrote:
>>> > On 18/10/16 11:36, Linus Walleij wrote:
>>> >> On Mon, Oct 17, 2016 at 4:32 PM, Linus Walleij <linus.walleij@linaro.org> wrote:
>>> >>
>>> >>> Before this patch the eMMC is detected and all partitions enumerated
>>> >>> immediately, but after the patch it doesn't come up at all, except
>>> >>> sometimes, when it appears minutes (!) after boot, all of a sudden.
>>> >>
>>> >> FYI this is what it looks like when it eventually happens:
>>> >> root@msm8660:/ [ 627.710175] mmc0: new high speed MMC card at address 0001
>>> >> [ 627.711641] mmcblk0: mmc0:0001 SEM04G 3.69 GiB
>>> >> [ 627.715485] mmcblk0boot0: mmc0:0001 SEM04G partition 1 1.00 MiB
>>> >> [ 627.736654] mmcblk0boot1: mmc0:0001 SEM04G partition 2 1.00 MiB
>>> >> [ 627.747397] mmcblk0rpmb: mmc0:0001 SEM04G partition 3 128 KiB
>>> >> [ 627.756326] mmcblk0: p1 p2 p3 p4 < p5 p6 p7 p8 p9 p10 p11 p12 p13
>>> >> p14 p15 p16 p17 p18 p19 p20 p21 >
>>> >>
>>> >> So after 627 seconds, a bit hard for users to wait this long for their
>>> >> root filesystem.
>>> >
>>> > If the driver does not support busy detection and the eMMC card provides
>>> > zero as the cmd6 generic timeout (which it may especially as cmd6 generic
>>> > timeout wasn't added until eMMCv4.5), then __mmc_switch() defaults to
>>> > waiting 10 minutes i.e.
>>> >
>>> > #define MMC_OPS_TIMEOUT_MS (10 * 60 * 1000) /* 10 minute timeout */
>>>
>>> Urgh! Yes, I have verified that this is exactly what happens.
>>>
>>> >
>>> > So removal of CMD13 polling for HS mode (as per commit
>>> > 08573eaf1a70104f83fdbee9b84e5be03480e9ed) is going to be a problem for some
>>> > combinations of eMMC cards and host drivers.
>>>
>>> I was looking in the __mmc_switch() function, it's just a pain to walk
>>> trough it :-) So first out I decided to clean it up and factor out the
>>> polling parts. I will post the patches first out tomorrow morning,
>>> running some final test right now.
>>>
>>> Although, that of course doesn't solve our problem. As I see it we
>>> only have a few options here.
>>>
>>> 1) In case when cmd6 generic timeout isn't available, let's assign
>>> another empirically selected value.
>>> 2) Use a specific timeout when switching to HS mode.
>>> 3) Even if we deploy 1 (and 2), perhaps we still should allow polling
>>> with CMD13 for switching to HS mode - unless it causes issues for some
>>> cards/drivers combination?
>>>
>>> BTW, I already tried 2) and it indeed solves the problem, although
>>> depending on the selected timeout, it might delay the card detection
>>> to process.
>>>
>>> Thoughts?
>>
>> I just have a try of switching to HS mode with Hynix EMMC, the first
>> CMD13 gets response of 0x900, but the EMMC is still pull-low DAT0. so
>> that CMD13 cannot indicate current card status in this case.
>
> Thanks for sharing that. Okay, so clearly we have some cards that
> don't supports polling with CMD13 when switching to HS mode.
> One could of course add quirks for these kind of cards and do a fixed
> delay for them, but then to find out which these cards are is going to
> be hard.
>
> It seems like we are left with using a fixed delay. Any ideas of what
> such delay should be? And should we have one specific for switch to
> the various speed modes and a different one that overrides the CMD6
> generic timout, when it doesn't exist?
>
Replying to my own earlier response, as I believe the problem could
also be related to another old commit, see below.
commit a27fbf2f067b0cd6f172c8b696b9a44c58bfaa7a
Author: Seungwon Jeon <tgih.jun@samsung.com>
Date: Wed Sep 4 21:21:05 2013 +0900
mmc: add ignorance case for CMD13 CRC error
While speed mode is changed, CMD13 cannot be guaranteed.
According to the spec., it is not recommended to use CMD13
to check the busy completion of the timing change.
If CMD13 is used in this case, CRC error must be ignored.
Signed-off-by: Seungwon Jeon <tgih.jun@samsung.com>
Acked-by: Ulf Hansson <ulf.hansson@linaro.org>
Signed-off-by: Chris Ball <cjb@laptop.org>
The intent with this commit was not really correct. We don't want to
ignore CRC errors, but instead we should *re-try* sending CMD13 once
we get a CRC error.
Unfortunate since this commit, instead we tell the host driver to
*ignore* CRC errors and instead reads the status and returns 0
(indicating success). In the mmc core, in __mmc_switch(), it will thus
parse the status reply, even for a reply that might have been received
with a CRC error. Not good!
I am wondering whether this actually is the main problem to why we
think polling isn't working for some cases. And perhaps that was the
original problem Chaotian was trying to solve?
Thoughts?
Kind regards
Uffe
^ permalink raw reply
* Re: [PATCH v2] mmc: s3cmci: Use DMA slave map rather than exported DMA filter
From: Ulf Hansson @ 2016-10-27 7:47 UTC (permalink / raw)
To: Sylwester Nawrocki
Cc: linux-samsung-soc, sam.van.den.berge, Arnd Bergmann, linux-mmc,
Bartlomiej Zolnierkiewicz
In-Reply-To: <1477499453-22549-1-git-send-email-s.nawrocki@samsung.com>
On 26 October 2016 at 18:30, Sylwester Nawrocki <s.nawrocki@samsung.com> wrote:
> Support for DMA slave map has been added to the s3c24xx-dma
> controller in commit 34681d84a0f7cc22ded1413dc79eef8a2f23d9c3
> "dmaengine: s3c24xx: Add dma_slave_map for s3c2440 devices"
> This patch converts the s3cmci driver to also use it, so we can
> eventually get rid of the exported filter function once all
> related DMA clients are updated.
>
> Signed-off-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Thanks, applied for next!
Kind regards
Uffe
> ---
> Changes since v1:
> - dma_request_chan() used instead of dma_request_slave_channel()
> for better error handling.
>
> drivers/mmc/host/s3cmci.c | 15 ++++-----------
> 1 file changed, 4 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c
> index c531dee..932a4b1 100644
> --- a/drivers/mmc/host/s3cmci.c
> +++ b/drivers/mmc/host/s3cmci.c
> @@ -28,7 +28,6 @@
> #include <mach/dma.h>
> #include <mach/gpio-samsung.h>
>
> -#include <linux/platform_data/dma-s3c24xx.h>
> #include <linux/platform_data/mmc-s3cmci.h>
>
> #include "s3cmci.h"
> @@ -1682,19 +1681,13 @@ static int s3cmci_probe(struct platform_device *pdev)
> gpio_direction_input(host->pdata->gpio_wprotect);
> }
>
> - /* depending on the dma state, get a dma channel to use. */
> + /* Depending on the dma state, get a DMA channel to use. */
>
> if (s3cmci_host_usedma(host)) {
> - dma_cap_mask_t mask;
> -
> - dma_cap_zero(mask);
> - dma_cap_set(DMA_SLAVE, mask);
> -
> - host->dma = dma_request_slave_channel_compat(mask,
> - s3c24xx_dma_filter, (void *)DMACH_SDI, &pdev->dev, "rx-tx");
> - if (!host->dma) {
> + host->dma = dma_request_chan(&pdev->dev, "rx-tx");
> + ret = PTR_ERR_OR_ZERO(host->dma);
> + if (ret) {
> dev_err(&pdev->dev, "cannot get DMA channel.\n");
> - ret = -EBUSY;
> goto probe_free_gpio_wp;
> }
> }
> --
> 1.9.1
>
^ permalink raw reply
* Re: [RFC PATCH 1/8] mmc: core: remove BUG_ONs from sdio
From: Ulf Hansson @ 2016-10-27 8:03 UTC (permalink / raw)
To: Shawn Lin; +Cc: linux-mmc
In-Reply-To: <1476792229-6306-1-git-send-email-shawn.lin@rock-chips.com>
On 18 October 2016 at 14:03, Shawn Lin <shawn.lin@rock-chips.com> wrote:
> BUG_ONs doesn't help anything except for stop the system from
> running. If it occurs, it implies we should deploy proper error
> handling for that. So this patch is gonna discard these meaningless
> BUG_ONs and deploy error handling if needed.
>
> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
>
> ---
>
> drivers/mmc/core/sdio.c | 26 ++++++++++++++------------
> drivers/mmc/core/sdio_cis.c | 3 ++-
> drivers/mmc/core/sdio_irq.c | 12 +++++++-----
> 3 files changed, 23 insertions(+), 18 deletions(-)
>
> diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c
> index bd44ba8..80883c6 100644
> --- a/drivers/mmc/core/sdio.c
> +++ b/drivers/mmc/core/sdio.c
> @@ -63,7 +63,8 @@ static int sdio_init_func(struct mmc_card *card, unsigned int fn)
> int ret;
> struct sdio_func *func;
>
> - BUG_ON(fn > SDIO_MAX_FUNCS);
> + if (WARN_ON(fn > SDIO_MAX_FUNCS))
> + return -EINVAL;
>
> func = sdio_alloc_func(card);
> if (IS_ERR(func))
> @@ -555,7 +556,9 @@ static int mmc_sdio_init_card(struct mmc_host *host, u32 ocr,
> u32 rocr = 0;
> u32 ocr_card = ocr;
>
> - BUG_ON(!host);
> + if (!host)
> + return -EINVAL;
> +
Just remove this altogether. We always have a host here and the
function is internal to sdio.c
> WARN_ON(!host->claimed);
>
> /* to query card if 1.8V signalling is supported */
> @@ -791,8 +794,8 @@ static void mmc_sdio_remove(struct mmc_host *host)
> {
> int i;
>
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> + if (!host)
> + return;
>
Ditto.
> for (i = 0;i < host->card->sdio_funcs;i++) {
> if (host->card->sdio_func[i]) {
> @@ -820,8 +823,8 @@ static void mmc_sdio_detect(struct mmc_host *host)
> {
> int err;
>
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> + if (WARN_ON(!host))
> + goto out;
Ditto.
>
> /* Make sure card is powered before detecting it */
> if (host->caps & MMC_CAP_POWER_OFF_CARD) {
> @@ -916,8 +919,8 @@ static int mmc_sdio_resume(struct mmc_host *host)
> {
> int err = 0;
>
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> + if (!host)
> + return -EINVAL;
>
Ditto.
> /* Basic card reinitialization. */
> mmc_claim_host(host);
> @@ -970,9 +973,6 @@ static int mmc_sdio_power_restore(struct mmc_host *host)
> {
> int ret;
>
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> -
> mmc_claim_host(host);
>
> /*
> @@ -1063,7 +1063,9 @@ int mmc_attach_sdio(struct mmc_host *host)
> u32 ocr, rocr;
> struct mmc_card *card;
>
> - BUG_ON(!host);
> + if (!host)
> + return -EINVAL;
> +
Ditto.
> WARN_ON(!host->claimed);
>
> err = mmc_send_io_op_cond(host, 0, &ocr);
> diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
> index dcb3dee..f8c3728 100644
> --- a/drivers/mmc/core/sdio_cis.c
> +++ b/drivers/mmc/core/sdio_cis.c
> @@ -262,7 +262,8 @@ static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
> else
> prev = &card->tuples;
>
> - BUG_ON(*prev);
> + if (*prev)
> + return -EINVAL;
>
> do {
> unsigned char tpl_code, tpl_link;
> diff --git a/drivers/mmc/core/sdio_irq.c b/drivers/mmc/core/sdio_irq.c
> index 91bbbfb..f1faf9a 100644
> --- a/drivers/mmc/core/sdio_irq.c
> +++ b/drivers/mmc/core/sdio_irq.c
> @@ -214,7 +214,9 @@ static int sdio_card_irq_put(struct mmc_card *card)
> struct mmc_host *host = card->host;
>
> WARN_ON(!host->claimed);
> - BUG_ON(host->sdio_irqs < 1);
> +
> + if (host->sdio_irqs < 1)
> + return -EINVAL;
>
> if (!--host->sdio_irqs) {
> if (!(host->caps2 & MMC_CAP2_SDIO_IRQ_NOTHREAD)) {
> @@ -261,8 +263,8 @@ int sdio_claim_irq(struct sdio_func *func, sdio_irq_handler_t *handler)
> int ret;
> unsigned char reg;
>
> - BUG_ON(!func);
> - BUG_ON(!func->card);
> + if (!func)
> + return -EINVAL;
>
> pr_debug("SDIO: Enabling IRQ for %s...\n", sdio_func_id(func));
>
> @@ -304,8 +306,8 @@ int sdio_release_irq(struct sdio_func *func)
> int ret;
> unsigned char reg;
>
> - BUG_ON(!func);
> - BUG_ON(!func->card);
> + if (!func)
> + return -EINVAL;
>
> pr_debug("SDIO: Disabling IRQ for %s...\n", sdio_func_id(func));
>
> --
> 2.3.7
Kind regards
Uffe
^ permalink raw reply
* Re: [PATCH] mmc: Hynix: add QUIRK_NOTIFY_POWEROFF_ON_SLEEP
From: Thierry Escande @ 2016-10-27 15:06 UTC (permalink / raw)
To: Ulf Hansson; +Cc: linux-mmc
In-Reply-To: <CAPDyKFoO1JX2-jpux+f-1O8kDC+jumspXapA0PZvKMBUE3YhSA@mail.gmail.com>
Hi Ulf,
On 25/10/2016 12:03, Ulf Hansson wrote:
> On 3 October 2016 at 16:19, Thierry Escande
> <thierry.escande@collabora.com> wrote:
>> From: zhaojohn <john.zhao@intel.com>
>>
>> Hynix eMMC devices sometimes take 50% longer to resume from sleep.
>> Based on a recommendation from Hynix, send a Power-Off Notification
>> before going to S3 to restore a resume time consistently within spec.
>
> Could you also share what mmc controller and SoC you get this results from?
>
> More precisely, are you using MMC_CAP_WAIT_WHILE_BUSY?
This occurs on a braswell based chromebook, using the acpi sdhci
controller. So yes, using MMC_CAP_WAIT_WHILE_BUSY.
[...]
>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>> index f2d185c..46a4562 100644
>> --- a/drivers/mmc/core/mmc.c
>> +++ b/drivers/mmc/core/mmc.c
>> @@ -1925,8 +1925,14 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
>> if (mmc_can_poweroff_notify(host->card) &&
>> ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
>> err = mmc_poweroff_notify(host->card, notify_type);
>> - else if (mmc_can_sleep(host->card))
>> + else if (mmc_can_sleep(host->card)) {
>> + if (host->card->quirks & MMC_QUIRK_NOTIFY_POWEROFF_ON_SLEEP) {
>> + err = mmc_poweroff_notify(host->card, notify_type);
>> + if (err)
>> + goto out;
>> + }
>
> So, I am curious to know from a power management point of view; how
> does the card behave comparing the sleep and power off notification
> command?
>
> Is the card in a low power state after the power off notification has
> been received? If so, did you manage to do some measurement for that
> or perhaps the data-sheet tells about this? It would be interesting to
> know if there were any differences between sleep and power off
> notification in this regards.
I do not have any clue about that. It appears only with Hynix emmc and
the fix has been approved by Hynix engineers... It seems that if not
powered off, the firmware does some garbage collection when resuming and
it takes more time...
Regards,
Thierry
^ permalink raw reply
* Re: [RFC PATCH 3/8] mmc: core: remove BUG_ONs from mmc
From: Ulf Hansson @ 2016-10-27 8:14 UTC (permalink / raw)
To: Shawn Lin; +Cc: linux-mmc
In-Reply-To: <1476792244-6390-1-git-send-email-shawn.lin@rock-chips.com>
On 18 October 2016 at 14:04, Shawn Lin <shawn.lin@rock-chips.com> wrote:
> BUG_ONs doesn't help anything except for stop the system from
> running. If it occurs, it implies we should deploy proper error
> handling for that. So this patch is gonna discard these meaningless
> BUG_ONs and deploy error handling if needed.
>
> Signed-off-by: Shawn Lin <shawn.lin@rock-chips.com>
> ---
>
> drivers/mmc/core/mmc.c | 24 ++++++++++++++----------
> drivers/mmc/core/mmc_ops.c | 20 ++++++--------------
> 2 files changed, 20 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
> index 39fc5b2..8f63b59 100644
> --- a/drivers/mmc/core/mmc.c
> +++ b/drivers/mmc/core/mmc.c
> @@ -1477,7 +1477,9 @@ static int mmc_init_card(struct mmc_host *host, u32 ocr,
> u32 cid[4];
> u32 rocr;
>
> - BUG_ON(!host);
> + if (!host)
> + return -EINVAL;
> +
Not needed, just remove.
> WARN_ON(!host->claimed);
>
> /* Set correct bus mode for MMC before attempting init */
> @@ -1867,8 +1869,8 @@ static int mmc_poweroff_notify(struct mmc_card *card, unsigned int notify_type)
> */
> static void mmc_remove(struct mmc_host *host)
> {
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> + if (!host)
> + return;
>
Ditto.
> mmc_remove_card(host->card);
> host->card = NULL;
> @@ -1889,8 +1891,8 @@ static void mmc_detect(struct mmc_host *host)
> {
> int err;
>
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> + if (WARN_ON(!host))
> + return;
>
Ditto.
> mmc_get_card(host->card);
>
> @@ -1917,8 +1919,8 @@ static int _mmc_suspend(struct mmc_host *host, bool is_suspend)
> unsigned int notify_type = is_suspend ? EXT_CSD_POWER_OFF_SHORT :
> EXT_CSD_POWER_OFF_LONG;
>
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> + if (WARN_ON(!host))
> + return -EINVAL;
>
Ditto.
> mmc_claim_host(host);
>
> @@ -1976,8 +1978,8 @@ static int _mmc_resume(struct mmc_host *host)
> {
> int err = 0;
>
> - BUG_ON(!host);
> - BUG_ON(!host->card);
> + if (WARN_ON(!host))
> + return -EINVAL;
>
Ditto.
> mmc_claim_host(host);
>
> @@ -2111,7 +2113,9 @@ int mmc_attach_mmc(struct mmc_host *host)
> int err;
> u32 ocr, rocr;
>
> - BUG_ON(!host);
> + if (!host)
> + return -EINVAL;
> +
Ditto.
> WARN_ON(!host->claimed);
>
> /* Set correct bus mode for MMC before attempting attach */
> diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c
> index ad6e979..ceb41eb 100644
> --- a/drivers/mmc/core/mmc_ops.c
> +++ b/drivers/mmc/core/mmc_ops.c
> @@ -60,9 +60,6 @@ static inline int __mmc_send_status(struct mmc_card *card, u32 *status,
> int err;
> struct mmc_command cmd = {0};
>
> - BUG_ON(!card);
> - BUG_ON(!card->host);
> -
> cmd.opcode = MMC_SEND_STATUS;
> if (!mmc_host_is_spi(card->host))
> cmd.arg = card->rca << 16;
> @@ -92,7 +89,8 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
> {
> struct mmc_command cmd = {0};
>
> - BUG_ON(!host);
> + if (!host)
> + return -EINVAL;
>
Ditto.
> cmd.opcode = MMC_SELECT_CARD;
>
> @@ -109,7 +107,6 @@ static int _mmc_select_card(struct mmc_host *host, struct mmc_card *card)
>
> int mmc_select_card(struct mmc_card *card)
> {
> - BUG_ON(!card);
>
> return _mmc_select_card(card->host, card);
> }
> @@ -181,8 +178,6 @@ int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr)
> struct mmc_command cmd = {0};
> int i, err = 0;
>
> - BUG_ON(!host);
> -
> cmd.opcode = MMC_SEND_OP_COND;
> cmd.arg = mmc_host_is_spi(host) ? 0 : ocr;
> cmd.flags = MMC_RSP_SPI_R1 | MMC_RSP_R3 | MMC_CMD_BCR;
> @@ -221,8 +216,8 @@ int mmc_all_send_cid(struct mmc_host *host, u32 *cid)
> int err;
> struct mmc_command cmd = {0};
>
> - BUG_ON(!host);
> - BUG_ON(!cid);
> + if (!cid)
> + return -ENOMEM;
Remove as not needed, as mmc_all_send_cid() is internal to the mmc
core. We know all callers already provide the cid.
>
> cmd.opcode = MMC_ALL_SEND_CID;
> cmd.arg = 0;
> @@ -241,9 +236,6 @@ int mmc_set_relative_addr(struct mmc_card *card)
> {
> struct mmc_command cmd = {0};
>
> - BUG_ON(!card);
> - BUG_ON(!card->host);
> -
> cmd.opcode = MMC_SET_RELATIVE_ADDR;
> cmd.arg = card->rca << 16;
> cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
> @@ -257,8 +249,8 @@ mmc_send_cxd_native(struct mmc_host *host, u32 arg, u32 *cxd, int opcode)
> int err;
> struct mmc_command cmd = {0};
>
> - BUG_ON(!host);
> - BUG_ON(!cxd);
> + if (!cxd)
> + return -ENOMEM;
For the same reasons as above, let's remove this.
>
> cmd.opcode = opcode;
> cmd.arg = arg;
> --
Kind regards
Uffe
^ permalink raw reply
* Re: [PATCH -next] mmc: sdhci-msm: Fix error return code in sdhci_msm_probe()
From: Ulf Hansson @ 2016-10-27 7:47 UTC (permalink / raw)
To: Wei Yongjun; +Cc: Adrian Hunter, Georgi Djakov, Wei Yongjun, linux-mmc
In-Reply-To: <1477494281-24246-1-git-send-email-weiyj.lk@gmail.com>
On 26 October 2016 at 17:04, Wei Yongjun <weiyj.lk@gmail.com> wrote:
> From: Wei Yongjun <weiyongjun1@huawei.com>
>
> Fix to return a negative error code from the platform_get_irq_byname()
> error handling case instead of 0, as done elsewhere in this function.
>
> Fixes: ad81d3871004 ("mmc: sdhci-msm: Add support for UHS cards")
> Signed-off-by: Wei Yongjun <weiyongjun1@huawei.com>
Thanks, applied for fixes!
Kind regards
Uffe
> ---
> drivers/mmc/host/sdhci-msm.c | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
> index 795f16f..b78d72f 100644
> --- a/drivers/mmc/host/sdhci-msm.c
> +++ b/drivers/mmc/host/sdhci-msm.c
> @@ -649,6 +649,7 @@ static int sdhci_msm_probe(struct platform_device *pdev)
> if (msm_host->pwr_irq < 0) {
> dev_err(&pdev->dev, "Get pwr_irq failed (%d)\n",
> msm_host->pwr_irq);
> + ret = msm_host->pwr_irq;
> goto clk_disable;
> }
>
^ permalink raw reply
* Re: [PATCH] mmc: sdhci-pci-core: Tuning mode support for HS200 on AMD Platforms
From: Shyam Sundar S K @ 2016-10-27 9:52 UTC (permalink / raw)
To: Adrian Hunter
Cc: ulf.hansson, linux-mmc, Sen, Pankaj, Shah, Nehal-bakulchandra,
Agrawal, Nitesh-kumar
In-Reply-To: <418f99e4-18e5-38a6-0b4d-0815f3bd604f@intel.com>
Made changes to the earlier submission based on the comments
received from Adrian.
Reviewed-by: Sen, Pankaj <Pankaj.Sen@amd.com>
Reviewed-by: Shah, Nehal-bakulchandra <Nehal-bakulchandra.Shah@amd.com>
Signed-off-by: S-k, Shyam-sundar <Shyam-sundar.S-k@amd.com>
Also, adding patch from Adrian for handling the device specific
private data.
From: Adrian Hunter <adrian.hunter@intel.com>
Date: Mon, 10 Oct 2016 10:04:45 +0300
Subject: [PATCH] mmc: sdhci-pci: Let devices define their own private data
Let devices define their own private data to facilitate device-specific
operations. The size of the private structure is specified in the
sdhci_pci_fixes structure, then sdhci_pci_probe_slot() will allocate extra
space for it, and sdhci_pci_priv() can be used to get a reference to it.
Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
---
drivers/mmc/host/sdhci-pci-core.c | 181 +++++++++++++++++++++++++++++++++++++-
drivers/mmc/host/sdhci-pci.h | 7 ++
2 files changed, 186 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index 1d9e00a..9576f82 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -817,6 +817,171 @@ enum amd_chipset_gen {
AMD_CHIPSET_UNKNOWN,
};
+static const struct sdhci_ops amd_sdhci_pci_ops;
+
+struct amd_tuning_descriptor {
+ u8 tune_around;
+ bool this_tune_ok;
+ bool last_tune_ok;
+ u8 valid_front;
+ u8 valid_window_max;
+ u8 tune_low_max;
+ u8 tune_low;
+ u8 valid_window;
+ u8 tune_result;
+};
+
+static int amd_tuning_reset(struct sdhci_host *host)
+{
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ val |= SDHCI_CTRL_PRESET_VAL_ENABLE | SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
+
+ val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
+ val &= ~SDHCI_CTRL_EXEC_TUNING;
+ sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return 0;
+}
+
+static int amd_config_tuning_phase(struct sdhci_host *host, u8 phase)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ pci_read_config_dword(pdev, 0xb8, &val);
+ val &= ~0x1f;
+ val |= (0x10800 | (phase << 1));
+ pci_write_config_dword(pdev, 0xb8, val);
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return 0;
+}
+
+static int amd_find_good_phase(struct sdhci_host *host)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct pci_dev *pdev = slot->chip->pdev;
+ struct amd_tuning_descriptor *td = sdhci_pci_priv(slot);
+
+ unsigned int val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&host->lock, flags);
+
+ if (td->this_tune_ok)
+ td->valid_front += 1;
+ if ((!td->this_tune_ok && td->last_tune_ok) ||
+ (td->tune_around == 11)) {
+ if (td->valid_window > td->valid_window_max) {
+ td->valid_window_max = td->valid_window;
+ td->tune_low_max = td->tune_low;
+ }
+ }
+ if (td->this_tune_ok && (!td->last_tune_ok))
+ td->tune_low = td->tune_around;
+ if (!td->this_tune_ok && td->last_tune_ok)
+ td->valid_window = 0;
+ else if (td->this_tune_ok)
+ td->valid_window += 1;
+
+ td->last_tune_ok = td->this_tune_ok;
+
+ if (td->tune_around == 11) {
+ if ((td->valid_front + td->valid_window) >
+ td->valid_window_max) {
+ if (td->valid_front > td->valid_window)
+ td->tune_result = ((td->valid_front -
+ td->valid_window) >> 1);
+ else
+ td->tune_result = td->tune_low +
+ ((td->valid_window + td->valid_front) >> 1);
+ } else {
+ td->tune_result = td->tune_low_max +
+ (td->valid_window_max >> 1);
+ }
+
+ if (td->tune_result > 0x0b)
+ td->tune_result = 0x0b;
+
+ pci_read_config_dword(pdev, 0xb8, &val);
+ val &= ~0x1f;
+ val |= (0x10800 | (td->tune_result << 1));
+ pci_write_config_dword(pdev, 0xb8, val);
+ }
+
+ spin_unlock_irqrestore(&host->lock, flags);
+
+ return 0;
+}
+
+static int amd_enable_manual_tuning(struct sdhci_pci_slot *slot)
+{
+ struct pci_dev *pdev = slot->chip->pdev;
+ unsigned int val;
+
+ pci_read_config_dword(pdev, 0xd0, &val);
+ val &= 0xffffffcf;
+ val |= 0x30;
+ pci_write_config_dword(pdev, 0xd0, val);
+
+ return 0;
+}
+
+static int amd_execute_tuning(struct sdhci_host *host, u32 opcode)
+{
+ struct sdhci_pci_slot *slot = sdhci_priv(host);
+ struct amd_tuning_descriptor *td = sdhci_pci_priv(slot);
+ u8 ctrl;
+
+ amd_tuning_reset(host);
+ memset(td, 0, sizeof(struct amd_tuning_descriptor));
+
+ /*********************************************************************/
+ /* Enabling Software Tuning */
+ /********************************************************************/
+ /* 1. First switch the eMMC to HS200 Mode
+ * 2. Prepare the registers by using the sampling clock select
+ * 3. Send the CMD21 12 times with block length of 64 bytes
+ * 4. Everytime change the clk phase and check for CRC error
+ * (CMD and DATA),if error, soft reset the CMD and DATA line
+ * 5. Calculate the window and set the clock phase.
+ */
+
+ for (td->tune_around = 0; td->tune_around < 12; td->tune_around++) {
+ amd_config_tuning_phase(host, td->tune_around);
+
+ if (mmc_send_tuning(host->mmc, opcode, NULL)) {
+ td->this_tune_ok = false;
+ host->mmc->need_retune = 0;
+ mdelay(4);
+ ctrl = SDHCI_RESET_CMD | SDHCI_RESET_DATA;
+ sdhci_writeb(host, ctrl, SDHCI_SOFTWARE_RESET);
+ } else {
+ td->this_tune_ok = true;
+ }
+
+ amd_find_good_phase(host);
+ }
+
+ host->mmc->retune_period = 0;
+
+ amd_enable_manual_tuning(slot);
+ return 0;
+}
+
static int amd_probe(struct sdhci_pci_chip *chip)
{
struct pci_dev *smbus_dev;
@@ -841,7 +1006,6 @@ static int amd_probe(struct sdhci_pci_chip *chip)
if ((gen == AMD_CHIPSET_BEFORE_ML) || (gen == AMD_CHIPSET_CZ)) {
chip->quirks2 |= SDHCI_QUIRK2_CLEAR_TRANSFERMODE_REG_BEFORE_CMD;
- chip->quirks2 |= SDHCI_QUIRK2_BROKEN_HS200;
}
return 0;
@@ -849,6 +1013,7 @@ static int amd_probe(struct sdhci_pci_chip *chip)
static const struct sdhci_pci_fixes sdhci_amd = {
.probe = amd_probe,
+ .ops = &amd_sdhci_pci_ops,
};
static const struct pci_device_id pci_ids[] = {
@@ -1469,6 +1634,17 @@ static const struct sdhci_ops sdhci_pci_ops = {
.select_drive_strength = sdhci_pci_select_drive_strength,
};
+static const struct sdhci_ops amd_sdhci_pci_ops = {
+ .set_clock = sdhci_set_clock,
+ .enable_dma = sdhci_pci_enable_dma,
+ .set_bus_width = sdhci_pci_set_bus_width,
+ .reset = sdhci_reset,
+ .set_uhs_signaling = sdhci_set_uhs_signaling,
+ .hw_reset = sdhci_pci_hw_reset,
+ .select_drive_strength = sdhci_pci_select_drive_strength,
+ .platform_execute_tuning = amd_execute_tuning,
+};
+
/*****************************************************************************\
* *
* Suspend/resume *
@@ -1646,6 +1822,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
struct sdhci_pci_slot *slot;
struct sdhci_host *host;
int ret, bar = first_bar + slotno;
+ size_t priv_size = chip->fixes ? chip->fixes->priv_size : 0;
if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
@@ -1667,7 +1844,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
return ERR_PTR(-ENODEV);
}
- host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
+ host = sdhci_alloc_host(&pdev->dev, sizeof(*slot) + priv_size);
if (IS_ERR(host)) {
dev_err(&pdev->dev, "cannot allocate host\n");
return ERR_CAST(host);
diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
index 6bccf56..0bfd568 100644
--- a/drivers/mmc/host/sdhci-pci.h
+++ b/drivers/mmc/host/sdhci-pci.h
@@ -67,6 +67,7 @@ struct sdhci_pci_fixes {
int (*resume) (struct sdhci_pci_chip *);
const struct sdhci_ops *ops;
+ size_t priv_size;
};
struct sdhci_pci_slot {
@@ -87,6 +88,7 @@ struct sdhci_pci_slot {
struct mmc_card *card,
unsigned int max_dtr, int host_drv,
int card_drv, int *drv_type);
+ unsigned long private[0] ____cacheline_aligned;
};
struct sdhci_pci_chip {
@@ -101,4 +103,9 @@ struct sdhci_pci_chip {
struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
};
+static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
+{
+ return (void *)slot->private;
+}
+
#endif /* __SDHCI_PCI_H */
--
2.7.4
On 10/10/2016 1:25 PM, Adrian Hunter wrote:
> On 04/10/16 11:42, Shyam Sundar S K wrote:
>> This patch adds support for HS200 tuning mode on AMD eMMC-4.5.1
>>
>> Reviewed-by: Sen, Pankaj <Pankaj.Sen@amd.com>
>> Reviewed-by: Shah, Nehal-bakulchandra <Nehal-bakulchandra.Shah@amd.com>
>> Signed-off-by: S-k, Shyam-sundar <Shyam-sundar.S-k@amd.com>
>> ---
>> drivers/mmc/host/sdhci-pci-core.c | 182 +++++++++++++++++++++++++++++++++++++-
>> 1 file changed, 180 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
>> index 897cfd2..5893ec4 100644
>> --- a/drivers/mmc/host/sdhci-pci-core.c
>> +++ b/drivers/mmc/host/sdhci-pci-core.c
>> @@ -734,6 +734,7 @@ static const struct sdhci_pci_fixes sdhci_via = {
>> .probe = via_probe,
>> };
>>
>> +
>
> Unnecessary blank line
>
>> static int rtsx_probe_slot(struct sdhci_pci_slot *slot)
>> {
>> slot->host->mmc->caps2 |= MMC_CAP2_HS200;
>> @@ -755,6 +756,172 @@ enum amd_chipset_gen {
>> AMD_CHIPSET_UNKNOWN,
>> };
>>
>> +struct tuning_descriptor {
>
> It would be nicer to prefix all structure and function names by something
> specific to the device e.g. amd_...
>
>> + unsigned char tune_around;
>> + bool this_tune_ok;
>> + bool last_tune_ok;
>> + bool valid_front_end;
>> + unsigned char valid_front;
>> + unsigned char valid_window_max;
>> + unsigned char tune_low_max;
>> + unsigned char tune_low;
>> + unsigned char valid_window;
>> + unsigned char tune_result;
>
> 'unsigned char' -> 'u8'
>
>> +};
>> +
>> +static struct sdhci_ops sdhci_pci_ops;
>> +static struct tuning_descriptor tdescriptor;
>
> tdescriptor should not be global. You need somewhere to put private
> data. How about this:
>
>
> From: Adrian Hunter <adrian.hunter@intel.com>
> Date: Mon, 10 Oct 2016 10:04:45 +0300
> Subject: [PATCH] mmc: sdhci-pci: Let devices define their own private data
>
> Let devices define their own private data to facilitate device-specific
> operations. The size of the private structure is specified in the
> sdhci_pci_fixes structure, then sdhci_pci_probe_slot() will allocate extra
> space for it, and sdhci_pci_priv() can be used to get a reference to it.
>
> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
> drivers/mmc/host/sdhci-pci-core.c | 3 ++-
> drivers/mmc/host/sdhci-pci.h | 8 ++++++++
> 2 files changed, 10 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
> index 1d9e00a00e9f..782c8d25c0c8 100644
> --- a/drivers/mmc/host/sdhci-pci-core.c
> +++ b/drivers/mmc/host/sdhci-pci-core.c
> @@ -1646,6 +1646,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
> struct sdhci_pci_slot *slot;
> struct sdhci_host *host;
> int ret, bar = first_bar + slotno;
> + size_t priv_size = chip->fixes ? chip->fixes->priv_size : 0;
>
> if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) {
> dev_err(&pdev->dev, "BAR %d is not iomem. Aborting.\n", bar);
> @@ -1667,7 +1668,7 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot(
> return ERR_PTR(-ENODEV);
> }
>
> - host = sdhci_alloc_host(&pdev->dev, sizeof(struct sdhci_pci_slot));
> + host = sdhci_alloc_host(&pdev->dev, sizeof(*slot) + priv_size);
> if (IS_ERR(host)) {
> dev_err(&pdev->dev, "cannot allocate host\n");
> return ERR_CAST(host);
> diff --git a/drivers/mmc/host/sdhci-pci.h b/drivers/mmc/host/sdhci-pci.h
> index 6bccf56bc5ff..6a1be6afe089 100644
> --- a/drivers/mmc/host/sdhci-pci.h
> +++ b/drivers/mmc/host/sdhci-pci.h
> @@ -67,6 +67,7 @@ struct sdhci_pci_fixes {
> int (*resume) (struct sdhci_pci_chip *);
>
> const struct sdhci_ops *ops;
> + size_t priv_size;
> };
>
> struct sdhci_pci_slot {
> @@ -87,6 +88,8 @@ struct sdhci_pci_slot {
> struct mmc_card *card,
> unsigned int max_dtr, int host_drv,
> int card_drv, int *drv_type);
> +
> + unsigned long private[0] ____cacheline_aligned;
> };
>
> struct sdhci_pci_chip {
> @@ -101,4 +104,9 @@ struct sdhci_pci_chip {
> struct sdhci_pci_slot *slots[MAX_SLOTS]; /* Pointers to host slots */
> };
>
> +static inline void *sdhci_pci_priv(struct sdhci_pci_slot *slot)
> +{
> + return (void *)slot->private;
> +}
> +
> #endif /* __SDHCI_PCI_H */
>
^ permalink raw reply related
* [PATCH 1/2] mmc: mxs: Initialize the spinlock prior to using it
From: Fabio Estevam @ 2016-10-27 18:57 UTC (permalink / raw)
To: ulf.hansson; +Cc: kernel, linux-mmc, stefan.wahren, marex, Fabio Estevam
From: Fabio Estevam <fabio.estevam@nxp.com>
An interrupt may occur right after devm_request_irq() is called and
prior to the spinlock initialization, leading to a kernel oops,
as the interrupt handler uses the spinlock.
In order to prevent this problem, move the spinlock initialization
prior to requesting the interrupts.
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
---
drivers/mmc/host/mxs-mmc.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index d839147..44ecebd 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -661,13 +661,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, mmc);
+ spin_lock_init(&host->lock);
+
ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
dev_name(&pdev->dev), host);
if (ret)
goto out_free_dma;
- spin_lock_init(&host->lock);
-
ret = mmc_add_host(mmc);
if (ret)
goto out_free_dma;
--
2.7.4
^ permalink raw reply related
* [PATCH 2/2] mmc: mxs: Use the spinlock irq variants
From: Fabio Estevam @ 2016-10-27 18:57 UTC (permalink / raw)
To: ulf.hansson; +Cc: kernel, linux-mmc, stefan.wahren, marex, Fabio Estevam
In-Reply-To: <1477594672-31611-1-git-send-email-festevam@gmail.com>
From: Fabio Estevam <fabio.estevam@nxp.com>
Inside an interrupt handler we should use the spin_lock_irqsave()/
spin_unlock_irqrestore() variants, so fix it accordingly.
Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
---
drivers/mmc/host/mxs-mmc.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
index 44ecebd..14b7548 100644
--- a/drivers/mmc/host/mxs-mmc.c
+++ b/drivers/mmc/host/mxs-mmc.c
@@ -189,15 +189,16 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
struct mmc_command *cmd = host->cmd;
struct mmc_data *data = host->data;
struct mxs_ssp *ssp = &host->ssp;
+ unsigned long flags;
u32 stat;
- spin_lock(&host->lock);
+ spin_lock_irqsave(&host->lock, flags);
stat = readl(ssp->base + HW_SSP_CTRL1(ssp));
writel(stat & MXS_MMC_IRQ_BITS,
ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
- spin_unlock(&host->lock);
+ spin_unlock_irqrestore(&host->lock, flags);
if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN))
mmc_signal_sdio_irq(host->mmc);
--
2.7.4
^ permalink raw reply related
* Re: [PATCH 1/2] mmc: mxs: Initialize the spinlock prior to using it
From: Marek Vasut @ 2016-10-27 19:06 UTC (permalink / raw)
To: Fabio Estevam, ulf.hansson
Cc: kernel, linux-mmc, stefan.wahren, Fabio Estevam
In-Reply-To: <1477594672-31611-1-git-send-email-festevam@gmail.com>
On 10/27/2016 08:57 PM, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> An interrupt may occur right after devm_request_irq() is called and
> prior to the spinlock initialization, leading to a kernel oops,
> as the interrupt handler uses the spinlock.
>
> In order to prevent this problem, move the spinlock initialization
> prior to requesting the interrupts.
>
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Nice, thanks.
Could you also check whether you can disable and clear interrupts in the
probe routine ? I think that might make sense too.
Reviewed-by: Marek Vasut <marex@denx.de>
> ---
> drivers/mmc/host/mxs-mmc.c | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
> index d839147..44ecebd 100644
> --- a/drivers/mmc/host/mxs-mmc.c
> +++ b/drivers/mmc/host/mxs-mmc.c
> @@ -661,13 +661,13 @@ static int mxs_mmc_probe(struct platform_device *pdev)
>
> platform_set_drvdata(pdev, mmc);
>
> + spin_lock_init(&host->lock);
> +
> ret = devm_request_irq(&pdev->dev, irq_err, mxs_mmc_irq_handler, 0,
> dev_name(&pdev->dev), host);
> if (ret)
> goto out_free_dma;
>
> - spin_lock_init(&host->lock);
> -
> ret = mmc_add_host(mmc);
> if (ret)
> goto out_free_dma;
>
--
Best regards,
Marek Vasut
^ permalink raw reply
* Re: [PATCH 2/2] mmc: mxs: Use the spinlock irq variants
From: Marek Vasut @ 2016-10-27 19:09 UTC (permalink / raw)
To: Fabio Estevam, ulf.hansson
Cc: kernel, linux-mmc, stefan.wahren, Fabio Estevam
In-Reply-To: <1477594672-31611-2-git-send-email-festevam@gmail.com>
On 10/27/2016 08:57 PM, Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> Inside an interrupt handler we should use the spin_lock_irqsave()/
> spin_unlock_irqrestore() variants, so fix it accordingly.
>
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
Reviewed-by: Marek Vasut <marex@denx.de>
--
Best regards,
Marek Vasut
^ permalink raw reply
* Re: [PATCH 1/2] mmc: mxs: Initialize the spinlock prior to using it
From: Fabio Estevam @ 2016-10-27 19:17 UTC (permalink / raw)
To: Marek Vasut
Cc: Ulf Hansson, Sascha Hauer, linux-mmc@vger.kernel.org,
Stefan Wahren, Fabio Estevam
In-Reply-To: <15d2f274-f1a2-d657-59d0-863be475eae8@denx.de>
Hi Marek,
On Thu, Oct 27, 2016 at 5:06 PM, Marek Vasut <marex@denx.de> wrote:
> Nice, thanks.
>
> Could you also check whether you can disable and clear interrupts in the
> probe routine ? I think that might make sense too.
Yes, that's a good idea. I will look into that after Ulf applies this
series. Thanks
^ permalink raw reply
* Re: [PATCH] mmc: Hynix: add QUIRK_NOTIFY_POWEROFF_ON_SLEEP
From: Ulf Hansson @ 2016-10-27 19:49 UTC (permalink / raw)
To: Thierry Escande; +Cc: linux-mmc
In-Reply-To: <e28c3ced-ddaa-27a3-469b-d1f5cc29b407@collabora.com>
On 27 October 2016 at 17:06, Thierry Escande
<thierry.escande@collabora.com> wrote:
> Hi Ulf,
>
> On 25/10/2016 12:03, Ulf Hansson wrote:
>>
>> On 3 October 2016 at 16:19, Thierry Escande
>> <thierry.escande@collabora.com> wrote:
>>>
>>> From: zhaojohn <john.zhao@intel.com>
>>>
>>> Hynix eMMC devices sometimes take 50% longer to resume from sleep.
>>> Based on a recommendation from Hynix, send a Power-Off Notification
>>> before going to S3 to restore a resume time consistently within spec.
>>
>>
>> Could you also share what mmc controller and SoC you get this results
>> from?
>>
>> More precisely, are you using MMC_CAP_WAIT_WHILE_BUSY?
>
> This occurs on a braswell based chromebook, using the acpi sdhci controller.
> So yes, using MMC_CAP_WAIT_WHILE_BUSY.
>
> [...]
>>>
>>> diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c
>>> index f2d185c..46a4562 100644
>>> --- a/drivers/mmc/core/mmc.c
>>> +++ b/drivers/mmc/core/mmc.c
>>> @@ -1925,8 +1925,14 @@ static int _mmc_suspend(struct mmc_host *host,
>>> bool is_suspend)
>>> if (mmc_can_poweroff_notify(host->card) &&
>>> ((host->caps2 & MMC_CAP2_FULL_PWR_CYCLE) || !is_suspend))
>>> err = mmc_poweroff_notify(host->card, notify_type);
>>> - else if (mmc_can_sleep(host->card))
>>> + else if (mmc_can_sleep(host->card)) {
>>> + if (host->card->quirks &
>>> MMC_QUIRK_NOTIFY_POWEROFF_ON_SLEEP) {
>>> + err = mmc_poweroff_notify(host->card,
>>> notify_type);
>>> + if (err)
>>> + goto out;
>>> + }
>>
>>
>> So, I am curious to know from a power management point of view; how
>> does the card behave comparing the sleep and power off notification
>> command?
>>
>> Is the card in a low power state after the power off notification has
>> been received? If so, did you manage to do some measurement for that
>> or perhaps the data-sheet tells about this? It would be interesting to
>> know if there were any differences between sleep and power off
>> notification in this regards.
>
> I do not have any clue about that. It appears only with Hynix emmc and the
> fix has been approved by Hynix engineers... It seems that if not powered
> off, the firmware does some garbage collection when resuming and it takes
> more time...
Okay, I see. So before I continue reviewing, can you please also tell
what regulators to the card that is being cut while powering off in
this path.
VMMC, VQMMC?
Kind regards
Uffe
^ permalink raw reply
* Re: [PATCH 1/2] mmc: mxs: Initialize the spinlock prior to using it
From: Marek Vasut @ 2016-10-27 19:56 UTC (permalink / raw)
To: Fabio Estevam
Cc: Ulf Hansson, Sascha Hauer, linux-mmc@vger.kernel.org,
Stefan Wahren, Fabio Estevam
In-Reply-To: <CAOMZO5A7CS-Q8FJmkJw3xy9SsF_9mLR=r77j8Kk5=5bPaWgV4A@mail.gmail.com>
On 10/27/2016 09:17 PM, Fabio Estevam wrote:
> Hi Marek,
>
> On Thu, Oct 27, 2016 at 5:06 PM, Marek Vasut <marex@denx.de> wrote:
>
>> Nice, thanks.
>>
>> Could you also check whether you can disable and clear interrupts in the
>> probe routine ? I think that might make sense too.
>
> Yes, that's a good idea. I will look into that after Ulf applies this
> series. Thanks
>
Cool, thanks!
--
Best regards,
Marek Vasut
^ permalink raw reply
* Re: [PATCH 2/2] mmc: mxs: Use the spinlock irq variants
From: Jisheng Zhang @ 2016-10-28 2:23 UTC (permalink / raw)
To: Fabio Estevam
Cc: ulf.hansson, kernel, linux-mmc, stefan.wahren, marex,
Fabio Estevam
In-Reply-To: <1477594672-31611-2-git-send-email-festevam@gmail.com>
On Thu, 27 Oct 2016 16:57:52 -0200 Fabio Estevam wrote:
> From: Fabio Estevam <fabio.estevam@nxp.com>
>
> Inside an interrupt handler we should use the spin_lock_irqsave()/
> spin_unlock_irqrestore() variants, so fix it accordingly.
hmm, in interrupt handler the irq is disabled, so IMHO there's no need to
use irqsave/irqrestore spinlock variants.
Thanks,
Jisheng
>
> Signed-off-by: Fabio Estevam <fabio.estevam@nxp.com>
> ---
> drivers/mmc/host/mxs-mmc.c | 5 +++--
> 1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/mxs-mmc.c b/drivers/mmc/host/mxs-mmc.c
> index 44ecebd..14b7548 100644
> --- a/drivers/mmc/host/mxs-mmc.c
> +++ b/drivers/mmc/host/mxs-mmc.c
> @@ -189,15 +189,16 @@ static irqreturn_t mxs_mmc_irq_handler(int irq, void *dev_id)
> struct mmc_command *cmd = host->cmd;
> struct mmc_data *data = host->data;
> struct mxs_ssp *ssp = &host->ssp;
> + unsigned long flags;
> u32 stat;
>
> - spin_lock(&host->lock);
> + spin_lock_irqsave(&host->lock, flags);
>
> stat = readl(ssp->base + HW_SSP_CTRL1(ssp));
> writel(stat & MXS_MMC_IRQ_BITS,
> ssp->base + HW_SSP_CTRL1(ssp) + STMP_OFFSET_REG_CLR);
>
> - spin_unlock(&host->lock);
> + spin_unlock_irqrestore(&host->lock, flags);
>
> if ((stat & BM_SSP_CTRL1_SDIO_IRQ) && (stat & BM_SSP_CTRL1_SDIO_IRQ_EN))
> mmc_signal_sdio_irq(host->mmc);
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox