diff options
author | Waldemar Brodkorb <wbx@openadk.org> | 2015-03-02 18:54:09 +0100 |
---|---|---|
committer | Waldemar Brodkorb <wbx@openadk.org> | 2015-03-02 18:54:09 +0100 |
commit | fde3043e3f9cc58b2b4639f2bb2f6d1ac6b15ed2 (patch) | |
tree | 59dce32391123f41066e4987b60864c3e05fbbc0 /target/arm | |
parent | 4876df8cb79b2bac3ba9ae02b3609f8d63309c16 (diff) |
add wlan support for 3.18.x via Russel King patches. 0086-0090
Diffstat (limited to 'target/arm')
-rw-r--r-- | target/arm/solidrun-imx6/patches/3.18.8/solidrun-imx6-wlan.patch | 3252 |
1 files changed, 3252 insertions, 0 deletions
diff --git a/target/arm/solidrun-imx6/patches/3.18.8/solidrun-imx6-wlan.patch b/target/arm/solidrun-imx6/patches/3.18.8/solidrun-imx6-wlan.patch new file mode 100644 index 000000000..3ab3081db --- /dev/null +++ b/target/arm/solidrun-imx6/patches/3.18.8/solidrun-imx6-wlan.patch @@ -0,0 +1,3252 @@ +diff -Nur linux-3.18.8.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi linux-3.18.8/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi +--- linux-3.18.8.orig/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2015-02-27 02:49:36.000000000 +0100 ++++ linux-3.18.8/arch/arm/boot/dts/imx6qdl-cubox-i.dtsi 2015-03-02 03:23:14.000000000 +0100 +@@ -170,6 +170,28 @@ + MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x13059 + >; + }; ++ ++ pinctrl_cubox_i_usdhc2_100mhz: cubox-i-usdhc2-100mhz { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170b9 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100b9 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170b9 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170b9 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170b9 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130b9 ++ >; ++ }; ++ ++ pinctrl_cubox_i_usdhc2_200mhz: cubox-i-usdhc2-200mhz { ++ fsl,pins = < ++ MX6QDL_PAD_SD2_CMD__SD2_CMD 0x170f9 ++ MX6QDL_PAD_SD2_CLK__SD2_CLK 0x100f9 ++ MX6QDL_PAD_SD2_DAT0__SD2_DATA0 0x170f9 ++ MX6QDL_PAD_SD2_DAT1__SD2_DATA1 0x170f9 ++ MX6QDL_PAD_SD2_DAT2__SD2_DATA2 0x170f9 ++ MX6QDL_PAD_SD2_DAT3__SD2_DATA3 0x130f9 ++ >; ++ }; + }; + }; + +@@ -194,8 +216,10 @@ + }; + + &usdhc2 { +- pinctrl-names = "default"; ++ pinctrl-names = "default", "state_100mhz", "state_200mhz"; + pinctrl-0 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2>; ++ pinctrl-1 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_100mhz>; ++ pinctrl-2 = <&pinctrl_cubox_i_usdhc2_aux &pinctrl_cubox_i_usdhc2_200mhz>; + vmmc-supply = <®_3p3v>; + cd-gpios = <&gpio1 4 0>; + status = "okay"; +diff -Nur linux-3.18.8.orig/arch/arm/boot/dts/imx6qdl-microsom.dtsi linux-3.18.8/arch/arm/boot/dts/imx6qdl-microsom.dtsi +--- linux-3.18.8.orig/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2015-02-27 02:49:36.000000000 +0100 ++++ linux-3.18.8/arch/arm/boot/dts/imx6qdl-microsom.dtsi 2015-03-02 02:58:12.000000000 +0100 +@@ -1,15 +1,95 @@ + /* + * Copyright (C) 2013,2014 Russell King + */ ++#include <dt-bindings/gpio/gpio.h> ++/ { ++ regulators { ++ compatible = "simple-bus"; ++ ++ reg_brcm_osc: brcm-osc-reg { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio5 5 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_osc_reg>; ++ regulator-name = "brcm_osc_reg"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ regulator-always-on; ++ regulator-boot-on; ++ }; ++ ++ reg_brcm: brcm-reg { ++ compatible = "regulator-fixed"; ++ enable-active-high; ++ gpio = <&gpio3 19 0>; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_reg>; ++ regulator-name = "brcm_reg"; ++ regulator-min-microvolt = <3300000>; ++ regulator-max-microvolt = <3300000>; ++ startup-delay-us = <200000>; ++ }; ++ }; ++}; + + &iomuxc { + microsom { ++ pinctrl_microsom_brcm_bt: microsom-brcm-bt { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT14__GPIO6_IO00 0x40013070 ++ MX6QDL_PAD_CSI0_DAT15__GPIO6_IO01 0x40013070 ++ MX6QDL_PAD_CSI0_DAT18__GPIO6_IO04 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_osc_reg: microsom-brcm-osc-reg { ++ fsl,pins = < ++ MX6QDL_PAD_DISP0_DAT11__GPIO5_IO05 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_reg: microsom-brcm-reg { ++ fsl,pins = < ++ MX6QDL_PAD_EIM_D19__GPIO3_IO19 0x40013070 ++ >; ++ }; ++ ++ pinctrl_microsom_brcm_wifi: microsom-brcm-wifi { ++ fsl,pins = < ++ MX6QDL_PAD_GPIO_8__XTALOSC_REF_CLK_32K 0x1b0b0 ++ MX6QDL_PAD_CSI0_DATA_EN__GPIO5_IO20 0x40013070 ++ MX6QDL_PAD_CSI0_DAT8__GPIO5_IO26 0x40013070 ++ MX6QDL_PAD_CSI0_DAT9__GPIO5_IO27 0x40013070 ++ >; ++ }; ++ + pinctrl_microsom_uart1: microsom-uart1 { + fsl,pins = < + MX6QDL_PAD_CSI0_DAT10__UART1_TX_DATA 0x1b0b1 + MX6QDL_PAD_CSI0_DAT11__UART1_RX_DATA 0x1b0b1 + >; + }; ++ ++ pinctrl_microsom_uart4_1: microsom-uart4 { ++ fsl,pins = < ++ MX6QDL_PAD_CSI0_DAT12__UART4_TX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT13__UART4_RX_DATA 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT16__UART4_RTS_B 0x1b0b1 ++ MX6QDL_PAD_CSI0_DAT17__UART4_CTS_B 0x1b0b1 ++ >; ++ }; ++ ++ pinctrl_microsom_usdhc1: microsom-usdhc1 { ++ fsl,pins = < ++ MX6QDL_PAD_SD1_CMD__SD1_CMD 0x17059 ++ MX6QDL_PAD_SD1_CLK__SD1_CLK 0x10059 ++ MX6QDL_PAD_SD1_DAT0__SD1_DATA0 0x17059 ++ MX6QDL_PAD_SD1_DAT1__SD1_DATA1 0x17059 ++ MX6QDL_PAD_SD1_DAT2__SD1_DATA2 0x17059 ++ MX6QDL_PAD_SD1_DAT3__SD1_DATA3 0x17059 ++ >; ++ }; + }; + }; + +@@ -18,3 +98,23 @@ + pinctrl-0 = <&pinctrl_microsom_uart1>; + status = "okay"; + }; ++ ++/* UART4 - Connected to optional BRCM Wifi/BT/FM */ ++&uart4 { ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_bt &pinctrl_microsom_uart4_1>; ++ fsl,uart-has-rtscts; ++ status = "okay"; ++}; ++ ++/* USDHC1 - Connected to optional BRCM Wifi/BT/FM */ ++&usdhc1 { ++ card-external-vcc-supply = <®_brcm>; ++ card-reset-gpios = <&gpio5 26 GPIO_ACTIVE_LOW>, <&gpio6 0 GPIO_ACTIVE_LOW>; ++ keep-power-in-suspend; ++ non-removable; ++ pinctrl-names = "default"; ++ pinctrl-0 = <&pinctrl_microsom_brcm_wifi &pinctrl_microsom_usdhc1>; ++ vmmc-supply = <®_brcm>; ++ status = "okay"; ++}; +diff -Nur linux-3.18.8.orig/Documentation/devicetree/bindings/mmc/mmc.txt linux-3.18.8/Documentation/devicetree/bindings/mmc/mmc.txt +--- linux-3.18.8.orig/Documentation/devicetree/bindings/mmc/mmc.txt 2015-02-27 02:49:36.000000000 +0100 ++++ linux-3.18.8/Documentation/devicetree/bindings/mmc/mmc.txt 2015-03-02 03:25:33.000000000 +0100 +@@ -5,6 +5,8 @@ + Interpreted by the OF core: + - reg: Registers location and length. + - interrupts: Interrupts used by the MMC controller. ++- clocks: Clocks needed for the host controller, if any. ++- clock-names: Goes with clocks above. + + Card detection: + If no property below is supplied, host native card detect is used. +@@ -43,6 +45,15 @@ + - dsr: Value the card's (optional) Driver Stage Register (DSR) should be + programmed with. Valid range: [0 .. 0xffff]. + ++Card power and reset control: ++The following properties can be specified for cases where the MMC ++peripheral needs additional reset, regulator and clock lines. It is for ++example common for WiFi/BT adapters to have these separate from the main ++MMC bus: ++ - card-reset-gpios: Specify GPIOs for card reset (reset active low) ++ - card-external-vcc-supply: Regulator to drive (independent) card VCC ++ - clock with name "card_ext_clock": External clock provided to the card ++ + *NOTE* on CD and WP polarity. To use common for all SD/MMC host controllers line + polarity properties, we have to fix the meaning of the "normal" and "inverted" + line levels. We choose to follow the SDHCI standard, which specifies both those +diff -Nur linux-3.18.8.orig/drivers/mmc/core/core.c linux-3.18.8/drivers/mmc/core/core.c +--- linux-3.18.8.orig/drivers/mmc/core/core.c 2015-02-27 02:49:36.000000000 +0100 ++++ linux-3.18.8/drivers/mmc/core/core.c 2015-03-02 03:25:33.000000000 +0100 +@@ -13,11 +13,13 @@ + #include <linux/module.h> + #include <linux/init.h> + #include <linux/interrupt.h> ++#include <linux/clk.h> + #include <linux/completion.h> + #include <linux/device.h> + #include <linux/delay.h> + #include <linux/pagemap.h> + #include <linux/err.h> ++#include <linux/gpio/consumer.h> + #include <linux/leds.h> + #include <linux/scatterlist.h> + #include <linux/log2.h> +@@ -1507,6 +1509,43 @@ + mmc_host_clk_release(host); + } + ++static void mmc_card_power_up(struct mmc_host *host) ++{ ++ int i; ++ struct gpio_desc **gds = host->card_reset_gpios; ++ ++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { ++ if (gds[i]) { ++ dev_dbg(host->parent, "Asserting reset line %d", i); ++ gpiod_set_value(gds[i], 1); ++ } ++ } ++ ++ if (host->card_regulator) { ++ dev_dbg(host->parent, "Enabling external regulator"); ++ if (regulator_enable(host->card_regulator)) ++ dev_err(host->parent, "Failed to enable external regulator"); ++ } ++ ++ if (host->card_clk) { ++ dev_dbg(host->parent, "Enabling external clock"); ++ clk_prepare_enable(host->card_clk); ++ } ++ ++ /* 2ms delay to let clocks and power settle */ ++ mmc_delay(20); ++ ++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { ++ if (gds[i]) { ++ dev_dbg(host->parent, "Deasserting reset line %d", i); ++ gpiod_set_value(gds[i], 0); ++ } ++ } ++ ++ /* 2ms delay to after reset release */ ++ mmc_delay(20); ++} ++ + /* + * Apply power to the MMC stack. This is a two-stage process. + * First, we enable power to the card without the clock running. +@@ -1523,6 +1562,9 @@ + if (host->ios.power_mode == MMC_POWER_ON) + return; + ++ /* Power up the card/module first, if needed */ ++ mmc_card_power_up(host); ++ + mmc_host_clk_hold(host); + + host->ios.vdd = fls(ocr) - 1; +diff -Nur linux-3.18.8.orig/drivers/mmc/core/host.c linux-3.18.8/drivers/mmc/core/host.c +--- linux-3.18.8.orig/drivers/mmc/core/host.c 2015-02-27 02:49:36.000000000 +0100 ++++ linux-3.18.8/drivers/mmc/core/host.c 2015-03-02 03:26:23.000000000 +0100 +@@ -12,14 +12,18 @@ + * MMC host class device management + */ + ++#include <linux/kernel.h> ++#include <linux/clk.h> + #include <linux/device.h> + #include <linux/err.h> ++#include <linux/gpio/consumer.h> + #include <linux/idr.h> + #include <linux/of.h> + #include <linux/of_gpio.h> + #include <linux/pagemap.h> + #include <linux/export.h> + #include <linux/leds.h> ++#include <linux/regulator/consumer.h> + #include <linux/slab.h> + #include <linux/suspend.h> + +@@ -466,6 +470,66 @@ + + EXPORT_SYMBOL(mmc_of_parse); + ++static int mmc_of_parse_child(struct mmc_host *host) ++{ ++ struct device_node *np; ++ struct clk *clk; ++ int i; ++ ++ if (!host->parent || !host->parent->of_node) ++ return 0; ++ ++ np = host->parent->of_node; ++ ++ host->card_regulator = regulator_get(host->parent, "card-external-vcc"); ++ if (IS_ERR(host->card_regulator)) { ++ if (PTR_ERR(host->card_regulator) == -EPROBE_DEFER) ++ return PTR_ERR(host->card_regulator); ++ host->card_regulator = NULL; ++ } ++ ++ /* Parse card power/reset/clock control */ ++ if (of_find_property(np, "card-reset-gpios", NULL)) { ++ struct gpio_desc *gpd; ++ int level = 0; ++ ++ /* ++ * If the regulator is enabled, then we can hold the ++ * card in reset with an active high resets. Otherwise, ++ * hold the resets low. ++ */ ++ if (host->card_regulator && regulator_is_enabled(host->card_regulator)) ++ level = 1; ++ ++ for (i = 0; i < ARRAY_SIZE(host->card_reset_gpios); i++) { ++ gpd = devm_gpiod_get_index(host->parent, "card-reset", i); ++ if (IS_ERR(gpd)) { ++ if (PTR_ERR(gpd) == -EPROBE_DEFER) ++ return PTR_ERR(gpd); ++ break; ++ } ++ gpiod_direction_output(gpd, gpiod_is_active_low(gpd) | level); ++ host->card_reset_gpios[i] = gpd; ++ } ++ ++ gpd = devm_gpiod_get_index(host->parent, "card-reset", ARRAY_SIZE(host->card_reset_gpios)); ++ if (!IS_ERR(gpd)) { ++ dev_warn(host->parent, "More reset gpios than we can handle"); ++ gpiod_put(gpd); ++ } ++ } ++ ++ clk = of_clk_get_by_name(np, "card_ext_clock"); ++ if (IS_ERR(clk)) { ++ if (PTR_ERR(clk) == -EPROBE_DEFER) ++ return PTR_ERR(clk); ++ clk = NULL; ++ } ++ host->card_clk = clk; ++ ++ return 0; ++} ++ + /** + * mmc_alloc_host - initialise the per-host structure. + * @extra: sizeof private data structure +@@ -545,6 +609,10 @@ + { + int err; + ++ err = mmc_of_parse_child(host); ++ if (err) ++ return err; ++ + WARN_ON((host->caps & MMC_CAP_SDIO_IRQ) && + !host->ops->enable_sdio_irq); + +diff -Nur linux-3.18.8.orig/drivers/mmc/host/dw_mmc.c linux-3.18.8/drivers/mmc/host/dw_mmc.c +--- linux-3.18.8.orig/drivers/mmc/host/dw_mmc.c 2015-02-27 02:49:36.000000000 +0100 ++++ linux-3.18.8/drivers/mmc/host/dw_mmc.c 2015-03-02 03:25:56.000000000 +0100 +@@ -2211,6 +2211,8 @@ + if (!mmc) + return -ENOMEM; + ++ mmc_of_parse(mmc); ++ + slot = mmc_priv(mmc); + slot->id = id; + slot->mmc = mmc; +diff -Nur linux-3.18.8.orig/drivers/mmc/host/dw_mmc.c.orig linux-3.18.8/drivers/mmc/host/dw_mmc.c.orig +--- linux-3.18.8.orig/drivers/mmc/host/dw_mmc.c.orig 1970-01-01 01:00:00.000000000 +0100 ++++ linux-3.18.8/drivers/mmc/host/dw_mmc.c.orig 2015-02-27 02:49:36.000000000 +0100 +@@ -0,0 +1,2855 @@ ++/* ++ * Synopsys DesignWare Multimedia Card Interface driver ++ * (Based on NXP driver for lpc 31xx) ++ * ++ * Copyright (C) 2009 NXP Semiconductors ++ * Copyright (C) 2009, 2010 Imagination Technologies Ltd. ++ * ++ * 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/blkdev.h> ++#include <linux/clk.h> ++#include <linux/debugfs.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/seq_file.h> ++#include <linux/slab.h> ++#include <linux/stat.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/mmc/host.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/sd.h> ++#include <linux/mmc/sdio.h> ++#include <linux/mmc/dw_mmc.h> ++#include <linux/bitops.h> ++#include <linux/regulator/consumer.h> ++#include <linux/workqueue.h> ++#include <linux/of.h> ++#include <linux/of_gpio.h> ++#include <linux/mmc/slot-gpio.h> ++ ++#include "dw_mmc.h" ++ ++/* Common flag combinations */ ++#define DW_MCI_DATA_ERROR_FLAGS (SDMMC_INT_DRTO | SDMMC_INT_DCRC | \ ++ SDMMC_INT_HTO | SDMMC_INT_SBE | \ ++ SDMMC_INT_EBE) ++#define DW_MCI_CMD_ERROR_FLAGS (SDMMC_INT_RTO | SDMMC_INT_RCRC | \ ++ SDMMC_INT_RESP_ERR) ++#define DW_MCI_ERROR_FLAGS (DW_MCI_DATA_ERROR_FLAGS | \ ++ DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_HLE) ++#define DW_MCI_SEND_STATUS 1 ++#define DW_MCI_RECV_STATUS 2 ++#define DW_MCI_DMA_THRESHOLD 16 ++ ++#define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ ++#define DW_MCI_FREQ_MIN 400000 /* unit: HZ */ ++ ++#ifdef CONFIG_MMC_DW_IDMAC ++#define IDMAC_INT_CLR (SDMMC_IDMAC_INT_AI | SDMMC_IDMAC_INT_NI | \ ++ SDMMC_IDMAC_INT_CES | SDMMC_IDMAC_INT_DU | \ ++ SDMMC_IDMAC_INT_FBE | SDMMC_IDMAC_INT_RI | \ ++ SDMMC_IDMAC_INT_TI) ++ ++struct idmac_desc { ++ u32 des0; /* Control Descriptor */ ++#define IDMAC_DES0_DIC BIT(1) ++#define IDMAC_DES0_LD BIT(2) ++#define IDMAC_DES0_FD BIT(3) ++#define IDMAC_DES0_CH BIT(4) ++#define IDMAC_DES0_ER BIT(5) ++#define IDMAC_DES0_CES BIT(30) ++#define IDMAC_DES0_OWN BIT(31) ++ ++ u32 des1; /* Buffer sizes */ ++#define IDMAC_SET_BUFFER1_SIZE(d, s) \ ++ ((d)->des1 = ((d)->des1 & 0x03ffe000) | ((s) & 0x1fff)) ++ ++ u32 des2; /* buffer 1 physical address */ ++ ++ u32 des3; /* buffer 2 physical address */ ++}; ++#endif /* CONFIG_MMC_DW_IDMAC */ ++ ++static bool dw_mci_reset(struct dw_mci *host); ++ ++#if defined(CONFIG_DEBUG_FS) ++static int dw_mci_req_show(struct seq_file *s, void *v) ++{ ++ struct dw_mci_slot *slot = s->private; ++ struct mmc_request *mrq; ++ struct mmc_command *cmd; ++ struct mmc_command *stop; ++ struct mmc_data *data; ++ ++ /* Make sure we get a consistent snapshot */ ++ spin_lock_bh(&slot->host->lock); ++ mrq = slot->mrq; ++ ++ if (mrq) { ++ cmd = mrq->cmd; ++ data = mrq->data; ++ stop = mrq->stop; ++ ++ if (cmd) ++ seq_printf(s, ++ "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n", ++ cmd->opcode, cmd->arg, cmd->flags, ++ cmd->resp[0], cmd->resp[1], cmd->resp[2], ++ cmd->resp[2], cmd->error); ++ if (data) ++ seq_printf(s, "DATA %u / %u * %u flg %x err %d\n", ++ data->bytes_xfered, data->blocks, ++ data->blksz, data->flags, data->error); ++ if (stop) ++ seq_printf(s, ++ "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n", ++ stop->opcode, stop->arg, stop->flags, ++ stop->resp[0], stop->resp[1], stop->resp[2], ++ stop->resp[2], stop->error); ++ } ++ ++ spin_unlock_bh(&slot->host->lock); ++ ++ return 0; ++} ++ ++static int dw_mci_req_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_mci_req_show, inode->i_private); ++} ++ ++static const struct file_operations dw_mci_req_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_mci_req_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int dw_mci_regs_show(struct seq_file *s, void *v) ++{ ++ seq_printf(s, "STATUS:\t0x%08x\n", SDMMC_STATUS); ++ seq_printf(s, "RINTSTS:\t0x%08x\n", SDMMC_RINTSTS); ++ seq_printf(s, "CMD:\t0x%08x\n", SDMMC_CMD); ++ seq_printf(s, "CTRL:\t0x%08x\n", SDMMC_CTRL); ++ seq_printf(s, "INTMASK:\t0x%08x\n", SDMMC_INTMASK); ++ seq_printf(s, "CLKENA:\t0x%08x\n", SDMMC_CLKENA); ++ ++ return 0; ++} ++ ++static int dw_mci_regs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, dw_mci_regs_show, inode->i_private); ++} ++ ++static const struct file_operations dw_mci_regs_fops = { ++ .owner = THIS_MODULE, ++ .open = dw_mci_regs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static void dw_mci_init_debugfs(struct dw_mci_slot *slot) ++{ ++ struct mmc_host *mmc = slot->mmc; ++ struct dw_mci *host = slot->host; ++ struct dentry *root; ++ struct dentry *node; ++ ++ root = mmc->debugfs_root; ++ if (!root) ++ return; ++ ++ node = debugfs_create_file("regs", S_IRUSR, root, host, ++ &dw_mci_regs_fops); ++ if (!node) ++ goto err; ++ ++ node = debugfs_create_file("req", S_IRUSR, root, slot, ++ &dw_mci_req_fops); ++ if (!node) ++ goto err; ++ ++ node = debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state); ++ if (!node) ++ goto err; ++ ++ node = debugfs_create_x32("pending_events", S_IRUSR, root, ++ (u32 *)&host->pending_events); ++ if (!node) ++ goto err; ++ ++ node = debugfs_create_x32("completed_events", S_IRUSR, root, ++ (u32 *)&host->completed_events); ++ if (!node) ++ goto err; ++ ++ return; ++ ++err: ++ dev_err(&mmc->class_dev, "failed to initialize debugfs for slot\n"); ++} ++#endif /* defined(CONFIG_DEBUG_FS) */ ++ ++static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg); ++ ++static u32 dw_mci_prepare_command(struct mmc_host *mmc, struct mmc_command *cmd) ++{ ++ struct mmc_data *data; ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct dw_mci *host = slot->host; ++ const struct dw_mci_drv_data *drv_data = slot->host->drv_data; ++ u32 cmdr; ++ cmd->error = -EINPROGRESS; ++ ++ cmdr = cmd->opcode; ++ ++ if (cmd->opcode == MMC_STOP_TRANSMISSION || ++ cmd->opcode == MMC_GO_IDLE_STATE || ++ cmd->opcode == MMC_GO_INACTIVE_STATE || ++ (cmd->opcode == SD_IO_RW_DIRECT && ++ ((cmd->arg >> 9) & 0x1FFFF) == SDIO_CCCR_ABORT)) ++ cmdr |= SDMMC_CMD_STOP; ++ else if (cmd->opcode != MMC_SEND_STATUS && cmd->data) ++ cmdr |= SDMMC_CMD_PRV_DAT_WAIT; ++ ++ if (cmd->opcode == SD_SWITCH_VOLTAGE) { ++ u32 clk_en_a; ++ ++ /* Special bit makes CMD11 not die */ ++ cmdr |= SDMMC_CMD_VOLT_SWITCH; ++ ++ /* Change state to continue to handle CMD11 weirdness */ ++ WARN_ON(slot->host->state != STATE_SENDING_CMD); ++ slot->host->state = STATE_SENDING_CMD11; ++ ++ /* ++ * We need to disable low power mode (automatic clock stop) ++ * while doing voltage switch so we don't confuse the card, ++ * since stopping the clock is a specific part of the UHS ++ * voltage change dance. ++ * ++ * Note that low power mode (SDMMC_CLKEN_LOW_PWR) will be ++ * unconditionally turned back on in dw_mci_setup_bus() if it's ++ * ever called with a non-zero clock. That shouldn't happen ++ * until the voltage change is all done. ++ */ ++ clk_en_a = mci_readl(host, CLKENA); ++ clk_en_a &= ~(SDMMC_CLKEN_LOW_PWR << slot->id); ++ mci_writel(host, CLKENA, clk_en_a); ++ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | ++ SDMMC_CMD_PRV_DAT_WAIT, 0); ++ } ++ ++ if (cmd->flags & MMC_RSP_PRESENT) { ++ /* We expect a response, so set this bit */ ++ cmdr |= SDMMC_CMD_RESP_EXP; ++ if (cmd->flags & MMC_RSP_136) ++ cmdr |= SDMMC_CMD_RESP_LONG; ++ } ++ ++ if (cmd->flags & MMC_RSP_CRC) ++ cmdr |= SDMMC_CMD_RESP_CRC; ++ ++ data = cmd->data; ++ if (data) { ++ cmdr |= SDMMC_CMD_DAT_EXP; ++ if (data->flags & MMC_DATA_STREAM) ++ cmdr |= SDMMC_CMD_STRM_MODE; ++ if (data->flags & MMC_DATA_WRITE) ++ cmdr |= SDMMC_CMD_DAT_WR; ++ } ++ ++ if (drv_data && drv_data->prepare_command) ++ drv_data->prepare_command(slot->host, &cmdr); ++ ++ return cmdr; ++} ++ ++static u32 dw_mci_prep_stop_abort(struct dw_mci *host, struct mmc_command *cmd) ++{ ++ struct mmc_command *stop; ++ u32 cmdr; ++ ++ if (!cmd->data) ++ return 0; ++ ++ stop = &host->stop_abort; ++ cmdr = cmd->opcode; ++ memset(stop, 0, sizeof(struct mmc_command)); ++ ++ if (cmdr == MMC_READ_SINGLE_BLOCK || ++ cmdr == MMC_READ_MULTIPLE_BLOCK || ++ cmdr == MMC_WRITE_BLOCK || ++ cmdr == MMC_WRITE_MULTIPLE_BLOCK) { ++ stop->opcode = MMC_STOP_TRANSMISSION; ++ stop->arg = 0; ++ stop->flags = MMC_RSP_R1B | MMC_CMD_AC; ++ } else if (cmdr == SD_IO_RW_EXTENDED) { ++ stop->opcode = SD_IO_RW_DIRECT; ++ stop->arg |= (1 << 31) | (0 << 28) | (SDIO_CCCR_ABORT << 9) | ++ ((cmd->arg >> 28) & 0x7); ++ stop->flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; ++ } else { ++ return 0; ++ } ++ ++ cmdr = stop->opcode | SDMMC_CMD_STOP | ++ SDMMC_CMD_RESP_CRC | SDMMC_CMD_RESP_EXP; ++ ++ return cmdr; ++} ++ ++static void dw_mci_start_command(struct dw_mci *host, ++ struct mmc_command *cmd, u32 cmd_flags) ++{ ++ host->cmd = cmd; ++ dev_vdbg(host->dev, ++ "start command: ARGR=0x%08x CMDR=0x%08x\n", ++ cmd->arg, cmd_flags); ++ ++ mci_writel(host, CMDARG, cmd->arg); ++ wmb(); ++ ++ mci_writel(host, CMD, cmd_flags | SDMMC_CMD_START); ++} ++ ++static inline void send_stop_abort(struct dw_mci *host, struct mmc_data *data) ++{ ++ struct mmc_command *stop = data->stop ? data->stop : &host->stop_abort; ++ dw_mci_start_command(host, stop, host->stop_cmdr); ++} ++ ++/* DMA interface functions */ ++static void dw_mci_stop_dma(struct dw_mci *host) ++{ ++ if (host->using_dma) { ++ host->dma_ops->stop(host); ++ host->dma_ops->cleanup(host); ++ } ++ ++ /* Data transfer was stopped by the interrupt handler */ ++ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); ++} ++ ++static int dw_mci_get_dma_dir(struct mmc_data *data) ++{ ++ if (data->flags & MMC_DATA_WRITE) ++ return DMA_TO_DEVICE; ++ else ++ return DMA_FROM_DEVICE; ++} ++ ++#ifdef CONFIG_MMC_DW_IDMAC ++static void dw_mci_dma_cleanup(struct dw_mci *host) ++{ ++ struct mmc_data *data = host->data; ++ ++ if (data) ++ if (!data->host_cookie) ++ dma_unmap_sg(host->dev, ++ data->sg, ++ data->sg_len, ++ dw_mci_get_dma_dir(data)); ++} ++ ++static void dw_mci_idmac_reset(struct dw_mci *host) ++{ ++ u32 bmod = mci_readl(host, BMOD); ++ /* Software reset of DMA */ ++ bmod |= SDMMC_IDMAC_SWRESET; ++ mci_writel(host, BMOD, bmod); ++} ++ ++static void dw_mci_idmac_stop_dma(struct dw_mci *host) ++{ ++ u32 temp; ++ ++ /* Disable and reset the IDMAC interface */ ++ temp = mci_readl(host, CTRL); ++ temp &= ~SDMMC_CTRL_USE_IDMAC; ++ temp |= SDMMC_CTRL_DMA_RESET; ++ mci_writel(host, CTRL, temp); ++ ++ /* Stop the IDMAC running */ ++ temp = mci_readl(host, BMOD); ++ temp &= ~(SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB); ++ temp |= SDMMC_IDMAC_SWRESET; ++ mci_writel(host, BMOD, temp); ++} ++ ++static void dw_mci_idmac_complete_dma(struct dw_mci *host) ++{ ++ struct mmc_data *data = host->data; ++ ++ dev_vdbg(host->dev, "DMA complete\n"); ++ ++ host->dma_ops->cleanup(host); ++ ++ /* ++ * If the card was removed, data will be NULL. No point in trying to ++ * send the stop command or waiting for NBUSY in this case. ++ */ ++ if (data) { ++ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); ++ tasklet_schedule(&host->tasklet); ++ } ++} ++ ++static void dw_mci_translate_sglist(struct dw_mci *host, struct mmc_data *data, ++ unsigned int sg_len) ++{ ++ int i; ++ struct idmac_desc *desc = host->sg_cpu; ++ ++ for (i = 0; i < sg_len; i++, desc++) { ++ unsigned int length = sg_dma_len(&data->sg[i]); ++ u32 mem_addr = sg_dma_address(&data->sg[i]); ++ ++ /* Set the OWN bit and disable interrupts for this descriptor */ ++ desc->des0 = IDMAC_DES0_OWN | IDMAC_DES0_DIC | IDMAC_DES0_CH; ++ ++ /* Buffer length */ ++ IDMAC_SET_BUFFER1_SIZE(desc, length); ++ ++ /* Physical address to DMA to/from */ ++ desc->des2 = mem_addr; ++ } ++ ++ /* Set first descriptor */ ++ desc = host->sg_cpu; ++ desc->des0 |= IDMAC_DES0_FD; ++ ++ /* Set last descriptor */ ++ desc = host->sg_cpu + (i - 1) * sizeof(struct idmac_desc); ++ desc->des0 &= ~(IDMAC_DES0_CH | IDMAC_DES0_DIC); ++ desc->des0 |= IDMAC_DES0_LD; ++ ++ wmb(); ++} ++ ++static void dw_mci_idmac_start_dma(struct dw_mci *host, unsigned int sg_len) ++{ ++ u32 temp; ++ ++ dw_mci_translate_sglist(host, host->data, sg_len); ++ ++ /* Select IDMAC interface */ ++ temp = mci_readl(host, CTRL); ++ temp |= SDMMC_CTRL_USE_IDMAC; ++ mci_writel(host, CTRL, temp); ++ ++ wmb(); ++ ++ /* Enable the IDMAC */ ++ temp = mci_readl(host, BMOD); ++ temp |= SDMMC_IDMAC_ENABLE | SDMMC_IDMAC_FB; ++ mci_writel(host, BMOD, temp); ++ ++ /* Start it running */ ++ mci_writel(host, PLDMND, 1); ++} ++ ++static int dw_mci_idmac_init(struct dw_mci *host) ++{ ++ struct idmac_desc *p; ++ int i; ++ ++ /* Number of descriptors in the ring buffer */ ++ host->ring_size = PAGE_SIZE / sizeof(struct idmac_desc); ++ ++ /* Forward link the descriptor list */ ++ for (i = 0, p = host->sg_cpu; i < host->ring_size - 1; i++, p++) ++ p->des3 = host->sg_dma + (sizeof(struct idmac_desc) * (i + 1)); ++ ++ /* Set the last descriptor as the end-of-ring descriptor */ ++ p->des3 = host->sg_dma; ++ p->des0 = IDMAC_DES0_ER; ++ ++ dw_mci_idmac_reset(host); ++ ++ /* Mask out interrupts - get Tx & Rx complete only */ ++ mci_writel(host, IDSTS, IDMAC_INT_CLR); ++ mci_writel(host, IDINTEN, SDMMC_IDMAC_INT_NI | SDMMC_IDMAC_INT_RI | ++ SDMMC_IDMAC_INT_TI); ++ ++ /* Set the descriptor base address */ ++ mci_writel(host, DBADDR, host->sg_dma); ++ return 0; ++} ++ ++static const struct dw_mci_dma_ops dw_mci_idmac_ops = { ++ .init = dw_mci_idmac_init, ++ .start = dw_mci_idmac_start_dma, ++ .stop = dw_mci_idmac_stop_dma, ++ .complete = dw_mci_idmac_complete_dma, ++ .cleanup = dw_mci_dma_cleanup, ++}; ++#endif /* CONFIG_MMC_DW_IDMAC */ ++ ++static int dw_mci_pre_dma_transfer(struct dw_mci *host, ++ struct mmc_data *data, ++ bool next) ++{ ++ struct scatterlist *sg; ++ unsigned int i, sg_len; ++ ++ if (!next && data->host_cookie) ++ return data->host_cookie; ++ ++ /* ++ * We don't do DMA on "complex" transfers, i.e. with ++ * non-word-aligned buffers or lengths. Also, we don't bother ++ * with all the DMA setup overhead for short transfers. ++ */ ++ if (data->blocks * data->blksz < DW_MCI_DMA_THRESHOLD) ++ return -EINVAL; ++ ++ if (data->blksz & 3) ++ return -EINVAL; ++ ++ for_each_sg(data->sg, sg, data->sg_len, i) { ++ if (sg->offset & 3 || sg->length & 3) ++ return -EINVAL; ++ } ++ ++ sg_len = dma_map_sg(host->dev, ++ data->sg, ++ data->sg_len, ++ dw_mci_get_dma_dir(data)); ++ if (sg_len == 0) ++ return -EINVAL; ++ ++ if (next) ++ data->host_cookie = sg_len; ++ ++ return sg_len; ++} ++ ++static void dw_mci_pre_req(struct mmc_host *mmc, ++ struct mmc_request *mrq, ++ bool is_first_req) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct mmc_data *data = mrq->data; ++ ++ if (!slot->host->use_dma || !data) ++ return; ++ ++ if (data->host_cookie) { ++ data->host_cookie = 0; ++ return; ++ } ++ ++ if (dw_mci_pre_dma_transfer(slot->host, mrq->data, 1) < 0) ++ data->host_cookie = 0; ++} ++ ++static void dw_mci_post_req(struct mmc_host *mmc, ++ struct mmc_request *mrq, ++ int err) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct mmc_data *data = mrq->data; ++ ++ if (!slot->host->use_dma || !data) ++ return; ++ ++ if (data->host_cookie) ++ dma_unmap_sg(slot->host->dev, ++ data->sg, ++ data->sg_len, ++ dw_mci_get_dma_dir(data)); ++ data->host_cookie = 0; ++} ++ ++static void dw_mci_adjust_fifoth(struct dw_mci *host, struct mmc_data *data) ++{ ++#ifdef CONFIG_MMC_DW_IDMAC ++ unsigned int blksz = data->blksz; ++ const u32 mszs[] = {1, 4, 8, 16, 32, 64, 128, 256}; ++ u32 fifo_width = 1 << host->data_shift; ++ u32 blksz_depth = blksz / fifo_width, fifoth_val; ++ u32 msize = 0, rx_wmark = 1, tx_wmark, tx_wmark_invers; ++ int idx = (sizeof(mszs) / sizeof(mszs[0])) - 1; ++ ++ tx_wmark = (host->fifo_depth) / 2; ++ tx_wmark_invers = host->fifo_depth - tx_wmark; ++ ++ /* ++ * MSIZE is '1', ++ * if blksz is not a multiple of the FIFO width ++ */ ++ if (blksz % fifo_width) { ++ msize = 0; ++ rx_wmark = 1; ++ goto done; ++ } ++ ++ do { ++ if (!((blksz_depth % mszs[idx]) || ++ (tx_wmark_invers % mszs[idx]))) { ++ msize = idx; ++ rx_wmark = mszs[idx] - 1; ++ break; ++ } ++ } while (--idx > 0); ++ /* ++ * If idx is '0', it won't be tried ++ * Thus, initial values are uesed ++ */ ++done: ++ fifoth_val = SDMMC_SET_FIFOTH(msize, rx_wmark, tx_wmark); ++ mci_writel(host, FIFOTH, fifoth_val); ++#endif ++} ++ ++static void dw_mci_ctrl_rd_thld(struct dw_mci *host, struct mmc_data *data) ++{ ++ unsigned int blksz = data->blksz; ++ u32 blksz_depth, fifo_depth; ++ u16 thld_size; ++ ++ WARN_ON(!(data->flags & MMC_DATA_READ)); ++ ++ /* ++ * CDTHRCTL doesn't exist prior to 240A (in fact that register offset is ++ * in the FIFO region, so we really shouldn't access it). ++ */ ++ if (host->verid < DW_MMC_240A) ++ return; ++ ++ if (host->timing != MMC_TIMING_MMC_HS200 && ++ host->timing != MMC_TIMING_UHS_SDR104) ++ goto disable; ++ ++ blksz_depth = blksz / (1 << host->data_shift); ++ fifo_depth = host->fifo_depth; ++ ++ if (blksz_depth > fifo_depth) ++ goto disable; ++ ++ /* ++ * If (blksz_depth) >= (fifo_depth >> 1), should be 'thld_size <= blksz' ++ * If (blksz_depth) < (fifo_depth >> 1), should be thld_size = blksz ++ * Currently just choose blksz. ++ */ ++ thld_size = blksz; ++ mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(thld_size, 1)); ++ return; ++ ++disable: ++ mci_writel(host, CDTHRCTL, SDMMC_SET_RD_THLD(0, 0)); ++} ++ ++static int dw_mci_submit_data_dma(struct dw_mci *host, struct mmc_data *data) ++{ ++ int sg_len; ++ u32 temp; ++ ++ host->using_dma = 0; ++ ++ /* If we don't have a channel, we can't do DMA */ ++ if (!host->use_dma) ++ return -ENODEV; ++ ++ sg_len = dw_mci_pre_dma_transfer(host, data, 0); ++ if (sg_len < 0) { ++ host->dma_ops->stop(host); ++ return sg_len; ++ } ++ ++ host->using_dma = 1; ++ ++ dev_vdbg(host->dev, ++ "sd sg_cpu: %#lx sg_dma: %#lx sg_len: %d\n", ++ (unsigned long)host->sg_cpu, (unsigned long)host->sg_dma, ++ sg_len); ++ ++ /* ++ * Decide the MSIZE and RX/TX Watermark. ++ * If current block size is same with previous size, ++ * no need to update fifoth. ++ */ ++ if (host->prev_blksz != data->blksz) ++ dw_mci_adjust_fifoth(host, data); ++ ++ /* Enable the DMA interface */ ++ temp = mci_readl(host, CTRL); ++ temp |= SDMMC_CTRL_DMA_ENABLE; ++ mci_writel(host, CTRL, temp); ++ ++ /* Disable RX/TX IRQs, let DMA handle it */ ++ temp = mci_readl(host, INTMASK); ++ temp &= ~(SDMMC_INT_RXDR | SDMMC_INT_TXDR); ++ mci_writel(host, INTMASK, temp); ++ ++ host->dma_ops->start(host, sg_len); ++ ++ return 0; ++} ++ ++static void dw_mci_submit_data(struct dw_mci *host, struct mmc_data *data) ++{ ++ u32 temp; ++ ++ data->error = -EINPROGRESS; ++ ++ WARN_ON(host->data); ++ host->sg = NULL; ++ host->data = data; ++ ++ if (data->flags & MMC_DATA_READ) { ++ host->dir_status = DW_MCI_RECV_STATUS; ++ dw_mci_ctrl_rd_thld(host, data); ++ } else { ++ host->dir_status = DW_MCI_SEND_STATUS; ++ } ++ ++ if (dw_mci_submit_data_dma(host, data)) { ++ int flags = SG_MITER_ATOMIC; ++ if (host->data->flags & MMC_DATA_READ) ++ flags |= SG_MITER_TO_SG; ++ else ++ flags |= SG_MITER_FROM_SG; ++ ++ sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags); ++ host->sg = data->sg; ++ host->part_buf_start = 0; ++ host->part_buf_count = 0; ++ ++ mci_writel(host, RINTSTS, SDMMC_INT_TXDR | SDMMC_INT_RXDR); ++ temp = mci_readl(host, INTMASK); ++ temp |= SDMMC_INT_TXDR | SDMMC_INT_RXDR; ++ mci_writel(host, INTMASK, temp); ++ ++ temp = mci_readl(host, CTRL); ++ temp &= ~SDMMC_CTRL_DMA_ENABLE; ++ mci_writel(host, CTRL, temp); ++ ++ /* ++ * Use the initial fifoth_val for PIO mode. ++ * If next issued data may be transfered by DMA mode, ++ * prev_blksz should be invalidated. ++ */ ++ mci_writel(host, FIFOTH, host->fifoth_val); ++ host->prev_blksz = 0; ++ } else { ++ /* ++ * Keep the current block size. ++ * It will be used to decide whether to update ++ * fifoth register next time. ++ */ ++ host->prev_blksz = data->blksz; ++ } ++} ++ ++static void mci_send_cmd(struct dw_mci_slot *slot, u32 cmd, u32 arg) ++{ ++ struct dw_mci *host = slot->host; ++ unsigned long timeout = jiffies + msecs_to_jiffies(500); ++ unsigned int cmd_status = 0; ++ ++ mci_writel(host, CMDARG, arg); ++ wmb(); ++ mci_writel(host, CMD, SDMMC_CMD_START | cmd); ++ ++ while (time_before(jiffies, timeout)) { ++ cmd_status = mci_readl(host, CMD); ++ if (!(cmd_status & SDMMC_CMD_START)) ++ return; ++ } ++ dev_err(&slot->mmc->class_dev, ++ "Timeout sending command (cmd %#x arg %#x status %#x)\n", ++ cmd, arg, cmd_status); ++} ++ ++static void dw_mci_setup_bus(struct dw_mci_slot *slot, bool force_clkinit) ++{ ++ struct dw_mci *host = slot->host; ++ unsigned int clock = slot->clock; ++ u32 div; ++ u32 clk_en_a; ++ u32 sdmmc_cmd_bits = SDMMC_CMD_UPD_CLK | SDMMC_CMD_PRV_DAT_WAIT; ++ ++ /* We must continue to set bit 28 in CMD until the change is complete */ ++ if (host->state == STATE_WAITING_CMD11_DONE) ++ sdmmc_cmd_bits |= SDMMC_CMD_VOLT_SWITCH; ++ ++ if (!clock) { ++ mci_writel(host, CLKENA, 0); ++ mci_send_cmd(slot, sdmmc_cmd_bits, 0); ++ } else if (clock != host->current_speed || force_clkinit) { ++ div = host->bus_hz / clock; ++ if (host->bus_hz % clock && host->bus_hz > clock) ++ /* ++ * move the + 1 after the divide to prevent ++ * over-clocking the card. ++ */ ++ div += 1; ++ ++ div = (host->bus_hz != clock) ? DIV_ROUND_UP(div, 2) : 0; ++ ++ if ((clock << div) != slot->__clk_old || force_clkinit) ++ dev_info(&slot->mmc->class_dev, ++ "Bus speed (slot %d) = %dHz (slot req %dHz, actual %dHZ div = %d)\n", ++ slot->id, host->bus_hz, clock, ++ div ? ((host->bus_hz / div) >> 1) : ++ host->bus_hz, div); ++ ++ /* disable clock */ ++ mci_writel(host, CLKENA, 0); ++ mci_writel(host, CLKSRC, 0); ++ ++ /* inform CIU */ ++ mci_send_cmd(slot, sdmmc_cmd_bits, 0); ++ ++ /* set clock to desired speed */ ++ mci_writel(host, CLKDIV, div); ++ ++ /* inform CIU */ ++ mci_send_cmd(slot, sdmmc_cmd_bits, 0); ++ ++ /* enable clock; only low power if no SDIO */ ++ clk_en_a = SDMMC_CLKEN_ENABLE << slot->id; ++ if (!(mci_readl(host, INTMASK) & SDMMC_INT_SDIO(slot->id))) ++ clk_en_a |= SDMMC_CLKEN_LOW_PWR << slot->id; ++ mci_writel(host, CLKENA, clk_en_a); ++ ++ /* inform CIU */ ++ mci_send_cmd(slot, sdmmc_cmd_bits, 0); ++ ++ /* keep the clock with reflecting clock dividor */ ++ slot->__clk_old = clock << div; ++ } ++ ++ host->current_speed = clock; ++ ++ /* Set the current slot bus width */ ++ mci_writel(host, CTYPE, (slot->ctype << slot->id)); ++} ++ ++static void __dw_mci_start_request(struct dw_mci *host, ++ struct dw_mci_slot *slot, ++ struct mmc_command *cmd) ++{ ++ struct mmc_request *mrq; ++ struct mmc_data *data; ++ u32 cmdflags; ++ ++ mrq = slot->mrq; ++ ++ host->cur_slot = slot; ++ host->mrq = mrq; ++ ++ host->pending_events = 0; ++ host->completed_events = 0; ++ host->cmd_status = 0; ++ host->data_status = 0; ++ host->dir_status = 0; ++ ++ data = cmd->data; ++ if (data) { ++ mci_writel(host, TMOUT, 0xFFFFFFFF); ++ mci_writel(host, BYTCNT, data->blksz*data->blocks); ++ mci_writel(host, BLKSIZ, data->blksz); ++ } ++ ++ cmdflags = dw_mci_prepare_command(slot->mmc, cmd); ++ ++ /* this is the first command, send the initialization clock */ ++ if (test_and_clear_bit(DW_MMC_CARD_NEED_INIT, &slot->flags)) ++ cmdflags |= SDMMC_CMD_INIT; ++ ++ if (data) { ++ dw_mci_submit_data(host, data); ++ wmb(); ++ } ++ ++ dw_mci_start_command(host, cmd, cmdflags); ++ ++ if (mrq->stop) ++ host->stop_cmdr = dw_mci_prepare_command(slot->mmc, mrq->stop); ++ else ++ host->stop_cmdr = dw_mci_prep_stop_abort(host, cmd); ++} ++ ++static void dw_mci_start_request(struct dw_mci *host, ++ struct dw_mci_slot *slot) ++{ ++ struct mmc_request *mrq = slot->mrq; ++ struct mmc_command *cmd; ++ ++ cmd = mrq->sbc ? mrq->sbc : mrq->cmd; ++ __dw_mci_start_request(host, slot, cmd); ++} ++ ++/* must be called with host->lock held */ ++static void dw_mci_queue_request(struct dw_mci *host, struct dw_mci_slot *slot, ++ struct mmc_request *mrq) ++{ ++ dev_vdbg(&slot->mmc->class_dev, "queue request: state=%d\n", ++ host->state); ++ ++ slot->mrq = mrq; ++ ++ if (host->state == STATE_WAITING_CMD11_DONE) { ++ dev_warn(&slot->mmc->class_dev, ++ "Voltage change didn't complete\n"); ++ /* ++ * this case isn't expected to happen, so we can ++ * either crash here or just try to continue on ++ * in the closest possible state ++ */ ++ host->state = STATE_IDLE; ++ } ++ ++ if (host->state == STATE_IDLE) { ++ host->state = STATE_SENDING_CMD; ++ dw_mci_start_request(host, slot); ++ } else { ++ list_add_tail(&slot->queue_node, &host->queue); ++ } ++} ++ ++static void dw_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct dw_mci *host = slot->host; ++ ++ WARN_ON(slot->mrq); ++ ++ /* ++ * The check for card presence and queueing of the request must be ++ * atomic, otherwise the card could be removed in between and the ++ * request wouldn't fail until another card was inserted. ++ */ ++ spin_lock_bh(&host->lock); ++ ++ if (!test_bit(DW_MMC_CARD_PRESENT, &slot->flags)) { ++ spin_unlock_bh(&host->lock); ++ mrq->cmd->error = -ENOMEDIUM; ++ mmc_request_done(mmc, mrq); ++ return; ++ } ++ ++ dw_mci_queue_request(host, slot, mrq); ++ ++ spin_unlock_bh(&host->lock); ++} ++ ++static void dw_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ const struct dw_mci_drv_data *drv_data = slot->host->drv_data; ++ u32 regs; ++ int ret; ++ ++ switch (ios->bus_width) { ++ case MMC_BUS_WIDTH_4: ++ slot->ctype = SDMMC_CTYPE_4BIT; ++ break; ++ case MMC_BUS_WIDTH_8: ++ slot->ctype = SDMMC_CTYPE_8BIT; ++ break; ++ default: ++ /* set default 1 bit mode */ ++ slot->ctype = SDMMC_CTYPE_1BIT; ++ } ++ ++ regs = mci_readl(slot->host, UHS_REG); ++ ++ /* DDR mode set */ ++ if (ios->timing == MMC_TIMING_MMC_DDR52) ++ regs |= ((0x1 << slot->id) << 16); ++ else ++ regs &= ~((0x1 << slot->id) << 16); ++ ++ mci_writel(slot->host, UHS_REG, regs); ++ slot->host->timing = ios->timing; ++ ++ /* ++ * Use mirror of ios->clock to prevent race with mmc ++ * core ios update when finding the minimum. ++ */ ++ slot->clock = ios->clock; ++ ++ if (drv_data && drv_data->set_ios) ++ drv_data->set_ios(slot->host, ios); ++ ++ /* Slot specific timing and width adjustment */ ++ dw_mci_setup_bus(slot, false); ++ ++ if (slot->host->state == STATE_WAITING_CMD11_DONE && ios->clock != 0) ++ slot->host->state = STATE_IDLE; ++ ++ switch (ios->power_mode) { ++ case MMC_POWER_UP: ++ if (!IS_ERR(mmc->supply.vmmc)) { ++ ret = mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ++ ios->vdd); ++ if (ret) { ++ dev_err(slot->host->dev, ++ "failed to enable vmmc regulator\n"); ++ /*return, if failed turn on vmmc*/ ++ return; ++ } ++ } ++ if (!IS_ERR(mmc->supply.vqmmc) && !slot->host->vqmmc_enabled) { ++ ret = regulator_enable(mmc->supply.vqmmc); ++ if (ret < 0) ++ dev_err(slot->host->dev, ++ "failed to enable vqmmc regulator\n"); ++ else ++ slot->host->vqmmc_enabled = true; ++ } ++ set_bit(DW_MMC_CARD_NEED_INIT, &slot->flags); ++ regs = mci_readl(slot->host, PWREN); ++ regs |= (1 << slot->id); ++ mci_writel(slot->host, PWREN, regs); ++ break; ++ 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) && slot->host->vqmmc_enabled) { ++ regulator_disable(mmc->supply.vqmmc); ++ slot->host->vqmmc_enabled = false; ++ } ++ ++ regs = mci_readl(slot->host, PWREN); ++ regs &= ~(1 << slot->id); ++ mci_writel(slot->host, PWREN, regs); ++ break; ++ default: ++ break; ++ } ++} ++ ++static int dw_mci_card_busy(struct mmc_host *mmc) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ u32 status; ++ ++ /* ++ * Check the busy bit which is low when DAT[3:0] ++ * (the data lines) are 0000 ++ */ ++ status = mci_readl(slot->host, STATUS); ++ ++ return !!(status & SDMMC_STATUS_BUSY); ++} ++ ++static int dw_mci_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct dw_mci *host = slot->host; ++ u32 uhs; ++ u32 v18 = SDMMC_UHS_18V << slot->id; ++ int min_uv, max_uv; ++ int ret; ++ ++ /* ++ * Program the voltage. Note that some instances of dw_mmc may use ++ * the UHS_REG for this. For other instances (like exynos) the UHS_REG ++ * does no harm but you need to set the regulator directly. Try both. ++ */ ++ uhs = mci_readl(host, UHS_REG); ++ if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330) { ++ min_uv = 2700000; ++ max_uv = 3600000; ++ uhs &= ~v18; ++ } else { ++ min_uv = 1700000; ++ max_uv = 1950000; ++ uhs |= v18; ++ } ++ if (!IS_ERR(mmc->supply.vqmmc)) { ++ ret = regulator_set_voltage(mmc->supply.vqmmc, min_uv, max_uv); ++ ++ if (ret) { ++ dev_err(&mmc->class_dev, ++ "Regulator set error %d: %d - %d\n", ++ ret, min_uv, max_uv); ++ return ret; ++ } ++ } ++ mci_writel(host, UHS_REG, uhs); ++ ++ return 0; ++} ++ ++static int dw_mci_get_ro(struct mmc_host *mmc) ++{ ++ int read_only; ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ int gpio_ro = mmc_gpio_get_ro(mmc); ++ ++ /* Use platform get_ro function, else try on board write protect */ ++ if ((slot->quirks & DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT) || ++ (slot->host->quirks & DW_MCI_QUIRK_NO_WRITE_PROTECT)) ++ read_only = 0; ++ else if (!IS_ERR_VALUE(gpio_ro)) ++ read_only = gpio_ro; ++ else ++ read_only = ++ mci_readl(slot->host, WRTPRT) & (1 << slot->id) ? 1 : 0; ++ ++ dev_dbg(&mmc->class_dev, "card is %s\n", ++ read_only ? "read-only" : "read-write"); ++ ++ return read_only; ++} ++ ++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; ++ struct dw_mci *host = slot->host; ++ 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 (!IS_ERR_VALUE(gpio_cd)) ++ present = gpio_cd; ++ else ++ present = (mci_readl(slot->host, CDETECT) & (1 << slot->id)) ++ == 0 ? 1 : 0; ++ ++ spin_lock_bh(&host->lock); ++ if (present) { ++ set_bit(DW_MMC_CARD_PRESENT, &slot->flags); ++ dev_dbg(&mmc->class_dev, "card is present\n"); ++ } else { ++ clear_bit(DW_MMC_CARD_PRESENT, &slot->flags); ++ dev_dbg(&mmc->class_dev, "card is not present\n"); ++ } ++ spin_unlock_bh(&host->lock); ++ ++ return present; ++} ++ ++/* ++ * Disable lower power mode. ++ * ++ * Low power mode will stop the card clock when idle. According to the ++ * description of the CLKENA register we should disable low power mode ++ * for SDIO cards if we need SDIO interrupts to work. ++ * ++ * This function is fast if low power mode is already disabled. ++ */ ++static void dw_mci_disable_low_power(struct dw_mci_slot *slot) ++{ ++ struct dw_mci *host = slot->host; ++ u32 clk_en_a; ++ const u32 clken_low_pwr = SDMMC_CLKEN_LOW_PWR << slot->id; ++ ++ clk_en_a = mci_readl(host, CLKENA); ++ ++ if (clk_en_a & clken_low_pwr) { ++ mci_writel(host, CLKENA, clk_en_a & ~clken_low_pwr); ++ mci_send_cmd(slot, SDMMC_CMD_UPD_CLK | ++ SDMMC_CMD_PRV_DAT_WAIT, 0); ++ } ++} ++ ++static void dw_mci_enable_sdio_irq(struct mmc_host *mmc, int enb) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct dw_mci *host = slot->host; ++ u32 int_mask; ++ ++ /* Enable/disable Slot Specific SDIO interrupt */ ++ int_mask = mci_readl(host, INTMASK); ++ if (enb) { ++ /* ++ * Turn off low power mode if it was enabled. This is a bit of ++ * a heavy operation and we disable / enable IRQs a lot, so ++ * we'll leave low power mode disabled and it will get ++ * re-enabled again in dw_mci_setup_bus(). ++ */ ++ dw_mci_disable_low_power(slot); ++ ++ mci_writel(host, INTMASK, ++ (int_mask | SDMMC_INT_SDIO(slot->id))); ++ } else { ++ mci_writel(host, INTMASK, ++ (int_mask & ~SDMMC_INT_SDIO(slot->id))); ++ } ++} ++ ++static int dw_mci_execute_tuning(struct mmc_host *mmc, u32 opcode) ++{ ++ struct dw_mci_slot *slot = mmc_priv(mmc); ++ struct dw_mci *host = slot->host; ++ const struct dw_mci_drv_data *drv_data = host->drv_data; ++ struct dw_mci_tuning_data tuning_data; ++ int err = -ENOSYS; ++ ++ if (opcode == MMC_SEND_TUNING_BLOCK_HS200) { ++ if (mmc->ios.bus_width == MMC_BUS_WIDTH_8) { ++ tuning_data.blk_pattern = tuning_blk_pattern_8bit; ++ tuning_data.blksz = sizeof(tuning_blk_pattern_8bit); ++ } else if (mmc->ios.bus_width == MMC_BUS_WIDTH_4) { ++ tuning_data.blk_pattern = tuning_blk_pattern_4bit; ++ tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); ++ } else { ++ return -EINVAL; ++ } ++ } else if (opcode == MMC_SEND_TUNING_BLOCK) { ++ tuning_data.blk_pattern = tuning_blk_pattern_4bit; ++ tuning_data.blksz = sizeof(tuning_blk_pattern_4bit); ++ } else { ++ dev_err(host->dev, ++ "Undefined command(%d) for tuning\n", opcode); ++ return -EINVAL; ++ } ++ ++ if (drv_data && drv_data->execute_tuning) ++ err = drv_data->execute_tuning(slot, opcode, &tuning_data); ++ return err; ++} ++ ++static const struct mmc_host_ops dw_mci_ops = { ++ .request = dw_mci_request, ++ .pre_req = dw_mci_pre_req, ++ .post_req = dw_mci_post_req, ++ .set_ios = dw_mci_set_ios, ++ .get_ro = dw_mci_get_ro, ++ .get_cd = dw_mci_get_cd, ++ .enable_sdio_irq = dw_mci_enable_sdio_irq, ++ .execute_tuning = dw_mci_execute_tuning, ++ .card_busy = dw_mci_card_busy, ++ .start_signal_voltage_switch = dw_mci_switch_voltage, ++ ++}; ++ ++static void dw_mci_request_end(struct dw_mci *host, struct mmc_request *mrq) ++ __releases(&host->lock) ++ __acquires(&host->lock) ++{ ++ struct dw_mci_slot *slot; ++ struct mmc_host *prev_mmc = host->cur_slot->mmc; ++ ++ WARN_ON(host->cmd || host->data); ++ ++ host->cur_slot->mrq = NULL; ++ host->mrq = NULL; ++ if (!list_empty(&host->queue)) { ++ slot = list_entry(host->queue.next, ++ struct dw_mci_slot, queue_node); ++ list_del(&slot->queue_node); ++ dev_vdbg(host->dev, "list not empty: %s is next\n", ++ mmc_hostname(slot->mmc)); ++ host->state = STATE_SENDING_CMD; ++ dw_mci_start_request(host, slot); ++ } else { ++ dev_vdbg(host->dev, "list empty\n"); ++ ++ if (host->state == STATE_SENDING_CMD11) ++ host->state = STATE_WAITING_CMD11_DONE; ++ else ++ host->state = STATE_IDLE; ++ } ++ ++ spin_unlock(&host->lock); ++ mmc_request_done(prev_mmc, mrq); ++ spin_lock(&host->lock); ++} ++ ++static int dw_mci_command_complete(struct dw_mci *host, struct mmc_command *cmd) ++{ ++ u32 status = host->cmd_status; ++ ++ host->cmd_status = 0; ++ ++ /* Read the response from the card (up to 16 bytes) */ ++ if (cmd->flags & MMC_RSP_PRESENT) { ++ if (cmd->flags & MMC_RSP_136) { ++ cmd->resp[3] = mci_readl(host, RESP0); ++ cmd->resp[2] = mci_readl(host, RESP1); ++ cmd->resp[1] = mci_readl(host, RESP2); ++ cmd->resp[0] = mci_readl(host, RESP3); ++ } else { ++ cmd->resp[0] = mci_readl(host, RESP0); ++ cmd->resp[1] = 0; ++ cmd->resp[2] = 0; ++ cmd->resp[3] = 0; ++ } ++ } ++ ++ if (status & SDMMC_INT_RTO) ++ cmd->error = -ETIMEDOUT; ++ else if ((cmd->flags & MMC_RSP_CRC) && (status & SDMMC_INT_RCRC)) ++ cmd->error = -EILSEQ; ++ else if (status & SDMMC_INT_RESP_ERR) ++ cmd->error = -EIO; ++ else ++ cmd->error = 0; ++ ++ if (cmd->error) { ++ /* newer ip versions need a delay between retries */ ++ if (host->quirks & DW_MCI_QUIRK_RETRY_DELAY) ++ mdelay(20); ++ } ++ ++ return cmd->error; ++} ++ ++static int dw_mci_data_complete(struct dw_mci *host, struct mmc_data *data) ++{ ++ u32 status = host->data_status; ++ ++ if (status & DW_MCI_DATA_ERROR_FLAGS) { ++ if (status & SDMMC_INT_DRTO) { ++ data->error = -ETIMEDOUT; ++ } else if (status & SDMMC_INT_DCRC) { ++ data->error = -EILSEQ; ++ } else if (status & SDMMC_INT_EBE) { ++ if (host->dir_status == ++ DW_MCI_SEND_STATUS) { ++ /* ++ * No data CRC status was returned. ++ * The number of bytes transferred ++ * will be exaggerated in PIO mode. ++ */ ++ data->bytes_xfered = 0; ++ data->error = -ETIMEDOUT; ++ } else if (host->dir_status == ++ DW_MCI_RECV_STATUS) { ++ data->error = -EIO; ++ } ++ } else { ++ /* SDMMC_INT_SBE is included */ ++ data->error = -EIO; ++ } ++ ++ dev_dbg(host->dev, "data error, status 0x%08x\n", status); ++ ++ /* ++ * After an error, there may be data lingering ++ * in the FIFO ++ */ ++ dw_mci_reset(host); ++ } else { ++ data->bytes_xfered = data->blocks * data->blksz; ++ data->error = 0; ++ } ++ ++ return data->error; ++} ++ ++static void dw_mci_tasklet_func(unsigned long priv) ++{ ++ struct dw_mci *host = (struct dw_mci *)priv; ++ struct mmc_data *data; ++ struct mmc_command *cmd; ++ struct mmc_request *mrq; ++ enum dw_mci_state state; ++ enum dw_mci_state prev_state; ++ unsigned int err; ++ ++ spin_lock(&host->lock); ++ ++ state = host->state; ++ data = host->data; ++ mrq = host->mrq; ++ ++ do { ++ prev_state = state; ++ ++ switch (state) { ++ case STATE_IDLE: ++ case STATE_WAITING_CMD11_DONE: ++ break; ++ ++ case STATE_SENDING_CMD11: ++ case STATE_SENDING_CMD: ++ if (!test_and_clear_bit(EVENT_CMD_COMPLETE, ++ &host->pending_events)) ++ break; ++ ++ cmd = host->cmd; ++ host->cmd = NULL; ++ set_bit(EVENT_CMD_COMPLETE, &host->completed_events); ++ err = dw_mci_command_complete(host, cmd); ++ if (cmd == mrq->sbc && !err) { ++ prev_state = state = STATE_SENDING_CMD; ++ __dw_mci_start_request(host, host->cur_slot, ++ mrq->cmd); ++ goto unlock; ++ } ++ ++ if (cmd->data && err) { ++ dw_mci_stop_dma(host); ++ send_stop_abort(host, data); ++ state = STATE_SENDING_STOP; ++ break; ++ } ++ ++ if (!cmd->data || err) { ++ dw_mci_request_end(host, mrq); ++ goto unlock; ++ } ++ ++ prev_state = state = STATE_SENDING_DATA; ++ /* fall through */ ++ ++ case STATE_SENDING_DATA: ++ /* ++ * We could get a data error and never a transfer ++ * complete so we'd better check for it here. ++ * ++ * Note that we don't really care if we also got a ++ * transfer complete; stopping the DMA and sending an ++ * abort won't hurt. ++ */ ++ if (test_and_clear_bit(EVENT_DATA_ERROR, ++ &host->pending_events)) { ++ dw_mci_stop_dma(host); ++ send_stop_abort(host, data); ++ state = STATE_DATA_ERROR; ++ break; ++ } ++ ++ if (!test_and_clear_bit(EVENT_XFER_COMPLETE, ++ &host->pending_events)) ++ break; ++ ++ set_bit(EVENT_XFER_COMPLETE, &host->completed_events); ++ ++ /* ++ * Handle an EVENT_DATA_ERROR that might have shown up ++ * before the transfer completed. This might not have ++ * been caught by the check above because the interrupt ++ * could have gone off between the previous check and ++ * the check for transfer complete. ++ * ++ * Technically this ought not be needed assuming we ++ * get a DATA_COMPLETE eventually (we'll notice the ++ * error and end the request), but it shouldn't hurt. ++ * ++ * This has the advantage of sending the stop command. ++ */ ++ if (test_and_clear_bit(EVENT_DATA_ERROR, ++ &host->pending_events)) { ++ dw_mci_stop_dma(host); ++ send_stop_abort(host, data); ++ state = STATE_DATA_ERROR; ++ break; ++ } ++ prev_state = state = STATE_DATA_BUSY; ++ ++ /* fall through */ ++ ++ case STATE_DATA_BUSY: ++ if (!test_and_clear_bit(EVENT_DATA_COMPLETE, ++ &host->pending_events)) ++ break; ++ ++ host->data = NULL; ++ set_bit(EVENT_DATA_COMPLETE, &host->completed_events); ++ err = dw_mci_data_complete(host, data); ++ ++ if (!err) { ++ if (!data->stop || mrq->sbc) { ++ if (mrq->sbc && data->stop) ++ data->stop->error = 0; ++ dw_mci_request_end(host, mrq); ++ goto unlock; ++ } ++ ++ /* stop command for open-ended transfer*/ ++ if (data->stop) ++ send_stop_abort(host, data); ++ } else { ++ /* ++ * If we don't have a command complete now we'll ++ * never get one since we just reset everything; ++ * better end the request. ++ * ++ * If we do have a command complete we'll fall ++ * through to the SENDING_STOP command and ++ * everything will be peachy keen. ++ */ ++ if (!test_bit(EVENT_CMD_COMPLETE, ++ &host->pending_events)) { ++ host->cmd = NULL; ++ dw_mci_request_end(host, mrq); ++ goto unlock; ++ } ++ } ++ ++ /* ++ * If err has non-zero, ++ * stop-abort command has been already issued. ++ */ ++ prev_state = state = STATE_SENDING_STOP; ++ ++ /* fall through */ ++ ++ case STATE_SENDING_STOP: ++ if (!test_and_clear_bit(EVENT_CMD_COMPLETE, ++ &host->pending_events)) ++ break; ++ ++ /* CMD error in data command */ ++ if (mrq->cmd->error && mrq->data) ++ dw_mci_reset(host); ++ ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ if (mrq->stop) ++ dw_mci_command_complete(host, mrq->stop); ++ else ++ host->cmd_status = 0; ++ ++ dw_mci_request_end(host, mrq); ++ goto unlock; ++ ++ case STATE_DATA_ERROR: ++ if (!test_and_clear_bit(EVENT_XFER_COMPLETE, ++ &host->pending_events)) ++ break; ++ ++ state = STATE_DATA_BUSY; ++ break; ++ } ++ } while (state != prev_state); ++ ++ host->state = state; ++unlock: ++ spin_unlock(&host->lock); ++ ++} ++ ++/* push final bytes to part_buf, only use during push */ ++static void dw_mci_set_part_bytes(struct dw_mci *host, void *buf, int cnt) ++{ ++ memcpy((void *)&host->part_buf, buf, cnt); ++ host->part_buf_count = cnt; ++} ++ ++/* append bytes to part_buf, only use during push */ ++static int dw_mci_push_part_bytes(struct dw_mci *host, void *buf, int cnt) ++{ ++ cnt = min(cnt, (1 << host->data_shift) - host->part_buf_count); ++ memcpy((void *)&host->part_buf + host->part_buf_count, buf, cnt); ++ host->part_buf_count += cnt; ++ return cnt; ++} ++ ++/* pull first bytes from part_buf, only use during pull */ ++static int dw_mci_pull_part_bytes(struct dw_mci *host, void *buf, int cnt) ++{ ++ cnt = min(cnt, (int)host->part_buf_count); ++ if (cnt) { ++ memcpy(buf, (void *)&host->part_buf + host->part_buf_start, ++ cnt); ++ host->part_buf_count -= cnt; ++ host->part_buf_start += cnt; ++ } ++ return cnt; ++} ++ ++/* pull final bytes from the part_buf, assuming it's just been filled */ ++static void dw_mci_pull_final_bytes(struct dw_mci *host, void *buf, int cnt) ++{ ++ memcpy(buf, &host->part_buf, cnt); ++ host->part_buf_start = cnt; ++ host->part_buf_count = (1 << host->data_shift) - cnt; ++} ++ ++static void dw_mci_push_data16(struct dw_mci *host, void *buf, int cnt) ++{ ++ struct mmc_data *data = host->data; ++ int init_cnt = cnt; ++ ++ /* try and push anything in the part_buf */ ++ if (unlikely(host->part_buf_count)) { ++ int len = dw_mci_push_part_bytes(host, buf, cnt); ++ buf += len; ++ cnt -= len; ++ if (host->part_buf_count == 2) { ++ mci_writew(host, DATA(host->data_offset), ++ host->part_buf16); ++ host->part_buf_count = 0; ++ } ++ } ++#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (unlikely((unsigned long)buf & 0x1)) { ++ while (cnt >= 2) { ++ u16 aligned_buf[64]; ++ int len = min(cnt & -2, (int)sizeof(aligned_buf)); ++ int items = len >> 1; ++ int i; ++ /* memcpy from input buffer into aligned buffer */ ++ memcpy(aligned_buf, buf, len); ++ buf += len; ++ cnt -= len; ++ /* push data from aligned buffer into fifo */ ++ for (i = 0; i < items; ++i) ++ mci_writew(host, DATA(host->data_offset), ++ aligned_buf[i]); ++ } ++ } else ++#endif ++ { ++ u16 *pdata = buf; ++ for (; cnt >= 2; cnt -= 2) ++ mci_writew(host, DATA(host->data_offset), *pdata++); ++ buf = pdata; ++ } ++ /* put anything remaining in the part_buf */ ++ if (cnt) { ++ dw_mci_set_part_bytes(host, buf, cnt); ++ /* Push data if we have reached the expected data length */ ++ if ((data->bytes_xfered + init_cnt) == ++ (data->blksz * data->blocks)) ++ mci_writew(host, DATA(host->data_offset), ++ host->part_buf16); ++ } ++} ++ ++static void dw_mci_pull_data16(struct dw_mci *host, void *buf, int cnt) ++{ ++#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (unlikely((unsigned long)buf & 0x1)) { ++ while (cnt >= 2) { ++ /* pull data from fifo into aligned buffer */ ++ u16 aligned_buf[64]; ++ int len = min(cnt & -2, (int)sizeof(aligned_buf)); ++ int items = len >> 1; ++ int i; ++ for (i = 0; i < items; ++i) ++ aligned_buf[i] = mci_readw(host, ++ DATA(host->data_offset)); ++ /* memcpy from aligned buffer into output buffer */ ++ memcpy(buf, aligned_buf, len); ++ buf += len; ++ cnt -= len; ++ } ++ } else ++#endif ++ { ++ u16 *pdata = buf; ++ for (; cnt >= 2; cnt -= 2) ++ *pdata++ = mci_readw(host, DATA(host->data_offset)); ++ buf = pdata; ++ } ++ if (cnt) { ++ host->part_buf16 = mci_readw(host, DATA(host->data_offset)); ++ dw_mci_pull_final_bytes(host, buf, cnt); ++ } ++} ++ ++static void dw_mci_push_data32(struct dw_mci *host, void *buf, int cnt) ++{ ++ struct mmc_data *data = host->data; ++ int init_cnt = cnt; ++ ++ /* try and push anything in the part_buf */ ++ if (unlikely(host->part_buf_count)) { ++ int len = dw_mci_push_part_bytes(host, buf, cnt); ++ buf += len; ++ cnt -= len; ++ if (host->part_buf_count == 4) { ++ mci_writel(host, DATA(host->data_offset), ++ host->part_buf32); ++ host->part_buf_count = 0; ++ } ++ } ++#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (unlikely((unsigned long)buf & 0x3)) { ++ while (cnt >= 4) { ++ u32 aligned_buf[32]; ++ int len = min(cnt & -4, (int)sizeof(aligned_buf)); ++ int items = len >> 2; ++ int i; ++ /* memcpy from input buffer into aligned buffer */ ++ memcpy(aligned_buf, buf, len); ++ buf += len; ++ cnt -= len; ++ /* push data from aligned buffer into fifo */ ++ for (i = 0; i < items; ++i) ++ mci_writel(host, DATA(host->data_offset), ++ aligned_buf[i]); ++ } ++ } else ++#endif ++ { ++ u32 *pdata = buf; ++ for (; cnt >= 4; cnt -= 4) ++ mci_writel(host, DATA(host->data_offset), *pdata++); ++ buf = pdata; ++ } ++ /* put anything remaining in the part_buf */ ++ if (cnt) { ++ dw_mci_set_part_bytes(host, buf, cnt); ++ /* Push data if we have reached the expected data length */ ++ if ((data->bytes_xfered + init_cnt) == ++ (data->blksz * data->blocks)) ++ mci_writel(host, DATA(host->data_offset), ++ host->part_buf32); ++ } ++} ++ ++static void dw_mci_pull_data32(struct dw_mci *host, void *buf, int cnt) ++{ ++#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (unlikely((unsigned long)buf & 0x3)) { ++ while (cnt >= 4) { ++ /* pull data from fifo into aligned buffer */ ++ u32 aligned_buf[32]; ++ int len = min(cnt & -4, (int)sizeof(aligned_buf)); ++ int items = len >> 2; ++ int i; ++ for (i = 0; i < items; ++i) ++ aligned_buf[i] = mci_readl(host, ++ DATA(host->data_offset)); ++ /* memcpy from aligned buffer into output buffer */ ++ memcpy(buf, aligned_buf, len); ++ buf += len; ++ cnt -= len; ++ } ++ } else ++#endif ++ { ++ u32 *pdata = buf; ++ for (; cnt >= 4; cnt -= 4) ++ *pdata++ = mci_readl(host, DATA(host->data_offset)); ++ buf = pdata; ++ } ++ if (cnt) { ++ host->part_buf32 = mci_readl(host, DATA(host->data_offset)); ++ dw_mci_pull_final_bytes(host, buf, cnt); ++ } ++} ++ ++static void dw_mci_push_data64(struct dw_mci *host, void *buf, int cnt) ++{ ++ struct mmc_data *data = host->data; ++ int init_cnt = cnt; ++ ++ /* try and push anything in the part_buf */ ++ if (unlikely(host->part_buf_count)) { ++ int len = dw_mci_push_part_bytes(host, buf, cnt); ++ buf += len; ++ cnt -= len; ++ ++ if (host->part_buf_count == 8) { ++ mci_writeq(host, DATA(host->data_offset), ++ host->part_buf); ++ host->part_buf_count = 0; ++ } ++ } ++#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (unlikely((unsigned long)buf & 0x7)) { ++ while (cnt >= 8) { ++ u64 aligned_buf[16]; ++ int len = min(cnt & -8, (int)sizeof(aligned_buf)); ++ int items = len >> 3; ++ int i; ++ /* memcpy from input buffer into aligned buffer */ ++ memcpy(aligned_buf, buf, len); ++ buf += len; ++ cnt -= len; ++ /* push data from aligned buffer into fifo */ ++ for (i = 0; i < items; ++i) ++ mci_writeq(host, DATA(host->data_offset), ++ aligned_buf[i]); ++ } ++ } else ++#endif ++ { ++ u64 *pdata = buf; ++ for (; cnt >= 8; cnt -= 8) ++ mci_writeq(host, DATA(host->data_offset), *pdata++); ++ buf = pdata; ++ } ++ /* put anything remaining in the part_buf */ ++ if (cnt) { ++ dw_mci_set_part_bytes(host, buf, cnt); ++ /* Push data if we have reached the expected data length */ ++ if ((data->bytes_xfered + init_cnt) == ++ (data->blksz * data->blocks)) ++ mci_writeq(host, DATA(host->data_offset), ++ host->part_buf); ++ } ++} ++ ++static void dw_mci_pull_data64(struct dw_mci *host, void *buf, int cnt) ++{ ++#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS ++ if (unlikely((unsigned long)buf & 0x7)) { ++ while (cnt >= 8) { ++ /* pull data from fifo into aligned buffer */ ++ u64 aligned_buf[16]; ++ int len = min(cnt & -8, (int)sizeof(aligned_buf)); ++ int items = len >> 3; ++ int i; ++ for (i = 0; i < items; ++i) ++ aligned_buf[i] = mci_readq(host, ++ DATA(host->data_offset)); ++ /* memcpy from aligned buffer into output buffer */ ++ memcpy(buf, aligned_buf, len); ++ buf += len; ++ cnt -= len; ++ } ++ } else ++#endif ++ { ++ u64 *pdata = buf; ++ for (; cnt >= 8; cnt -= 8) ++ *pdata++ = mci_readq(host, DATA(host->data_offset)); ++ buf = pdata; ++ } ++ if (cnt) { ++ host->part_buf = mci_readq(host, DATA(host->data_offset)); ++ dw_mci_pull_final_bytes(host, buf, cnt); ++ } ++} ++ ++static void dw_mci_pull_data(struct dw_mci *host, void *buf, int cnt) ++{ ++ int len; ++ ++ /* get remaining partial bytes */ ++ len = dw_mci_pull_part_bytes(host, buf, cnt); ++ if (unlikely(len == cnt)) ++ return; ++ buf += len; ++ cnt -= len; ++ ++ /* get the rest of the data */ ++ host->pull_data(host, buf, cnt); ++} ++ ++static void dw_mci_read_data_pio(struct dw_mci *host, bool dto) ++{ ++ struct sg_mapping_iter *sg_miter = &host->sg_miter; ++ void *buf; ++ unsigned int offset; ++ struct mmc_data *data = host->data; ++ int shift = host->data_shift; ++ u32 status; ++ unsigned int len; ++ unsigned int remain, fcnt; ++ ++ do { ++ if (!sg_miter_next(sg_miter)) ++ goto done; ++ ++ host->sg = sg_miter->piter.sg; ++ buf = sg_miter->addr; ++ remain = sg_miter->length; ++ offset = 0; ++ ++ do { ++ fcnt = (SDMMC_GET_FCNT(mci_readl(host, STATUS)) ++ << shift) + host->part_buf_count; ++ len = min(remain, fcnt); ++ if (!len) ++ break; ++ dw_mci_pull_data(host, (void *)(buf + offset), len); ++ data->bytes_xfered += len; ++ offset += len; ++ remain -= len; ++ } while (remain); ++ ++ sg_miter->consumed = offset; ++ status = mci_readl(host, MINTSTS); ++ mci_writel(host, RINTSTS, SDMMC_INT_RXDR); ++ /* if the RXDR is ready read again */ ++ } while ((status & SDMMC_INT_RXDR) || ++ (dto && SDMMC_GET_FCNT(mci_readl(host, STATUS)))); ++ ++ if (!remain) { ++ if (!sg_miter_next(sg_miter)) ++ goto done; ++ sg_miter->consumed = 0; ++ } ++ sg_miter_stop(sg_miter); ++ return; ++ ++done: ++ sg_miter_stop(sg_miter); ++ host->sg = NULL; ++ smp_wmb(); ++ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); ++} ++ ++static void dw_mci_write_data_pio(struct dw_mci *host) ++{ ++ struct sg_mapping_iter *sg_miter = &host->sg_miter; ++ void *buf; ++ unsigned int offset; ++ struct mmc_data *data = host->data; ++ int shift = host->data_shift; ++ u32 status; ++ unsigned int len; ++ unsigned int fifo_depth = host->fifo_depth; ++ unsigned int remain, fcnt; ++ ++ do { ++ if (!sg_miter_next(sg_miter)) ++ goto done; ++ ++ host->sg = sg_miter->piter.sg; ++ buf = sg_miter->addr; ++ remain = sg_miter->length; ++ offset = 0; ++ ++ do { ++ fcnt = ((fifo_depth - ++ SDMMC_GET_FCNT(mci_readl(host, STATUS))) ++ << shift) - host->part_buf_count; ++ len = min(remain, fcnt); ++ if (!len) ++ break; ++ host->push_data(host, (void *)(buf + offset), len); ++ data->bytes_xfered += len; ++ offset += len; ++ remain -= len; ++ } while (remain); ++ ++ sg_miter->consumed = offset; ++ status = mci_readl(host, MINTSTS); ++ mci_writel(host, RINTSTS, SDMMC_INT_TXDR); ++ } while (status & SDMMC_INT_TXDR); /* if TXDR write again */ ++ ++ if (!remain) { ++ if (!sg_miter_next(sg_miter)) ++ goto done; ++ sg_miter->consumed = 0; ++ } ++ sg_miter_stop(sg_miter); ++ return; ++ ++done: ++ sg_miter_stop(sg_miter); ++ host->sg = NULL; ++ smp_wmb(); ++ set_bit(EVENT_XFER_COMPLETE, &host->pending_events); ++} ++ ++static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) ++{ ++ if (!host->cmd_status) ++ host->cmd_status = status; ++ ++ smp_wmb(); ++ ++ set_bit(EVENT_CMD_COMPLETE, &host->pending_events); ++ tasklet_schedule(&host->tasklet); ++} ++ ++static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) ++{ ++ struct dw_mci *host = dev_id; ++ u32 pending; ++ int i; ++ ++ pending = mci_readl(host, MINTSTS); /* read-only mask reg */ ++ ++ /* ++ * DTO fix - version 2.10a and below, and only if internal DMA ++ * is configured. ++ */ ++ if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) { ++ if (!pending && ++ ((mci_readl(host, STATUS) >> 17) & 0x1fff)) ++ pending |= SDMMC_INT_DATA_OVER; ++ } ++ ++ if (pending) { ++ /* Check volt switch first, since it can look like an error */ ++ if ((host->state == STATE_SENDING_CMD11) && ++ (pending & SDMMC_INT_VOLT_SWITCH)) { ++ mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); ++ pending &= ~SDMMC_INT_VOLT_SWITCH; ++ dw_mci_cmd_interrupt(host, pending); ++ } ++ ++ if (pending & DW_MCI_CMD_ERROR_FLAGS) { ++ mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); ++ host->cmd_status = pending; ++ smp_wmb(); ++ set_bit(EVENT_CMD_COMPLETE, &host->pending_events); ++ } ++ ++ if (pending & DW_MCI_DATA_ERROR_FLAGS) { ++ /* if there is an error report DATA_ERROR */ ++ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); ++ host->data_status = pending; ++ smp_wmb(); ++ set_bit(EVENT_DATA_ERROR, &host->pending_events); ++ tasklet_schedule(&host->tasklet); ++ } ++ ++ if (pending & SDMMC_INT_DATA_OVER) { ++ mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); ++ if (!host->data_status) ++ host->data_status = pending; ++ smp_wmb(); ++ if (host->dir_status == DW_MCI_RECV_STATUS) { ++ if (host->sg != NULL) ++ dw_mci_read_data_pio(host, true); ++ } ++ set_bit(EVENT_DATA_COMPLETE, &host->pending_events); ++ tasklet_schedule(&host->tasklet); ++ } ++ ++ if (pending & SDMMC_INT_RXDR) { ++ mci_writel(host, RINTSTS, SDMMC_INT_RXDR); ++ if (host->dir_status == DW_MCI_RECV_STATUS && host->sg) ++ dw_mci_read_data_pio(host, false); ++ } ++ ++ if (pending & SDMMC_INT_TXDR) { ++ mci_writel(host, RINTSTS, SDMMC_INT_TXDR); ++ if (host->dir_status == DW_MCI_SEND_STATUS && host->sg) ++ dw_mci_write_data_pio(host); ++ } ++ ++ if (pending & SDMMC_INT_CMD_DONE) { ++ mci_writel(host, RINTSTS, SDMMC_INT_CMD_DONE); ++ dw_mci_cmd_interrupt(host, pending); ++ } ++ ++ if (pending & SDMMC_INT_CD) { ++ mci_writel(host, RINTSTS, SDMMC_INT_CD); ++ queue_work(host->card_workqueue, &host->card_work); ++ } ++ ++ /* Handle SDIO Interrupts */ ++ for (i = 0; i < host->num_slots; i++) { ++ struct dw_mci_slot *slot = host->slot[i]; ++ if (pending & SDMMC_INT_SDIO(i)) { ++ mci_writel(host, RINTSTS, SDMMC_INT_SDIO(i)); ++ mmc_signal_sdio_irq(slot->mmc); ++ } ++ } ++ ++ } ++ ++#ifdef CONFIG_MMC_DW_IDMAC ++ /* Handle DMA interrupts */ ++ pending = mci_readl(host, IDSTS); ++ if (pending & (SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI)) { ++ mci_writel(host, IDSTS, SDMMC_IDMAC_INT_TI | SDMMC_IDMAC_INT_RI); ++ mci_writel(host, IDSTS, SDMMC_IDMAC_INT_NI); ++ host->dma_ops->complete(host); ++ } ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++static void dw_mci_work_routine_card(struct work_struct *work) ++{ ++ struct dw_mci *host = container_of(work, struct dw_mci, card_work); ++ int i; ++ ++ for (i = 0; i < host->num_slots; i++) { ++ struct dw_mci_slot *slot = host->slot[i]; ++ struct mmc_host *mmc = slot->mmc; ++ struct mmc_request *mrq; ++ int present; ++ ++ present = dw_mci_get_cd(mmc); ++ while (present != slot->last_detect_state) { ++ dev_dbg(&slot->mmc->class_dev, "card %s\n", ++ present ? "inserted" : "removed"); ++ ++ spin_lock_bh(&host->lock); ++ ++ /* Card change detected */ ++ slot->last_detect_state = present; ++ ++ /* Clean up queue if present */ ++ mrq = slot->mrq; ++ if (mrq) { ++ if (mrq == host->mrq) { ++ host->data = NULL; ++ host->cmd = NULL; ++ ++ switch (host->state) { ++ case STATE_IDLE: ++ case STATE_WAITING_CMD11_DONE: ++ break; ++ case STATE_SENDING_CMD11: ++ case STATE_SENDING_CMD: ++ mrq->cmd->error = -ENOMEDIUM; ++ if (!mrq->data) ++ break; ++ /* fall through */ ++ case STATE_SENDING_DATA: ++ mrq->data->error = -ENOMEDIUM; ++ dw_mci_stop_dma(host); ++ break; ++ case STATE_DATA_BUSY: ++ case STATE_DATA_ERROR: ++ if (mrq->data->error == -EINPROGRESS) ++ mrq->data->error = -ENOMEDIUM; ++ /* fall through */ ++ case STATE_SENDING_STOP: ++ if (mrq->stop) ++ mrq->stop->error = -ENOMEDIUM; ++ break; ++ } ++ ++ dw_mci_request_end(host, mrq); ++ } else { ++ list_del(&slot->queue_node); ++ mrq->cmd->error = -ENOMEDIUM; ++ if (mrq->data) ++ mrq->data->error = -ENOMEDIUM; ++ if (mrq->stop) ++ mrq->stop->error = -ENOMEDIUM; ++ ++ spin_unlock(&host->lock); ++ mmc_request_done(slot->mmc, mrq); ++ spin_lock(&host->lock); ++ } ++ } ++ ++ /* Power down slot */ ++ if (present == 0) ++ dw_mci_reset(host); ++ ++ spin_unlock_bh(&host->lock); ++ ++ present = dw_mci_get_cd(mmc); ++ } ++ ++ mmc_detect_change(slot->mmc, ++ msecs_to_jiffies(host->pdata->detect_delay_ms)); ++ } ++} ++ ++#ifdef CONFIG_OF ++/* given a slot id, find out the device node representing that slot */ ++static struct device_node *dw_mci_of_find_slot_node(struct device *dev, u8 slot) ++{ ++ struct device_node *np; ++ const __be32 *addr; ++ int len; ++ ++ if (!dev || !dev->of_node) ++ return NULL; ++ ++ for_each_child_of_node(dev->of_node, np) { ++ addr = of_get_property(np, "reg", &len); ++ if (!addr || (len < sizeof(int))) ++ continue; ++ if (be32_to_cpup(addr) == slot) ++ return np; ++ } ++ return NULL; ++} ++ ++static struct dw_mci_of_slot_quirks { ++ char *quirk; ++ int id; ++} of_slot_quirks[] = { ++ { ++ .quirk = "disable-wp", ++ .id = DW_MCI_SLOT_QUIRK_NO_WRITE_PROTECT, ++ }, ++}; ++ ++static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) ++{ ++ struct device_node *np = dw_mci_of_find_slot_node(dev, slot); ++ int quirks = 0; ++ int idx; ++ ++ /* get quirks */ ++ for (idx = 0; idx < ARRAY_SIZE(of_slot_quirks); idx++) ++ if (of_get_property(np, of_slot_quirks[idx].quirk, NULL)) { ++ dev_warn(dev, "Slot quirk %s is deprecated\n", ++ of_slot_quirks[idx].quirk); ++ quirks |= of_slot_quirks[idx].id; ++ } ++ ++ return quirks; ++} ++#else /* CONFIG_OF */ ++static int dw_mci_of_get_slot_quirks(struct device *dev, u8 slot) ++{ ++ return 0; ++} ++#endif /* CONFIG_OF */ ++ ++static int dw_mci_init_slot(struct dw_mci *host, unsigned int id) ++{ ++ struct mmc_host *mmc; ++ struct dw_mci_slot *slot; ++ const struct dw_mci_drv_data *drv_data = host->drv_data; ++ int ctrl_id, ret; ++ u32 freq[2]; ++ ++ mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev); ++ if (!mmc) ++ return -ENOMEM; ++ ++ slot = mmc_priv(mmc); ++ slot->id = id; ++ slot->mmc = mmc; ++ slot->host = host; ++ host->slot[id] = slot; ++ ++ slot->quirks = dw_mci_of_get_slot_quirks(host->dev, slot->id); ++ ++ mmc->ops = &dw_mci_ops; ++ if (of_property_read_u32_array(host->dev->of_node, ++ "clock-freq-min-max", freq, 2)) { ++ mmc->f_min = DW_MCI_FREQ_MIN; ++ mmc->f_max = DW_MCI_FREQ_MAX; ++ } else { ++ mmc->f_min = freq[0]; ++ mmc->f_max = freq[1]; ++ } ++ ++ /*if there are external regulators, get them*/ ++ ret = mmc_regulator_get_supply(mmc); ++ if (ret == -EPROBE_DEFER) ++ goto err_host_allocated; ++ ++ if (!mmc->ocr_avail) ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ if (host->pdata->caps) ++ mmc->caps = host->pdata->caps; ++ ++ if (host->pdata->pm_caps) ++ mmc->pm_caps = host->pdata->pm_caps; ++ ++ if (host->dev->of_node) { ++ ctrl_id = of_alias_get_id(host->dev->of_node, "mshc"); ++ if (ctrl_id < 0) ++ ctrl_id = 0; ++ } else { ++ ctrl_id = to_platform_device(host->dev)->id; ++ } ++ if (drv_data && drv_data->caps) ++ mmc->caps |= drv_data->caps[ctrl_id]; ++ ++ if (host->pdata->caps2) ++ mmc->caps2 = host->pdata->caps2; ++ ++ ret = mmc_of_parse(mmc); ++ if (ret) ++ goto err_host_allocated; ++ ++ if (host->pdata->blk_settings) { ++ mmc->max_segs = host->pdata->blk_settings->max_segs; ++ mmc->max_blk_size = host->pdata->blk_settings->max_blk_size; ++ mmc->max_blk_count = host->pdata->blk_settings->max_blk_count; ++ mmc->max_req_size = host->pdata->blk_settings->max_req_size; ++ mmc->max_seg_size = host->pdata->blk_settings->max_seg_size; ++ } else { ++ /* Useful defaults if platform data is unset. */ ++#ifdef CONFIG_MMC_DW_IDMAC ++ mmc->max_segs = host->ring_size; ++ mmc->max_blk_size = 65536; ++ mmc->max_blk_count = host->ring_size; ++ mmc->max_seg_size = 0x1000; ++ mmc->max_req_size = mmc->max_seg_size * mmc->max_blk_count; ++#else ++ mmc->max_segs = 64; ++ mmc->max_blk_size = 65536; /* BLKSIZ is 16 bits */ ++ mmc->max_blk_count = 512; ++ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; ++ mmc->max_seg_size = mmc->max_req_size; ++#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); ++ ++ ret = mmc_add_host(mmc); ++ if (ret) ++ goto err_host_allocated; ++ ++#if defined(CONFIG_DEBUG_FS) ++ dw_mci_init_debugfs(slot); ++#endif ++ ++ /* Card initially undetected */ ++ slot->last_detect_state = 0; ++ ++ return 0; ++ ++err_host_allocated: ++ mmc_free_host(mmc); ++ return ret; ++} ++ ++static void dw_mci_cleanup_slot(struct dw_mci_slot *slot, unsigned int id) ++{ ++ /* Debugfs stuff is cleaned up by mmc core */ ++ mmc_remove_host(slot->mmc); ++ slot->host->slot[id] = NULL; ++ mmc_free_host(slot->mmc); ++} ++ ++static void dw_mci_init_dma(struct dw_mci *host) ++{ ++ /* Alloc memory for sg translation */ ++ host->sg_cpu = dmam_alloc_coherent(host->dev, PAGE_SIZE, ++ &host->sg_dma, GFP_KERNEL); ++ if (!host->sg_cpu) { ++ dev_err(host->dev, "%s: could not alloc DMA memory\n", ++ __func__); ++ goto no_dma; ++ } ++ ++ /* Determine which DMA interface to use */ ++#ifdef CONFIG_MMC_DW_IDMAC ++ host->dma_ops = &dw_mci_idmac_ops; ++ dev_info(host->dev, "Using internal DMA controller.\n"); ++#endif ++ ++ if (!host->dma_ops) ++ goto no_dma; ++ ++ if (host->dma_ops->init && host->dma_ops->start && ++ host->dma_ops->stop && host->dma_ops->cleanup) { ++ if (host->dma_ops->init(host)) { ++ dev_err(host->dev, "%s: Unable to initialize " ++ "DMA Controller.\n", __func__); ++ goto no_dma; ++ } ++ } else { ++ dev_err(host->dev, "DMA initialization not found.\n"); ++ goto no_dma; ++ } ++ ++ host->use_dma = 1; ++ return; ++ ++no_dma: ++ dev_info(host->dev, "Using PIO mode.\n"); ++ host->use_dma = 0; ++ return; ++} ++ ++static bool dw_mci_ctrl_reset(struct dw_mci *host, u32 reset) ++{ ++ unsigned long timeout = jiffies + msecs_to_jiffies(500); ++ u32 ctrl; ++ ++ ctrl = mci_readl(host, CTRL); ++ ctrl |= reset; ++ mci_writel(host, CTRL, ctrl); ++ ++ /* wait till resets clear */ ++ do { ++ ctrl = mci_readl(host, CTRL); ++ if (!(ctrl & reset)) ++ return true; ++ } while (time_before(jiffies, timeout)); ++ ++ dev_err(host->dev, ++ "Timeout resetting block (ctrl reset %#x)\n", ++ ctrl & reset); ++ ++ return false; ++} ++ ++static bool dw_mci_reset(struct dw_mci *host) ++{ ++ u32 flags = SDMMC_CTRL_RESET | SDMMC_CTRL_FIFO_RESET; ++ bool ret = false; ++ ++ /* ++ * Reseting generates a block interrupt, hence setting ++ * the scatter-gather pointer to NULL. ++ */ ++ if (host->sg) { ++ sg_miter_stop(&host->sg_miter); ++ host->sg = NULL; ++ } ++ ++ if (host->use_dma) ++ flags |= SDMMC_CTRL_DMA_RESET; ++ ++ if (dw_mci_ctrl_reset(host, flags)) { ++ /* ++ * In all cases we clear the RAWINTS register to clear any ++ * interrupts. ++ */ ++ mci_writel(host, RINTSTS, 0xFFFFFFFF); ++ ++ /* if using dma we wait for dma_req to clear */ ++ if (host->use_dma) { ++ unsigned long timeout = jiffies + msecs_to_jiffies(500); ++ u32 status; ++ do { ++ status = mci_readl(host, STATUS); ++ if (!(status & SDMMC_STATUS_DMA_REQ)) ++ break; ++ cpu_relax(); ++ } while (time_before(jiffies, timeout)); ++ ++ if (status & SDMMC_STATUS_DMA_REQ) { ++ dev_err(host->dev, ++ "%s: Timeout waiting for dma_req to " ++ "clear during reset\n", __func__); ++ goto ciu_out; ++ } ++ ++ /* when using DMA next we reset the fifo again */ ++ if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_FIFO_RESET)) ++ goto ciu_out; ++ } ++ } else { ++ /* if the controller reset bit did clear, then set clock regs */ ++ if (!(mci_readl(host, CTRL) & SDMMC_CTRL_RESET)) { ++ dev_err(host->dev, "%s: fifo/dma reset bits didn't " ++ "clear but ciu was reset, doing clock update\n", ++ __func__); ++ goto ciu_out; ++ } ++ } ++ ++#if IS_ENABLED(CONFIG_MMC_DW_IDMAC) ++ /* It is also recommended that we reset and reprogram idmac */ ++ dw_mci_idmac_reset(host); ++#endif ++ ++ ret = true; ++ ++ciu_out: ++ /* After a CTRL reset we need to have CIU set clock registers */ ++ mci_send_cmd(host->cur_slot, SDMMC_CMD_UPD_CLK, 0); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_OF ++static struct dw_mci_of_quirks { ++ char *quirk; ++ int id; ++} of_quirks[] = { ++ { ++ .quirk = "broken-cd", ++ .id = DW_MCI_QUIRK_BROKEN_CARD_DETECTION, ++ }, { ++ .quirk = "disable-wp", ++ .id = DW_MCI_QUIRK_NO_WRITE_PROTECT, ++ }, ++}; ++ ++static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) ++{ ++ struct dw_mci_board *pdata; ++ struct device *dev = host->dev; ++ struct device_node *np = dev->of_node; ++ const struct dw_mci_drv_data *drv_data = host->drv_data; ++ int idx, ret; ++ u32 clock_frequency; ++ ++ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); ++ if (!pdata) { ++ dev_err(dev, "could not allocate memory for pdata\n"); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ /* find out number of slots supported */ ++ if (of_property_read_u32(dev->of_node, "num-slots", ++ &pdata->num_slots)) { ++ dev_info(dev, "num-slots property not found, " ++ "assuming 1 slot is available\n"); ++ pdata->num_slots = 1; ++ } ++ ++ /* get quirks */ ++ for (idx = 0; idx < ARRAY_SIZE(of_quirks); idx++) ++ if (of_get_property(np, of_quirks[idx].quirk, NULL)) ++ pdata->quirks |= of_quirks[idx].id; ++ ++ if (of_property_read_u32(np, "fifo-depth", &pdata->fifo_depth)) ++ dev_info(dev, "fifo-depth property not found, using " ++ "value of FIFOTH register as default\n"); ++ ++ of_property_read_u32(np, "card-detect-delay", &pdata->detect_delay_ms); ++ ++ if (!of_property_read_u32(np, "clock-frequency", &clock_frequency)) ++ pdata->bus_hz = clock_frequency; ++ ++ if (drv_data && drv_data->parse_dt) { ++ ret = drv_data->parse_dt(host); ++ if (ret) ++ return ERR_PTR(ret); ++ } ++ ++ if (of_find_property(np, "supports-highspeed", NULL)) ++ pdata->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; ++ ++ return pdata; ++} ++ ++#else /* CONFIG_OF */ ++static struct dw_mci_board *dw_mci_parse_dt(struct dw_mci *host) ++{ ++ return ERR_PTR(-EINVAL); ++} ++#endif /* CONFIG_OF */ ++ ++int dw_mci_probe(struct dw_mci *host) ++{ ++ const struct dw_mci_drv_data *drv_data = host->drv_data; ++ int width, i, ret = 0; ++ u32 fifo_size; ++ int init_slots = 0; ++ ++ if (!host->pdata) { ++ host->pdata = dw_mci_parse_dt(host); ++ if (IS_ERR(host->pdata)) { ++ dev_err(host->dev, "platform data not available\n"); ++ return -EINVAL; ++ } ++ } ++ ++ if (host->pdata->num_slots > 1) { ++ dev_err(host->dev, ++ "Platform data must supply num_slots.\n"); ++ return -ENODEV; ++ } ++ ++ host->biu_clk = devm_clk_get(host->dev, "biu"); ++ if (IS_ERR(host->biu_clk)) { ++ dev_dbg(host->dev, "biu clock not available\n"); ++ } else { ++ ret = clk_prepare_enable(host->biu_clk); ++ if (ret) { ++ dev_err(host->dev, "failed to enable biu clock\n"); ++ return ret; ++ } ++ } ++ ++ host->ciu_clk = devm_clk_get(host->dev, "ciu"); ++ if (IS_ERR(host->ciu_clk)) { ++ dev_dbg(host->dev, "ciu clock not available\n"); ++ host->bus_hz = host->pdata->bus_hz; ++ } else { ++ ret = clk_prepare_enable(host->ciu_clk); ++ if (ret) { ++ dev_err(host->dev, "failed to enable ciu clock\n"); ++ goto err_clk_biu; ++ } ++ ++ if (host->pdata->bus_hz) { ++ ret = clk_set_rate(host->ciu_clk, host->pdata->bus_hz); ++ if (ret) ++ dev_warn(host->dev, ++ "Unable to set bus rate to %uHz\n", ++ host->pdata->bus_hz); ++ } ++ host->bus_hz = clk_get_rate(host->ciu_clk); ++ } ++ ++ if (!host->bus_hz) { ++ dev_err(host->dev, ++ "Platform data must supply bus speed\n"); ++ ret = -ENODEV; ++ goto err_clk_ciu; ++ } ++ ++ if (drv_data && drv_data->init) { ++ ret = drv_data->init(host); ++ if (ret) { ++ dev_err(host->dev, ++ "implementation specific init failed\n"); ++ goto err_clk_ciu; ++ } ++ } ++ ++ if (drv_data && drv_data->setup_clock) { ++ ret = drv_data->setup_clock(host); ++ if (ret) { ++ dev_err(host->dev, ++ "implementation specific clock setup failed\n"); ++ goto err_clk_ciu; ++ } ++ } ++ ++ host->quirks = host->pdata->quirks; ++ ++ spin_lock_init(&host->lock); ++ INIT_LIST_HEAD(&host->queue); ++ ++ /* ++ * Get the host data width - this assumes that HCON has been set with ++ * the correct values. ++ */ ++ i = (mci_readl(host, HCON) >> 7) & 0x7; ++ if (!i) { ++ host->push_data = dw_mci_push_data16; ++ host->pull_data = dw_mci_pull_data16; ++ width = 16; ++ host->data_shift = 1; ++ } else if (i == 2) { ++ host->push_data = dw_mci_push_data64; ++ host->pull_data = dw_mci_pull_data64; ++ width = 64; ++ host->data_shift = 3; ++ } else { ++ /* Check for a reserved value, and warn if it is */ ++ WARN((i != 1), ++ "HCON reports a reserved host data width!\n" ++ "Defaulting to 32-bit access.\n"); ++ host->push_data = dw_mci_push_data32; ++ host->pull_data = dw_mci_pull_data32; ++ width = 32; ++ host->data_shift = 2; ++ } ++ ++ /* Reset all blocks */ ++ if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) ++ return -ENODEV; ++ ++ host->dma_ops = host->pdata->dma_ops; ++ dw_mci_init_dma(host); ++ ++ /* Clear the interrupts for the host controller */ ++ mci_writel(host, RINTSTS, 0xFFFFFFFF); ++ mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ ++ ++ /* Put in max timeout */ ++ mci_writel(host, TMOUT, 0xFFFFFFFF); ++ ++ /* ++ * FIFO threshold settings RxMark = fifo_size / 2 - 1, ++ * Tx Mark = fifo_size / 2 DMA Size = 8 ++ */ ++ if (!host->pdata->fifo_depth) { ++ /* ++ * Power-on value of RX_WMark is FIFO_DEPTH-1, but this may ++ * have been overwritten by the bootloader, just like we're ++ * about to do, so if you know the value for your hardware, you ++ * should put it in the platform data. ++ */ ++ fifo_size = mci_readl(host, FIFOTH); ++ fifo_size = 1 + ((fifo_size >> 16) & 0xfff); ++ } else { ++ fifo_size = host->pdata->fifo_depth; ++ } ++ host->fifo_depth = fifo_size; ++ host->fifoth_val = ++ SDMMC_SET_FIFOTH(0x2, fifo_size / 2 - 1, fifo_size / 2); ++ mci_writel(host, FIFOTH, host->fifoth_val); ++ ++ /* disable clock to CIU */ ++ mci_writel(host, CLKENA, 0); ++ mci_writel(host, CLKSRC, 0); ++ ++ /* ++ * In 2.40a spec, Data offset is changed. ++ * Need to check the version-id and set data-offset for DATA register. ++ */ ++ host->verid = SDMMC_GET_VERID(mci_readl(host, VERID)); ++ dev_info(host->dev, "Version ID is %04x\n", host->verid); ++ ++ if (host->verid < DW_MMC_240A) ++ host->data_offset = DATA_OFFSET; ++ else ++ host->data_offset = DATA_240A_OFFSET; ++ ++ tasklet_init(&host->tasklet, dw_mci_tasklet_func, (unsigned long)host); ++ host->card_workqueue = alloc_workqueue("dw-mci-card", ++ WQ_MEM_RECLAIM, 1); ++ if (!host->card_workqueue) { ++ ret = -ENOMEM; ++ goto err_dmaunmap; ++ } ++ INIT_WORK(&host->card_work, dw_mci_work_routine_card); ++ ret = devm_request_irq(host->dev, host->irq, dw_mci_interrupt, ++ host->irq_flags, "dw-mci", host); ++ if (ret) ++ goto err_workqueue; ++ ++ if (host->pdata->num_slots) ++ host->num_slots = host->pdata->num_slots; ++ else ++ host->num_slots = ((mci_readl(host, HCON) >> 1) & 0x1F) + 1; ++ ++ /* ++ * Enable interrupts for command done, data over, data empty, card det, ++ * receive ready and error such as transmit, receive timeout, crc error ++ */ ++ mci_writel(host, RINTSTS, 0xFFFFFFFF); ++ mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | ++ SDMMC_INT_TXDR | SDMMC_INT_RXDR | ++ DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); ++ mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); /* Enable mci interrupt */ ++ ++ dev_info(host->dev, "DW MMC controller at irq %d, " ++ "%d bit host data width, " ++ "%u deep fifo\n", ++ host->irq, width, fifo_size); ++ ++ /* We need at least one slot to succeed */ ++ for (i = 0; i < host->num_slots; i++) { ++ ret = dw_mci_init_slot(host, i); ++ if (ret) ++ dev_dbg(host->dev, "slot %d init failed\n", i); ++ else ++ init_slots++; ++ } ++ ++ if (init_slots) { ++ dev_info(host->dev, "%d slots initialized\n", init_slots); ++ } else { ++ dev_dbg(host->dev, "attempted to initialize %d slots, " ++ "but failed on all\n", host->num_slots); ++ goto err_workqueue; ++ } ++ ++ if (host->quirks & DW_MCI_QUIRK_IDMAC_DTO) ++ dev_info(host->dev, "Internal DMAC interrupt fix enabled.\n"); ++ ++ return 0; ++ ++err_workqueue: ++ destroy_workqueue(host->card_workqueue); ++ ++err_dmaunmap: ++ if (host->use_dma && host->dma_ops->exit) ++ host->dma_ops->exit(host); ++ ++err_clk_ciu: ++ if (!IS_ERR(host->ciu_clk)) ++ clk_disable_unprepare(host->ciu_clk); ++ ++err_clk_biu: ++ if (!IS_ERR(host->biu_clk)) ++ clk_disable_unprepare(host->biu_clk); ++ ++ return ret; ++} ++EXPORT_SYMBOL(dw_mci_probe); ++ ++void dw_mci_remove(struct dw_mci *host) ++{ ++ int i; ++ ++ mci_writel(host, RINTSTS, 0xFFFFFFFF); ++ mci_writel(host, INTMASK, 0); /* disable all mmc interrupt first */ ++ ++ for (i = 0; i < host->num_slots; i++) { ++ dev_dbg(host->dev, "remove slot %d\n", i); ++ if (host->slot[i]) ++ dw_mci_cleanup_slot(host->slot[i], i); ++ } ++ ++ /* disable clock to CIU */ ++ mci_writel(host, CLKENA, 0); ++ mci_writel(host, CLKSRC, 0); ++ ++ destroy_workqueue(host->card_workqueue); ++ ++ if (host->use_dma && host->dma_ops->exit) ++ host->dma_ops->exit(host); ++ ++ if (!IS_ERR(host->ciu_clk)) ++ clk_disable_unprepare(host->ciu_clk); ++ ++ if (!IS_ERR(host->biu_clk)) ++ clk_disable_unprepare(host->biu_clk); ++} ++EXPORT_SYMBOL(dw_mci_remove); ++ ++ ++ ++#ifdef CONFIG_PM_SLEEP ++/* ++ * TODO: we should probably disable the clock to the card in the suspend path. ++ */ ++int dw_mci_suspend(struct dw_mci *host) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(dw_mci_suspend); ++ ++int dw_mci_resume(struct dw_mci *host) ++{ ++ int i, ret; ++ ++ if (!dw_mci_ctrl_reset(host, SDMMC_CTRL_ALL_RESET_FLAGS)) { ++ ret = -ENODEV; ++ return ret; ++ } ++ ++ if (host->use_dma && host->dma_ops->init) ++ host->dma_ops->init(host); ++ ++ /* ++ * Restore the initial value at FIFOTH register ++ * And Invalidate the prev_blksz with zero ++ */ ++ mci_writel(host, FIFOTH, host->fifoth_val); ++ host->prev_blksz = 0; ++ ++ /* Put in max timeout */ ++ mci_writel(host, TMOUT, 0xFFFFFFFF); ++ ++ mci_writel(host, RINTSTS, 0xFFFFFFFF); ++ mci_writel(host, INTMASK, SDMMC_INT_CMD_DONE | SDMMC_INT_DATA_OVER | ++ SDMMC_INT_TXDR | SDMMC_INT_RXDR | ++ DW_MCI_ERROR_FLAGS | SDMMC_INT_CD); ++ mci_writel(host, CTRL, SDMMC_CTRL_INT_ENABLE); ++ ++ for (i = 0; i < host->num_slots; i++) { ++ struct dw_mci_slot *slot = host->slot[i]; ++ if (!slot) ++ continue; ++ if (slot->mmc->pm_flags & MMC_PM_KEEP_POWER) { ++ dw_mci_set_ios(slot->mmc, &slot->mmc->ios); ++ dw_mci_setup_bus(slot, true); ++ } ++ } ++ return 0; ++} ++EXPORT_SYMBOL(dw_mci_resume); ++#endif /* CONFIG_PM_SLEEP */ ++ ++static int __init dw_mci_init(void) ++{ ++ pr_info("Synopsys Designware Multimedia Card Interface Driver\n"); ++ return 0; ++} ++ ++static void __exit dw_mci_exit(void) ++{ ++} ++ ++module_init(dw_mci_init); ++module_exit(dw_mci_exit); ++ ++MODULE_DESCRIPTION("DW Multimedia Card Interface driver"); ++MODULE_AUTHOR("NXP Semiconductor VietNam"); ++MODULE_AUTHOR("Imagination Technologies Ltd"); ++MODULE_LICENSE("GPL v2"); +diff -Nur linux-3.18.8.orig/include/linux/mmc/host.h linux-3.18.8/include/linux/mmc/host.h +--- linux-3.18.8.orig/include/linux/mmc/host.h 2015-02-27 02:49:36.000000000 +0100 ++++ linux-3.18.8/include/linux/mmc/host.h 2015-03-02 03:25:33.000000000 +0100 +@@ -305,6 +305,11 @@ + unsigned long clkgate_delay; + #endif + ++ /* card specific properties to deal with power and reset */ ++ struct regulator *card_regulator; /* External VCC needed by the card */ ++ struct gpio_desc *card_reset_gpios[2]; /* External resets, active low */ ++ struct clk *card_clk; /* External clock needed by the card */ ++ + /* host specific block data */ + unsigned int max_seg_size; /* see blk_queue_max_segment_size */ + unsigned short max_segs; /* see blk_queue_max_segments */ |