diff options
47 files changed, 1825 insertions, 2918 deletions
@@ -3113,6 +3113,11 @@ N: Peter Oruba D: AMD Microcode loader driver S: Germany +N: Pierre Ossman +D: MMC maintainer 2006-2009 +D: First SDIO implementation +S: Sweden + N: Jens Osterkamp E: jens@de.ibm.com D: Maintainer of Spidernet network driver for Cell diff --git a/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml b/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml index 9e9610bb1eda..c2ec7151dccf 100644 --- a/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/allwinner,sun4i-a10-mmc.yaml @@ -32,10 +32,12 @@ properties: - items: - enum: - allwinner,sun8i-a83t-mmc + - allwinner,sun8i-b288-mmc - allwinner,suniv-f1c100s-mmc - const: allwinner,sun7i-a20-mmc - items: - enum: + - allwinner,sun8i-b288-emmc - allwinner,sun8i-r40-emmc - allwinner,sun50i-h5-emmc - allwinner,sun50i-h6-emmc diff --git a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml index 976f36de2091..12342b1d78e5 100644 --- a/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml +++ b/Documentation/devicetree/bindings/mmc/amlogic,meson-gx-mmc.yaml @@ -15,6 +15,23 @@ maintainers: allOf: - $ref: mmc-controller.yaml# + - if: + properties: + compatible: + contains: + const: amlogic,t7-mmc + then: + properties: + clocks: + minItems: 4 + clock-names: + minItems: 4 + else: + properties: + clocks: + maxItems: 3 + clock-names: + maxItems: 3 properties: compatible: @@ -38,13 +55,23 @@ properties: - description: card detect clocks: - maxItems: 3 + minItems: 3 + items: + - description: core clock + - description: clock input 0 of the internal mux + - description: clock input 1 of the internal mux + - description: + clock of the pipeline stage inserted in the bus path between + the controller and the DRAM. Without it, the controller cannot + complete DMA transfers. clock-names: + minItems: 3 items: - const: core - const: clkin0 - const: clkin1 + - const: pipeline resets: maxItems: 1 diff --git a/Documentation/devicetree/bindings/mmc/cdns,sd6hc.yaml b/Documentation/devicetree/bindings/mmc/cdns,sd6hc.yaml new file mode 100644 index 000000000000..d5ea2717904b --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/cdns,sd6hc.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/cdns,sd6hc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Cadence SD6HC SD/SDIO/eMMC Host Controller + +maintainers: + - Tanmay Kathpalia <tanmay.kathpalia@altera.com> + +description: + Cadence SD6HC is a sixth-generation SD/SDIO/eMMC host controller with an + interface to a Cadence Combo PHY. The controller has separate card interface + and bus interface clock domains. + +properties: + compatible: + items: + - const: altr,agilex5-sd6hc + - const: cdns,sd6hc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + items: + - description: card interface unit (CIU / SDMCLK) clock + - description: bus interface unit (BIU) clock + + clock-names: + items: + - const: ciu + - const: biu + + resets: + items: + - description: SDHC controller reset + - description: Combo PHY reset + - description: SDMMC OCP/AXI reset + + reset-names: + items: + - const: sdhc-reset + - const: combophy + - const: sdmmc-ocp + + iommus: + maxItems: 1 + + dma-coherent: true + + cdns,iocell-input-delay-ps: + description: + Input delay across the IO cells, used for SD6HC PHY timing calculations. + minimum: 0 + maximum: 20000 + default: 2500 + + cdns,iocell-output-delay-ps: + description: + Output delay across the IO cells, used for SD6HC PHY timing calculations. + minimum: 0 + maximum: 20000 + default: 2500 + + cdns,delay-element-ps: + description: + DLL delay element size, used for SD6HC PHY timing calculations. + minimum: 1 + maximum: 1000 + default: 24 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - resets + - reset-names + +allOf: + - $ref: sdhci-common.yaml# + +unevaluatedProperties: false + +examples: + - | + #include <dt-bindings/interrupt-controller/arm-gic.h> + + mmc@10808000 { + compatible = "altr,agilex5-sd6hc", "cdns,sd6hc"; + reg = <0x10808000 0x1000>; + interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&clkmgr 0>, <&clkmgr 1>; + clock-names = "ciu", "biu"; + resets = <&rst 0>, <&rst 1>, <&rst 2>; + reset-names = "sdhc-reset", "combophy", "sdmmc-ocp"; + iommus = <&smmu 5>; + dma-coherent; + }; diff --git a/Documentation/devicetree/bindings/mmc/mmc-controller-common.yaml b/Documentation/devicetree/bindings/mmc/mmc-controller-common.yaml index 3d7195e9461c..8b9b3b036129 100644 --- a/Documentation/devicetree/bindings/mmc/mmc-controller-common.yaml +++ b/Documentation/devicetree/bindings/mmc/mmc-controller-common.yaml @@ -275,6 +275,16 @@ properties: not available. default: 10 + power-off-delay-us: + description: + Delay in microseconds after card power is deasserted during a power + cycle to allow time for proper power discharge. Larger values can be + configured to work around hardware issues such as slow RC discharge + on MMC VDD rails. + minimum: 0 + maximum: 10000000 + default: 1000 + supports-cqe: $ref: /schemas/types.yaml#/definitions/flag description: diff --git a/Documentation/devicetree/bindings/mmc/qcom,sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/qcom,sdhci-msm.yaml index b3fcc1673c10..9d4409c958fa 100644 --- a/Documentation/devicetree/bindings/mmc/qcom,sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/qcom,sdhci-msm.yaml @@ -26,6 +26,7 @@ properties: - qcom,ipq4019-sdhci - qcom,ipq8074-sdhci - qcom,msm8226-sdhci + - qcom,msm8952-sdhci - qcom,msm8953-sdhci - qcom,msm8974-sdhci - qcom,msm8976-sdhci @@ -47,6 +48,7 @@ properties: - qcom,ipq9574-sdhci - qcom,ipq9650-sdhci - qcom,kaanapali-sdhci + - qcom,maili-sdhci - qcom,milos-sdhci - qcom,qcm2290-sdhci - qcom,qcs404-sdhci diff --git a/Documentation/driver-api/mmc/mmc-async-req.rst b/Documentation/driver-api/mmc/mmc-async-req.rst index 0f7197c9c3b5..d9d4ac74b846 100644 --- a/Documentation/driver-api/mmc/mmc-async-req.rst +++ b/Documentation/driver-api/mmc/mmc-async-req.rst @@ -23,7 +23,7 @@ MMC request. MMC block driver ================ -The mmc_blk_issue_rw_rq() in the MMC block driver is made non-blocking. +The mmc_blk_mq_issue_rw_rq() in the MMC block driver is made non-blocking. The increase in throughput is proportional to the time it takes to prepare (major part of preparations are dma_map_sg() and dma_unmap_sg()) @@ -34,21 +34,20 @@ platform. In power save mode, when clocks run on a lower frequency, the DMA preparation may cost even more. As long as these slower preparations are run in parallel with the transfer performance won't be affected. -Details on measurements from IOZone and mmc_test -================================================ +MMC core API +============ -https://wiki.linaro.org/WorkingGroups/Kernel/Specs/StoragePerfMMC-async-req +The preparation of a request is separated from starting the transfer, so +that a host can prepare a request while another one is still in progress: -MMC core API extension -====================== - -There is one new public function mmc_start_req(). - -It starts a new MMC command request for a host. The function isn't -truly non-blocking. If there is an ongoing async request it waits -for completion of that request and starts the new one and returns. It -doesn't wait for the new request to complete. If there is no ongoing -request it starts the new request and returns immediately. + * mmc_pre_req() prepares a request before it is started. It may be called + while another request is running on the host. + * mmc_start_request() starts a prepared request without waiting for it to + complete. mmc_wait_for_req() starts a request and waits for it to + complete as well. + * mmc_post_req() releases the resources allocated by mmc_pre_req() after + the request has completed. It may likewise run while another request is + active. MMC host extensions =================== @@ -59,40 +58,3 @@ to before and after the actual mmc_host_ops.request() function is called. In the DMA case pre_req() may do dma_map_sg() and prepare the DMA descriptor, and post_req() runs the dma_unmap_sg(). - -Optimize for the first request -============================== - -The first request in a series of requests can't be prepared in parallel -with the previous transfer, since there is no previous request. - -The argument is_first_req in pre_req() indicates that there is no previous -request. The host driver may optimize for this scenario to minimize -the performance loss. A way to optimize for this is to split the current -request in two chunks, prepare the first chunk and start the request, -and finally prepare the second chunk and start the transfer. - -Pseudocode to handle is_first_req scenario with minimal prepare overhead:: - - if (is_first_req && req->size > threshold) - /* start MMC transfer for the complete transfer size */ - mmc_start_command(MMC_CMD_TRANSFER_FULL_SIZE); - - /* - * Begin to prepare DMA while cmd is being processed by MMC. - * The first chunk of the request should take the same time - * to prepare as the "MMC process command time". - * If prepare time exceeds MMC cmd time - * the transfer is delayed, guesstimate max 4k as first chunk size. - */ - prepare_1st_chunk_for_dma(req); - /* flush pending desc to the DMAC (dmaengine.h) */ - dma_issue_pending(req->dma_desc); - - prepare_2nd_chunk_for_dma(req); - /* - * The second issue_pending should be called before MMC runs out - * of the first chunk. If the MMC runs out of the first data chunk - * before this call, the transfer is delayed. - */ - dma_issue_pending(req->dma_desc); diff --git a/Documentation/driver-api/mmc/mmc-tools.rst b/Documentation/driver-api/mmc/mmc-tools.rst index eee1c2ccfa8f..728d3aa67eda 100644 --- a/Documentation/driver-api/mmc/mmc-tools.rst +++ b/Documentation/driver-api/mmc/mmc-tools.rst @@ -32,6 +32,7 @@ The mmc-utils tools can do the following: - Write to rpmb device from data file. - Enable the eMMC cache feature. - Disable the eMMC cache feature. + - Print the eMMC health status. - Print and parse CID data. - Print and parse CSD data. - Print and parse SCR data. diff --git a/MAINTAINERS b/MAINTAINERS index c5bce14d76ec..22587fce224c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -24807,6 +24807,13 @@ L: linux-mmc@vger.kernel.org S: Maintained F: drivers/mmc/host/sdhci-brcmstb* +SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) CADENCE DRIVER +M: Tanmay Kathpalia <tanmay.kathpalia@altera.com> +L: linux-mmc@vger.kernel.org +S: Supported +F: Documentation/devicetree/bindings/mmc/cdns,sd6hc.yaml +F: drivers/mmc/host/sdhci-cadence* + SECURE DIGITAL HOST CONTROLLER INTERFACE (SDHCI) DRIVER M: Adrian Hunter <adrian.hunter@intel.com> L: linux-mmc@vger.kernel.org @@ -29417,11 +29424,6 @@ L: linux-hwmon@vger.kernel.org S: Maintained F: drivers/hwmon/vt8231.c -VUB300 USB to SDIO/SD/MMC bridge chip -L: linux-mmc@vger.kernel.org -S: Orphan -F: drivers/mmc/host/vub300.c - W1 DALLAS'S 1-WIRE BUS M: Krzysztof Kozlowski <krzk@kernel.org> S: Maintained diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index 65154e569a9e..cd4ff1b79a6d 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -720,7 +720,7 @@ static int h_msb_parallel_switch(struct memstick_dev *card, static int msb_switch_to_parallel(struct msb_data *msb); -/* Reset the card, to guard against hw errors beeing treated as bad blocks */ +/* Reset the card, to guard against hw errors being treated as bad blocks */ static int msb_reset(struct msb_data *msb, bool full) { diff --git a/drivers/memstick/host/r592.c b/drivers/memstick/host/r592.c index 605b2265536f..dc3de63901be 100644 --- a/drivers/memstick/host/r592.c +++ b/drivers/memstick/host/r592.c @@ -289,7 +289,7 @@ static int r592_transfer_fifo_dma(struct r592_device *dev) dev->dma_error = 0; reinit_completion(&dev->dma_done); - /* TODO: hidden assumption about nenth beeing always 1 */ + /* TODO: hidden assumption about nenth being always 1 */ sg_count = dma_map_sg(&dev->pci_dev->dev, &dev->req->sg, 1, is_write ? DMA_TO_DEVICE : DMA_FROM_DEVICE); diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index e760d13ee325..a02bb2fee874 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -86,7 +86,7 @@ MODULE_ALIAS("mmc:block"); static DEFINE_MUTEX(block_mutex); /* - * The defaults come from config options but can be overriden by module + * The defaults come from config options but can be overridden by module * or bootarg options. */ static int perdev_minors = CONFIG_MMC_BLOCK_MINORS; diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 29e80e5f928e..677e445dc48c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -589,8 +589,8 @@ EXPORT_SYMBOL(mmc_cqe_recovery); * mmc_is_req_done() is used with requests that have * mrq->cap_cmd_during_tfr = true. mmc_is_req_done() must be called after * starting a request and before waiting for it to complete. That is, - * either in between calls to mmc_start_req(), or after mmc_wait_for_req() - * and before mmc_wait_for_req_done(). If it is called at other times the + * after mmc_start_request() or mmc_wait_for_req() and before + * mmc_wait_for_req_done(). If it is called at other times the * result is not meaningful. */ bool mmc_is_req_done(struct mmc_host *host, struct mmc_request *mrq) @@ -1388,7 +1388,7 @@ void mmc_power_off(struct mmc_host *host) * XO-1.5, require a short delay after poweroff before the card * can be successfully turned on again. */ - mmc_delay(1); + mmc_delay_us(host->ios.power_off_delay_us); } void mmc_power_cycle(struct mmc_host *host, u32 ocr) @@ -2004,7 +2004,7 @@ unsigned int mmc_calc_max_discard(struct mmc_card *card) /* * Without erase_group_def set, MMC erase timeout depends on clock - * frequence which can change. In that case, the best choice is + * frequency which can change. In that case, the best choice is * just the preferred erase size. */ if (mmc_card_mmc(card) && !(card->ext_csd.erase_group_def & 1)) diff --git a/drivers/mmc/core/core.h b/drivers/mmc/core/core.h index a028b48be164..9be2875608ce 100644 --- a/drivers/mmc/core/core.h +++ b/drivers/mmc/core/core.h @@ -73,6 +73,14 @@ static inline void mmc_delay(unsigned int ms) msleep(ms); } +static inline void mmc_delay_us(unsigned int us) +{ + if (us == 0) + return; + + fsleep(us); +} + void mmc_rescan(struct work_struct *work); void mmc_start_host(struct mmc_host *host); void __mmc_stop_host(struct mmc_host *host); @@ -162,7 +170,7 @@ int mmc_cqe_recovery(struct mmc_host *host); * @host: MMC host to prepare command * @mrq: MMC request to prepare for * - * mmc_pre_req() is called in prior to mmc_start_req() to let + * mmc_pre_req() is called in prior to mmc_start_request() to let * host prepare for the new request. Preparation of a request may be * performed while another request is running on the host. */ diff --git a/drivers/mmc/core/crypto.c b/drivers/mmc/core/crypto.c index fec4fbf16a5b..a5a90bfc634e 100644 --- a/drivers/mmc/core/crypto.c +++ b/drivers/mmc/core/crypto.c @@ -15,7 +15,7 @@ void mmc_crypto_set_initial_state(struct mmc_host *host) { /* Reset might clear all keys, so reprogram all the keys. */ - if (host->caps2 & MMC_CAP2_CRYPTO) + if ((host->caps2 & MMC_CAP2_CRYPTO) && !(host->caps2 & MMC_CAP2_CRYPTO_NO_REPROG)) blk_crypto_reprogram_all_keys(&host->crypto_profile); } diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 828078b3f962..8e9e61839198 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -421,6 +421,9 @@ int mmc_of_parse(struct mmc_host *host) device_property_read_u32(dev, "post-power-on-delay-ms", &host->ios.power_delay_ms); + device_property_read_u32(dev, "power-off-delay-us", + &host->ios.power_off_delay_us); + return mmc_pwrseq_alloc(host); } @@ -502,14 +505,7 @@ static int mmc_first_nonreserved_index(void) return max + 1; } -/** - * mmc_alloc_host - initialise the per-host structure. - * @extra: sizeof private data structure - * @dev: pointer to host device model structure - * - * Initialise the per-host structure. - */ -struct mmc_host *mmc_alloc_host(int extra, struct device *dev) +static struct mmc_host *mmc_alloc_host(struct device *dev, int extra) { int index; struct mmc_host *host; @@ -574,12 +570,19 @@ struct mmc_host *mmc_alloc_host(int extra, struct device *dev) host->fixed_drv_type = -EINVAL; host->ios.power_delay_ms = 10; + host->ios.power_off_delay_us = 1000; host->ios.power_mode = MMC_POWER_UNDEFINED; return host; } -EXPORT_SYMBOL(mmc_alloc_host); +static void mmc_free_host(struct mmc_host *host) +{ + cancel_delayed_work_sync(&host->detect); + cancel_work_sync(&host->sdio_irq_work); + mmc_pwrseq_free(host); + put_device(&host->class_dev); +} static void devm_mmc_host_release(struct device *dev, void *res) { @@ -594,7 +597,7 @@ struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra) if (!dr) return NULL; - host = mmc_alloc_host(extra, dev); + host = mmc_alloc_host(dev, extra); if (!host) { devres_free(dr); return NULL; @@ -688,19 +691,3 @@ void mmc_remove_host(struct mmc_host *host) } EXPORT_SYMBOL(mmc_remove_host); - -/** - * mmc_free_host - free the host structure - * @host: mmc host - * - * Free the host once all references to it have been dropped. - */ -void mmc_free_host(struct mmc_host *host) -{ - cancel_delayed_work_sync(&host->detect); - cancel_work_sync(&host->sdio_irq_work); - mmc_pwrseq_free(host); - put_device(&host->class_dev); -} - -EXPORT_SYMBOL(mmc_free_host); diff --git a/drivers/mmc/core/mmc.c b/drivers/mmc/core/mmc.c index 05444ecf3909..a02b04938e1a 100644 --- a/drivers/mmc/core/mmc.c +++ b/drivers/mmc/core/mmc.c @@ -1015,7 +1015,7 @@ static void mmc_set_bus_speed(struct mmc_card *card) } /* - * Select the bus width amoung 4-bit and 8-bit(SDR). + * Select the bus width among 4-bit and 8-bit(SDR). * If the bus width is changed successfully, return the selected width value. * Zero is returned instead of error value if the wide width is not supported. */ diff --git a/drivers/mmc/core/mmc_ops.c b/drivers/mmc/core/mmc_ops.c index a952cc8265af..d59ff82fb899 100644 --- a/drivers/mmc/core/mmc_ops.c +++ b/drivers/mmc/core/mmc_ops.c @@ -673,7 +673,8 @@ int mmc_switch(struct mmc_card *card, u8 set, u8 index, u8 value, } EXPORT_SYMBOL_GPL(mmc_switch); -int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error) +int mmc_send_tuning_timeout(struct mmc_host *host, u32 opcode, int *cmd_error, + u32 timeout_ms) { struct mmc_request mrq = {}; struct mmc_command cmd = {}; @@ -707,12 +708,7 @@ int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error) data.blocks = 1; data.flags = MMC_DATA_READ; - /* - * According to the tuning specs, Tuning process - * is normally shorter 40 executions of CMD19, - * and timeout value should be shorter than 150 ms - */ - data.timeout_ns = 150 * NSEC_PER_MSEC; + data.timeout_ns = timeout_ms * NSEC_PER_MSEC; data.sg = &sg; data.sg_len = 1; @@ -740,6 +736,18 @@ out: kfree(data_buf); return err; } +EXPORT_SYMBOL_GPL(mmc_send_tuning_timeout); + +int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error) +{ + /* + * The eMMC and SD specs give the tuning process an overall + * budget of 150 ms. mmc_send_tuning() simply applies that as + * the timeout of every single tuning command. Drivers which + * want a different timeout can use mmc_send_tuning_timeout(). + */ + return mmc_send_tuning_timeout(host, opcode, cmd_error, 150); +} EXPORT_SYMBOL_GPL(mmc_send_tuning); int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode) diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c index afaa6cab1adc..0b818cb478ce 100644 --- a/drivers/mmc/core/sdio_cis.c +++ b/drivers/mmc/core/sdio_cis.c @@ -57,12 +57,11 @@ static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, return -ENOMEM; string = (char*)(buffer + nr_strings); + memcpy(string, buf, size); for (i = 0; i < nr_strings; i++) { buffer[i] = string; - strcpy(string, buf); - string += strlen(string) + 1; - buf += strlen(buf) + 1; + string = strchr(string, 0) + 1; } if (func) { diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig index 4f060d3e5636..79bfc425e7c7 100644 --- a/drivers/mmc/host/Kconfig +++ b/drivers/mmc/host/Kconfig @@ -917,37 +917,6 @@ config MMC_JZ4740 If you have a board based on such a SoC and with a SD/MMC slot, say Y or M here. -config MMC_VUB300 - tristate "VUB300 USB to SDIO/SD/MMC Host Controller support" - depends on USB - help - This selects support for Elan Digital Systems' VUB300 chip. - - The VUB300 is a USB-SDIO Host Controller Interface chip - that enables the host computer to use SDIO/SD/MMC cards - via a USB 2.0 or USB 1.1 host. - - The VUB300 chip will be found in both physically separate - USB to SDIO/SD/MMC adapters and embedded on some motherboards. - - The VUB300 chip supports SD and MMC memory cards in addition - to single and multifunction SDIO cards. - - Some SDIO cards will need a firmware file to be loaded and - sent to VUB300 chip in order to achieve better data throughput. - Download these "Offload Pseudocode" from Elan Digital Systems' - web-site http://www.elandigitalsystems.com/support/downloads.php - and put them in /lib/firmware. Note that without these additional - firmware files the VUB300 chip will still function, but not at - the best obtainable data rate. - - To compile this mmc host controller driver as a module, - choose M here: the module will be called vub300. - - If you have a computer with an embedded VUB300 chip - or if you intend connecting a USB adapter based on a - VUB300 chip say Y or M here. - config MMC_USHC tristate "USB SD Host Controller (USHC) support" depends on USB diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile index ee412e6b84d6..fd043afab57e 100644 --- a/drivers/mmc/host/Makefile +++ b/drivers/mmc/host/Makefile @@ -60,7 +60,6 @@ obj-$(CONFIG_MMC_DW_ROCKCHIP) += dw_mmc-rockchip.o obj-$(CONFIG_MMC_DW_STARFIVE) += dw_mmc-starfive.o obj-$(CONFIG_MMC_SH_MMCIF) += sh_mmcif.o 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 @@ -80,6 +79,7 @@ obj-$(CONFIG_MMC_REALTEK_USB) += rtsx_usb_sdmmc.o obj-$(CONFIG_MMC_SDHCI_PLTFM) += sdhci-pltfm.o obj-$(CONFIG_MMC_SDHCI_CADENCE) += sdhci-cadence.o +sdhci-cadence-y += sdhci-cadence-core.o sdhci-cadence-phy-v6.o obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF) += sdhci-esdhc-mcf.o obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX) += sdhci-esdhc-imx.o obj-$(CONFIG_MMC_SDHCI_DOVE) += sdhci-dove.o diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c index 8f4df250a77a..98933be5f174 100644 --- a/drivers/mmc/host/atmel-mci.c +++ b/drivers/mmc/host/atmel-mci.c @@ -1677,10 +1677,7 @@ static void atmci_detect_change(struct timer_list *t) spin_lock(&host->lock); - if (!present) - clear_bit(ATMCI_CARD_PRESENT, &slot->flags); - else - set_bit(ATMCI_CARD_PRESENT, &slot->flags); + assign_bit(ATMCI_CARD_PRESENT, &slot->flags, present); /* Clean up queue if present */ mrq = slot->mrq; diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index c949a3eaae79..efd1903c3ccd 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1095,7 +1095,7 @@ static void bcm2835_set_clock(struct bcm2835_host *host, unsigned int clock) int div; /* The SDCDIV register has 11 bits, and holds (div - 2). But - * in data mode the max is 50MHz wihout a minimum, and only + * in data mode the max is 50MHz without a minimum, and only * the bottom 3 bits are used. Since the switch over is * automatic (unless we have marked the card as slow...), * chosen values have to make sense in both modes. Ident mode diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c index 2635ac94ec5d..ee19ea52b27d 100644 --- a/drivers/mmc/host/cavium-thunderx.c +++ b/drivers/mmc/host/cavium-thunderx.c @@ -157,11 +157,8 @@ error: for (i = 0; i < CAVIUM_MAX_MMC; i++) { if (host->slot[i]) cvm_mmc_of_slot_remove(host->slot[i]); - if (host->slot_pdev[i]) { - get_device(&host->slot_pdev[i]->dev); + if (host->slot_pdev[i]) of_platform_device_destroy(&host->slot_pdev[i]->dev, NULL); - put_device(&host->slot_pdev[i]->dev); - } } clk_disable_unprepare(host->clk); return ret; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index cdb9fa94b56d..c503e5f5c886 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -1249,6 +1249,10 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) host->use_dma = use_dma; host->mmc_irq = irq; host->sdio_irq = platform_get_irq_optional(pdev, 1); + if (host->sdio_irq < 0 && host->sdio_irq != -ENXIO) { + ret = host->sdio_irq; + goto parse_fail; + } if (host->use_dma) { ret = davinci_acquire_dma_channels(host); @@ -1299,7 +1303,7 @@ static int davinci_mmcsd_probe(struct platform_device *pdev) if (ret) goto mmc_add_host_fail; - if (host->sdio_irq >= 0) { + if (host->sdio_irq > 0) { ret = devm_request_irq(&pdev->dev, host->sdio_irq, mmc_davinci_sdio_irq, 0, mmc_hostname(mmc), host); diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index 75c82ff20f17..a07707a99a78 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -327,7 +327,7 @@ static int dw_mci_rk3288_execute_tuning(struct dw_mci *host, u32 opcode) i, priv->num_phases)); - v = !mmc_send_tuning(mmc, opcode, NULL); + v = !mmc_send_tuning_timeout(mmc, opcode, NULL, 5); if (i == 0) first_v = v; diff --git a/drivers/mmc/host/dw_mmc.c b/drivers/mmc/host/dw_mmc.c index cf5cecc5491d..ff0b25d287a9 100644 --- a/drivers/mmc/host/dw_mmc.c +++ b/drivers/mmc/host/dw_mmc.c @@ -44,6 +44,137 @@ #define DW_MCI_FREQ_MAX 200000000 /* unit: HZ */ #define DW_MCI_FREQ_MIN 100000 /* unit: HZ */ +/* Event sets guarded by the central watchdog, see below. */ +#define DW_MCI_WD_CMD_EVENTS BIT(EVENT_CMD_COMPLETE) +#define DW_MCI_WD_DATA_EVENTS (BIT(EVENT_DATA_ERROR) | \ + BIT(EVENT_DATA_COMPLETE)) +/* Extra watch rounds granted while the completion IRQ is merely late. */ +#define DW_MCI_WD_INFLIGHT_GRACE_MS 10 + +/* + * Central watchdog + * ================ + * + * A single hrtimer guards the legs of a request (command, data, voltage + * switch) against the hardware going silent: whenever a completion event + * that was expected within a bounded time does not arrive in time, the + * callback synthesizes it so that the request state machine can error out. + * + * @wd_events holds the pending_events bits still being waited for and + * @wd_states the host->state values for which the watch is valid; both + * are protected by irq_lock. Every producer of the guarded events posts + * them under irq_lock as well, so a callback holding irq_lock can never + * miss an already-delivered event. A stale callback which raced past + * all checks therefore degrades to at most one redundant state-machine + * run instead of completing a foreign leg. Arming replaces whatever + * watch was outstanding before: command and data legs are strictly + * serial, so consecutive arms on the same path collapse onto the latest + * deadline. + * + * On expiry, before declaring a timeout, the callback double-checks that + * the completion interrupt is not already latched in hardware; if it is, + * only the interrupt latency is to blame and the watch is granted extra + * rounds instead of failing a transfer that is about to complete. + */ +static void dw_mci_wd_deliver(struct dw_mci *host, unsigned long events) +{ + host->wd_events &= ~events; + if (!host->wd_events) + hrtimer_try_to_cancel(&host->wd_timer); +} + +/* + * Arm a new watch for @events valid while host->state is one of @states, + * expiring after @ms milliseconds. @already lists bits whose delivery + * makes the watch pointless before it is even armed: arming is skipped + * when any of them has been posted concurrently. Must be called with + * irq_lock held. + */ +static void dw_mci_wd_arm(struct dw_mci *host, unsigned long ms, + unsigned long already, unsigned long events, + unsigned long states) +{ + lockdep_assert_held(&host->irq_lock); + + if (host->pending_events & already) { + /* + * The event was delivered concurrently; no need for a + * watch anymore. + */ + host->wd_events = 0; + hrtimer_try_to_cancel(&host->wd_timer); + return; + } + + host->wd_events = events; + host->wd_states = states; + host->wd_deadline_ns = ktime_get_ns() + (u64)ms * NSEC_PER_MSEC; + hrtimer_start(&host->wd_timer, ms_to_ktime(ms), HRTIMER_MODE_REL); +} + +/* Called from hardirq context on watchdog expiry. */ +static enum hrtimer_restart dw_mci_watchdog_fn(struct hrtimer *t) +{ + struct dw_mci *host = container_of(t, struct dw_mci, wd_timer); + unsigned long irqflags; + u32 pending; + + spin_lock_irqsave(&host->irq_lock, irqflags); + + /* + * Last line of defense against a false timeout: when the completion + * interrupt is already latched in MINTSTS but its handler has not + * been scheduled yet, the leg is about to finish normally. Warn + * about the latency and keep watching instead of failing the + * transfer; delivery by the real interrupt will stop the watch. + * The retired per-timer callbacks went passive here and relied on + * the latched interrupt arriving; rearming bounds the damage if + * that assumption is ever broken. + */ + if (host->wd_events) { + pending = mci_readl(host, MINTSTS); /* read-only mask reg */ + if ((host->wd_events == DW_MCI_WD_CMD_EVENTS && + (pending & (DW_MCI_CMD_ERROR_FLAGS | + SDMMC_INT_CMD_DONE))) || + (host->wd_events == DW_MCI_WD_DATA_EVENTS && + (pending & (SDMMC_INT_DATA_OVER | + DW_MCI_DATA_ERROR_FLAGS)))) { + dev_warn(host->dev, + "Interrupt latency, state %d (watched %#lx)\n", + host->state, host->wd_events); + hrtimer_forward_now(&host->wd_timer, + ms_to_ktime(DW_MCI_WD_INFLIGHT_GRACE_MS)); + spin_unlock_irqrestore(&host->irq_lock, irqflags); + return HRTIMER_RESTART; + } + } + + if (host->wd_events == DW_MCI_WD_DATA_EVENTS) { + dev_warn(host->dev, + "Data timeout, state %d\n", host->state); + host->data_status = SDMMC_INT_DRTO; + set_bit(EVENT_DATA_ERROR, &host->pending_events); + set_bit(EVENT_DATA_COMPLETE, &host->pending_events); + } else if (host->wd_events) { + dev_warn(host->dev, + "Command timeout, state %d (watched %#lx)\n", + host->state, host->wd_events); + host->cmd_status = SDMMC_INT_RTO; + set_bit(EVENT_CMD_COMPLETE, &host->pending_events); + } + + if (host->wd_events) + queue_work(system_bh_wq, &host->bh_work); + + host->wd_events = 0; + host->wd_states = 0; + host->wd_deadline_ns = 0; + + spin_unlock_irqrestore(&host->irq_lock, irqflags); + + return HRTIMER_NORESTART; +} + #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 | \ @@ -171,6 +302,10 @@ static void dw_mci_init_debugfs(struct dw_mci *host) &host->pending_events); debugfs_create_xul("completed_events", 0400, root, &host->completed_events); + debugfs_create_x64("wd_deadline_ns", 0400, root, + &host->wd_deadline_ns); + debugfs_create_xul("wd_events", 0400, root, &host->wd_events); + debugfs_create_xul("wd_states", 0400, root, &host->wd_states); #ifdef CONFIG_FAULT_INJECTION fault_create_debugfs_attr("fail_data_crc", root, &host->fail_data_crc); #endif @@ -366,18 +501,17 @@ static inline void dw_mci_set_cto(struct dw_mci *host) * extra careful about synchronization here. Specifically in hardware a * command timeout is _at most_ 5.1 ms, so that means we expect an * interrupt (either command done or timeout) to come rather quickly - * after the mci_writel. ...but just in case we have a long interrupt - * latency let's add a bit of paranoia. + * after the mci_writel. * - * In general we'll assume that at least an interrupt will be asserted - * in hardware by the time the cto_timer runs. ...and if it hasn't - * been asserted in hardware by that time then we'll assume it'll never - * come. + * dw_mci_wd_arm() skips arming when the event was already delivered, + * and the watchdog callback rechecks under the same lock before + * declaring a timeout, so both orderings of the race are covered. */ spin_lock_irqsave(&host->irq_lock, irqflags); - if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) - mod_timer(&host->cto_timer, - jiffies + msecs_to_jiffies(cto_ms) + 1); + dw_mci_wd_arm(host, cto_ms, DW_MCI_WD_CMD_EVENTS, + DW_MCI_WD_CMD_EVENTS, + BIT(STATE_SENDING_CMD) | BIT(STATE_SENDING_STOP) | + BIT(STATE_SENDING_CMD11)); spin_unlock_irqrestore(&host->irq_lock, irqflags); } @@ -1265,17 +1399,16 @@ static void dw_mci_start_request(struct dw_mci *host, struct mmc_command *cmd) /* * Databook says to fail after 2ms w/ no response, but evidence * shows that sometimes the cmd11 interrupt takes over 130ms. - * We'll set to 500ms, plus an extra jiffy just in case jiffies - * is just about to roll over. + * We'll set to 500ms. * * We do this whole thing under spinlock and only if the * command hasn't already completed (indicating the irq * already ran so we don't want the timeout). */ spin_lock_irqsave(&host->irq_lock, irqflags); - if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) - mod_timer(&host->cmd11_timer, - jiffies + msecs_to_jiffies(500) + 1); + dw_mci_wd_arm(host, 500, DW_MCI_WD_CMD_EVENTS, + DW_MCI_WD_CMD_EVENTS, + BIT(STATE_SENDING_CMD11)); spin_unlock_irqrestore(&host->irq_lock, irqflags); } @@ -1870,9 +2003,9 @@ static void dw_mci_set_drto(struct dw_mci *host) drto_ms += 10; spin_lock_irqsave(&host->irq_lock, irqflags); - if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) - mod_timer(&host->dto_timer, - jiffies + msecs_to_jiffies(drto_ms)); + dw_mci_wd_arm(host, drto_ms, BIT(EVENT_DATA_COMPLETE), + DW_MCI_WD_DATA_EVENTS, + BIT(STATE_SENDING_DATA) | BIT(STATE_DATA_BUSY)); spin_unlock_irqrestore(&host->irq_lock, irqflags); } @@ -1881,14 +2014,6 @@ static bool dw_mci_clear_pending_cmd_complete(struct dw_mci *host) if (!test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) return false; - /* - * Really be certain that the timer has stopped. This is a bit of - * paranoia and could only really happen if we had really bad - * interrupt latency and the interrupt routine and timeout were - * running concurrently so that the timer_delete() in the interrupt - * handler couldn't run. - */ - WARN_ON(timer_delete_sync(&host->cto_timer)); clear_bit(EVENT_CMD_COMPLETE, &host->pending_events); return true; @@ -1899,8 +2024,6 @@ static bool dw_mci_clear_pending_data_complete(struct dw_mci *host) if (!test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) return false; - /* Extra paranoia just like dw_mci_clear_pending_cmd_complete() */ - WARN_ON(timer_delete_sync(&host->dto_timer)); clear_bit(EVENT_DATA_COMPLETE, &host->pending_events); return true; @@ -2633,7 +2756,7 @@ done: static void dw_mci_cmd_interrupt(struct dw_mci *host, u32 status) { - timer_delete(&host->cto_timer); + dw_mci_wd_deliver(host, BIT(EVENT_CMD_COMPLETE)); if (!host->cmd_status) host->cmd_status = status; @@ -2666,21 +2789,15 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) mci_writel(host, RINTSTS, SDMMC_INT_VOLT_SWITCH); pending &= ~SDMMC_INT_VOLT_SWITCH; - /* - * Hold the lock; we know cmd11_timer can't be kicked - * off after the lock is released, so safe to delete. - */ spin_lock(&host->irq_lock); dw_mci_cmd_interrupt(host, pending); spin_unlock(&host->irq_lock); - - timer_delete(&host->cmd11_timer); } if (pending & DW_MCI_CMD_ERROR_FLAGS) { spin_lock(&host->irq_lock); - timer_delete(&host->cto_timer); + dw_mci_wd_deliver(host, BIT(EVENT_CMD_COMPLETE)); mci_writel(host, RINTSTS, DW_MCI_CMD_ERROR_FLAGS); host->cmd_status = pending; smp_wmb(); /* drain writebuffer */ @@ -2693,7 +2810,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) spin_lock(&host->irq_lock); if (host->quirks & DW_MMC_QUIRK_EXTENDED_TMOUT) - timer_delete(&host->dto_timer); + dw_mci_wd_deliver(host, DW_MCI_WD_DATA_EVENTS); /* if there is an error report DATA_ERROR */ mci_writel(host, RINTSTS, DW_MCI_DATA_ERROR_FLAGS); @@ -2714,7 +2831,7 @@ static irqreturn_t dw_mci_interrupt(int irq, void *dev_id) if (pending & SDMMC_INT_DATA_OVER) { spin_lock(&host->irq_lock); - timer_delete(&host->dto_timer); + dw_mci_wd_deliver(host, DW_MCI_WD_DATA_EVENTS); mci_writel(host, RINTSTS, SDMMC_INT_DATA_OVER); if (!host->data_status) @@ -2994,126 +3111,6 @@ no_dma: host->use_dma = TRANS_MODE_PIO; } -static void dw_mci_cmd11_timer(struct timer_list *t) -{ - struct dw_mci *host = timer_container_of(host, t, cmd11_timer); - - if (host->state != STATE_SENDING_CMD11) { - dev_warn(host->dev, "Unexpected CMD11 timeout\n"); - return; - } - - host->cmd_status = SDMMC_INT_RTO; - set_bit(EVENT_CMD_COMPLETE, &host->pending_events); - queue_work(system_bh_wq, &host->bh_work); -} - -static void dw_mci_cto_timer(struct timer_list *t) -{ - struct dw_mci *host = timer_container_of(host, t, cto_timer); - unsigned long irqflags; - u32 pending; - - spin_lock_irqsave(&host->irq_lock, irqflags); - - /* - * If somehow we have very bad interrupt latency it's remotely possible - * that the timer could fire while the interrupt is still pending or - * while the interrupt is midway through running. Let's be paranoid - * and detect those two cases. Note that this is paranoia is somewhat - * justified because in this function we don't actually cancel the - * pending command in the controller--we just assume it will never come. - */ - pending = mci_readl(host, MINTSTS); /* read-only mask reg */ - if (pending & (DW_MCI_CMD_ERROR_FLAGS | SDMMC_INT_CMD_DONE)) { - /* The interrupt should fire; no need to act but we can warn */ - dev_warn(host->dev, "Unexpected interrupt latency\n"); - goto exit; - } - if (test_bit(EVENT_CMD_COMPLETE, &host->pending_events)) { - /* Presumably interrupt handler couldn't delete the timer */ - dev_warn(host->dev, "CTO timeout when already completed\n"); - goto exit; - } - - /* - * Continued paranoia to make sure we're in the state we expect. - * This paranoia isn't really justified but it seems good to be safe. - */ - switch (host->state) { - case STATE_SENDING_CMD11: - case STATE_SENDING_CMD: - case STATE_SENDING_STOP: - /* - * If CMD_DONE interrupt does NOT come in sending command - * state, we should notify the driver to terminate current - * transfer and report a command timeout to the core. - */ - host->cmd_status = SDMMC_INT_RTO; - set_bit(EVENT_CMD_COMPLETE, &host->pending_events); - queue_work(system_bh_wq, &host->bh_work); - break; - default: - dev_warn(host->dev, "Unexpected command timeout, state %d\n", - host->state); - break; - } - -exit: - spin_unlock_irqrestore(&host->irq_lock, irqflags); -} - -static void dw_mci_dto_timer(struct timer_list *t) -{ - struct dw_mci *host = timer_container_of(host, t, dto_timer); - unsigned long irqflags; - u32 pending; - - spin_lock_irqsave(&host->irq_lock, irqflags); - - /* - * The DTO timer is much longer than the CTO timer, so it's even less - * likely that we'll these cases, but it pays to be paranoid. - */ - pending = mci_readl(host, MINTSTS); /* read-only mask reg */ - if (pending & SDMMC_INT_DATA_OVER) { - /* The interrupt should fire; no need to act but we can warn */ - dev_warn(host->dev, "Unexpected data interrupt latency\n"); - goto exit; - } - if (test_bit(EVENT_DATA_COMPLETE, &host->pending_events)) { - /* Presumably interrupt handler couldn't delete the timer */ - dev_warn(host->dev, "DTO timeout when already completed\n"); - goto exit; - } - - /* - * Continued paranoia to make sure we're in the state we expect. - * This paranoia isn't really justified but it seems good to be safe. - */ - switch (host->state) { - case STATE_SENDING_DATA: - case STATE_DATA_BUSY: - /* - * If DTO interrupt does NOT come in sending data state, - * we should notify the driver to terminate current transfer - * and report a data timeout to the core. - */ - host->data_status = SDMMC_INT_DRTO; - set_bit(EVENT_DATA_ERROR, &host->pending_events); - set_bit(EVENT_DATA_COMPLETE, &host->pending_events); - queue_work(system_bh_wq, &host->bh_work); - break; - default: - dev_warn(host->dev, "Unexpected data timeout, state %d\n", - host->state); - break; - } - -exit: - spin_unlock_irqrestore(&host->irq_lock, irqflags); -} - static int dw_mci_parse_dt(struct dw_mci *host) { struct device *dev = host->dev; @@ -3261,9 +3258,8 @@ int dw_mci_probe(struct dw_mci *host) } } - timer_setup(&host->cmd11_timer, dw_mci_cmd11_timer, 0); - timer_setup(&host->cto_timer, dw_mci_cto_timer, 0); - timer_setup(&host->dto_timer, dw_mci_dto_timer, 0); + hrtimer_setup(&host->wd_timer, dw_mci_watchdog_fn, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); spin_lock_init(&host->lock); spin_lock_init(&host->irq_lock); diff --git a/drivers/mmc/host/dw_mmc.h b/drivers/mmc/host/dw_mmc.h index 38610c89d54a..5c2a8d0e9fb4 100644 --- a/drivers/mmc/host/dw_mmc.h +++ b/drivers/mmc/host/dw_mmc.h @@ -69,7 +69,7 @@ struct dw_mci_dma_slave { * @cmd: The command currently being sent to the card, or NULL. * @data: The data currently being transferred, or NULL if no data * transfer is in progress. - * @stop_abort: The command currently prepared for stoping transfer. + * @stop_abort: The command currently prepared for stopping transfer. * @prev_blksz: The former transfer blksz record. * @timing: Record of current ios timing. * @use_dma: Which DMA channel is in use for the current transfer, zero @@ -122,9 +122,11 @@ struct dw_mci_dma_slave { * @irq_flags: The flags to be passed to request_irq. * @irq: The irq value to be passed to request_irq. * @sdio_irq: SDIO interrupt bit in interrupt registers. - * @cmd11_timer: Timer for SD3.0 voltage switch over scheme. - * @cto_timer: Timer for broken command transfer over scheme. - * @dto_timer: Timer for broken data transfer over scheme. + * @wd_timer: Central watchdog guarding request legs against hardware going + * silent; fires when an expected completion event fails to arrive in + * time so that the state machine can synthesize an error. + * @wd_events: pending_events bits still awaited by the armed watch. + * @wd_states: host->state values for which the armed watch is valid. * @mmc: The mmc_host representing this dw_mci. * @flags: Random state bits associated with the host. * @ctype: Card type for this host. @@ -214,7 +216,6 @@ struct dw_mci { void *priv; struct clk *biu_clk; struct clk *ciu_clk; - struct dw_mci_slot *slot; /* FIFO push and pull */ int fifo_depth; @@ -235,9 +236,10 @@ struct dw_mci { int sdio_irq; - struct timer_list cmd11_timer; - struct timer_list cto_timer; - struct timer_list dto_timer; + struct hrtimer wd_timer; + unsigned long wd_events; + unsigned long wd_states; + u64 wd_deadline_ns; #ifdef CONFIG_FAULT_INJECTION struct fault_attr fail_data_crc; diff --git a/drivers/mmc/host/meson-gx-mmc.c b/drivers/mmc/host/meson-gx-mmc.c index 694bb443d5f3..7ef16d7c03cb 100644 --- a/drivers/mmc/host/meson-gx-mmc.c +++ b/drivers/mmc/host/meson-gx-mmc.c @@ -139,6 +139,7 @@ struct meson_mmc_data { unsigned int always_on; unsigned int adjust; unsigned int irq_sdio_sleep; + bool has_pipeline_clk; }; struct sd_emmc_desc { @@ -418,7 +419,7 @@ static int meson_mmc_clk_set(struct meson_host *host, unsigned long rate, */ static int meson_mmc_clk_init(struct meson_host *host) { - struct clk_init_data init; + struct clk_init_data init = {}; struct clk_mux *mux; struct clk_divider *div; char clk_name[32]; @@ -1141,7 +1142,8 @@ static int meson_mmc_probe(struct platform_device *pdev) struct meson_host *host; struct mmc_host *mmc; struct clk *core_clk; - int cd_irq, ret; + int cd_irq = -ENXIO; + int ret; mmc = devm_mmc_alloc_host(&pdev->dev, sizeof(struct meson_host)); if (!mmc) @@ -1185,7 +1187,11 @@ static int meson_mmc_probe(struct platform_device *pdev) if (host->irq < 0) return host->irq; - cd_irq = platform_get_irq_optional(pdev, 1); + ret = platform_get_irq_optional(pdev, 1); + if (ret < 0 && ret != -ENXIO) + return ret; + if (ret > 0) + cd_irq = ret; mmc_gpio_set_cd_irq(mmc, cd_irq); host->pinctrl = devm_pinctrl_get(&pdev->dev); @@ -1204,6 +1210,15 @@ static int meson_mmc_probe(struct platform_device *pdev) if (IS_ERR(core_clk)) return PTR_ERR(core_clk); + if (host->data->has_pipeline_clk) { + struct clk *pipe_clk; + + pipe_clk = devm_clk_get_enabled(&pdev->dev, "pipeline"); + if (IS_ERR(pipe_clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(pipe_clk), + "missing pipeline clock\n"); + } + ret = meson_mmc_clk_init(host); if (ret) return ret; @@ -1322,12 +1337,22 @@ static const struct meson_mmc_data meson_axg_data = { .irq_sdio_sleep = CLK_V3_IRQ_SDIO_SLEEP, }; +static const struct meson_mmc_data meson_t7_data = { + .tx_delay_mask = CLK_V3_TX_DELAY_MASK, + .rx_delay_mask = CLK_V3_RX_DELAY_MASK, + .always_on = CLK_V3_ALWAYS_ON, + .adjust = SD_EMMC_V3_ADJUST, + .irq_sdio_sleep = CLK_V3_IRQ_SDIO_SLEEP, + .has_pipeline_clk = true, +}; + static const struct of_device_id meson_mmc_of_match[] = { { .compatible = "amlogic,meson-gx-mmc", .data = &meson_gx_data }, { .compatible = "amlogic,meson-gxbb-mmc", .data = &meson_gx_data }, { .compatible = "amlogic,meson-gxl-mmc", .data = &meson_gx_data }, { .compatible = "amlogic,meson-gxm-mmc", .data = &meson_gx_data }, { .compatible = "amlogic,meson-axg-mmc", .data = &meson_axg_data }, + { .compatible = "amlogic,t7-mmc", .data = &meson_t7_data }, {} }; MODULE_DEVICE_TABLE(of, meson_mmc_of_match); diff --git a/drivers/mmc/host/mvsdio.c b/drivers/mmc/host/mvsdio.c index cf705b265e21..7d86426c6001 100644 --- a/drivers/mmc/host/mvsdio.c +++ b/drivers/mmc/host/mvsdio.c @@ -717,7 +717,7 @@ static int mvsd_probe(struct platform_device *pdev) /* * Some non-DT platforms do not pass a clock, and the clock * frequency is passed through platform_data. On DT platforms, - * a clock must always be passed, even if there is no gatable + * a clock must always be passed, even if there is no gateable * clock associated to the SDIO interface (it can simply be a * fixed rate clock). */ diff --git a/drivers/mmc/host/rtsx_usb_sdmmc.c b/drivers/mmc/host/rtsx_usb_sdmmc.c index bf37fab233cf..d9b33aa79165 100644 --- a/drivers/mmc/host/rtsx_usb_sdmmc.c +++ b/drivers/mmc/host/rtsx_usb_sdmmc.c @@ -980,6 +980,11 @@ static int sd_power_on(struct rtsx_usb_sdmmc *host) } dev_dbg(sdmmc_dev(host), "%s\n", __func__); rtsx_usb_init_cmd(ucr); + /* Start SD init at 3.3V, like the old rts5139 driver. */ + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, SD_PAD_CTL, + SD_IO_USING_1V8, SD_IO_USING_3V3); + rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, LDO_POWER_CFG, + TUNE_SD18_MASK, TUNE_SD18_3V3); rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SELECT, 0x07, SD_MOD_SEL); rtsx_usb_add_cmd(ucr, WRITE_REG_CMD, CARD_SHARE_MODE, CARD_SHARE_MASK, CARD_SHARE_SD); diff --git a/drivers/mmc/host/sdhci-cadence.c b/drivers/mmc/host/sdhci-cadence-core.c index 435603c8c00b..745455743b01 100644 --- a/drivers/mmc/host/sdhci-cadence.c +++ b/drivers/mmc/host/sdhci-cadence-core.c @@ -2,10 +2,12 @@ /* * Copyright (C) 2016 Socionext Inc. * Author: Masahiro Yamada <yamada.masahiro@socionext.com> + * Copyright (C) 2026 Altera Corporation */ #include <linux/bitfield.h> #include <linux/bits.h> +#include <linux/dma-mapping.h> #include <linux/iopoll.h> #include <linux/module.h> #include <linux/mmc/host.h> @@ -14,10 +16,10 @@ #include <linux/platform_device.h> #include <linux/reset.h> -#include "sdhci-pltfm.h" +#include "sdhci-cadence.h" /* HRS - Host Register Set (specific to Cadence) */ -#define SDHCI_CDNS_HRS04 0x10 /* PHY access port */ +/* HRS04 (PHY access) bitfields (SD4HC) */ #define SDHCI_CDNS_HRS04_ACK BIT(26) #define SDHCI_CDNS_HRS04_RD BIT(25) #define SDHCI_CDNS_HRS04_WR BIT(24) @@ -71,30 +73,17 @@ #define SDHCI_CDNS_PHY_DLY_HSMMC 0x0c #define SDHCI_CDNS_PHY_DLY_STROBE 0x0d -/* - * The tuned val register is 6 bit-wide, but not the whole of the range is - * available. The range 0-42 seems to be available (then 43 wraps around to 0) - * but I am not quite sure if it is official. Use only 0 to 39 for safety. - */ -#define SDHCI_CDNS_MAX_TUNING_LOOP 40 - -struct sdhci_cdns_phy_param { +struct sdhci_cdns4_phy_param { u8 addr; u8 data; }; -struct sdhci_cdns_priv { - void __iomem *hrs_addr; - void __iomem *ctl_addr; /* write control */ - spinlock_t wrlock; /* write lock */ - bool enhanced_strobe; - void (*priv_writel)(struct sdhci_cdns_priv *priv, u32 val, void __iomem *reg); - struct reset_control *rst_hw; +struct sdhci_cdns4_phy { unsigned int nr_phy_params; - struct sdhci_cdns_phy_param phy_params[]; + struct sdhci_cdns4_phy_param phy_params[]; }; -struct sdhci_cdns_phy_cfg { +struct sdhci_cdns4_phy_cfg { const char *property; u8 addr; }; @@ -102,9 +91,10 @@ struct sdhci_cdns_phy_cfg { struct sdhci_cdns_drv_data { int (*init)(struct platform_device *pdev); const struct sdhci_pltfm_data pltfm_data; + u64 dma_mask; }; -static const struct sdhci_cdns_phy_cfg sdhci_cdns_phy_cfgs[] = { +static const struct sdhci_cdns4_phy_cfg sdhci_cdns4_phy_cfgs[] = { { "cdns,phy-input-delay-sd-highspeed", SDHCI_CDNS_PHY_DLY_SD_HS, }, { "cdns,phy-input-delay-legacy", SDHCI_CDNS_PHY_DLY_SD_DEFAULT, }, { "cdns,phy-input-delay-sd-uhs-sdr12", SDHCI_CDNS_PHY_DLY_UHS_SDR12, }, @@ -124,8 +114,7 @@ static inline void cdns_writel(struct sdhci_cdns_priv *priv, u32 val, writel(val, reg); } -static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, - u8 addr, u8 data) +static int sdhci_cdns4_write_phy_reg(struct sdhci_cdns_priv *priv, u8 addr, u8 data) { void __iomem *reg = priv->hrs_addr + SDHCI_CDNS_HRS04; u32 tmp; @@ -156,44 +145,43 @@ static int sdhci_cdns_write_phy_reg(struct sdhci_cdns_priv *priv, return ret; } -static unsigned int sdhci_cdns_phy_param_count(struct device_node *np) +static unsigned int sdhci_cdns4_phy_param_count(struct device_node *np) { unsigned int count = 0; int i; - for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) - if (of_property_present(np, sdhci_cdns_phy_cfgs[i].property)) + for (i = 0; i < ARRAY_SIZE(sdhci_cdns4_phy_cfgs); i++) + if (of_property_present(np, sdhci_cdns4_phy_cfgs[i].property)) count++; return count; } -static void sdhci_cdns_phy_param_parse(struct device_node *np, - struct sdhci_cdns_priv *priv) +static void sdhci_cdns4_phy_param_parse(struct device_node *np, struct sdhci_cdns4_phy *phy) { - struct sdhci_cdns_phy_param *p = priv->phy_params; + struct sdhci_cdns4_phy_param *p = phy->phy_params; u32 val; int ret, i; - for (i = 0; i < ARRAY_SIZE(sdhci_cdns_phy_cfgs); i++) { - ret = of_property_read_u32(np, sdhci_cdns_phy_cfgs[i].property, - &val); + for (i = 0; i < ARRAY_SIZE(sdhci_cdns4_phy_cfgs); i++) { + ret = of_property_read_u32(np, sdhci_cdns4_phy_cfgs[i].property, &val); if (ret) continue; - p->addr = sdhci_cdns_phy_cfgs[i].addr; + p->addr = sdhci_cdns4_phy_cfgs[i].addr; p->data = val; p++; } } -static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv) +static int sdhci_cdns4_phy_init(struct sdhci_cdns_priv *priv) { + struct sdhci_cdns4_phy *phy = priv->phy; int ret, i; - for (i = 0; i < priv->nr_phy_params; i++) { - ret = sdhci_cdns_write_phy_reg(priv, priv->phy_params[i].addr, - priv->phy_params[i].data); + for (i = 0; i < phy->nr_phy_params; i++) { + ret = sdhci_cdns4_write_phy_reg(priv, phy->phy_params[i].addr, + phy->phy_params[i].data); if (ret) return ret; } @@ -201,13 +189,6 @@ static int sdhci_cdns_phy_init(struct sdhci_cdns_priv *priv) return 0; } -static void *sdhci_cdns_priv(struct sdhci_host *host) -{ - struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); - - return sdhci_pltfm_priv(pltfm_host); -} - static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host) { /* @@ -217,6 +198,23 @@ static unsigned int sdhci_cdns_get_timeout_clock(struct sdhci_host *host) return host->max_clk; } +static int sdhci_cdns_set_dma_mask(struct sdhci_host *host) +{ + const struct sdhci_cdns_drv_data *data; + struct device *dev = mmc_dev(host->mmc); + int ret; + + data = of_device_get_match_data(dev); + if (!data || !data->dma_mask) + return 0; + + ret = dma_set_mask_and_coherent(dev, data->dma_mask); + if (ret) + return dev_err_probe(dev, ret, "failed to set DMA mask\n"); + + return 0; +} + static void sdhci_cdns_set_emmc_mode(struct sdhci_cdns_priv *priv, u32 mode) { u32 tmp; @@ -243,6 +241,9 @@ static int sdhci_cdns_set_tune_val(struct sdhci_host *host, unsigned int val) u32 tmp; int i, ret; + if (host->version >= SDHCI_SPEC_420) + return sdhci_cdns6_set_tune_val(host, val); + if (WARN_ON(!FIELD_FIT(SDHCI_CDNS_HRS06_TUNE, val))) return -EINVAL; @@ -323,8 +324,10 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode) * The delay is set by probe, based on the DT properties. */ if (host->timing != MMC_TIMING_MMC_HS200 && - host->timing != MMC_TIMING_UHS_SDR104) + host->timing != MMC_TIMING_UHS_SDR104) { + dev_dbg(mmc_dev(host->mmc), "Tuning skipped (timing: %d)\n", host->timing); return 0; + } for (i = 0; i < SDHCI_CDNS_MAX_TUNING_LOOP; i++) { if (sdhci_cdns_set_tune_val(host, i) || @@ -348,6 +351,10 @@ static int sdhci_cdns_execute_tuning(struct sdhci_host *host, u32 opcode) if (ret) return ret; + /* Block gap tuning is only required for SD4HC, not for SD6HC */ + if (host->version >= SDHCI_SPEC_420) + return 0; + return sdhci_cdns_tune_blkgap(host->mmc); } @@ -383,6 +390,10 @@ static void sdhci_cdns_set_uhs_signaling(struct sdhci_host *host, /* For SD, fall back to the default handler */ if (mode == SDHCI_CDNS_HRS06_MODE_SD) sdhci_set_uhs_signaling(host, timing); + + /* For host controller V6, set SDHCI and PHY registers for UHS signaling */ + if (host->version >= SDHCI_SPEC_420) + sdhci_cdns6_set_uhs_signaling(host, timing); } /* Elba control register bits [6:3] are byte-lane enables */ @@ -470,7 +481,49 @@ static int elba_drv_init(struct platform_device *pdev) return 0; } -static const struct sdhci_ops sdhci_cdns_ops = { +static const struct reset_control_bulk_data sdhci_cdns6_agilex5_reset_ids[] = { + { .id = "sdhc-reset" }, + { .id = "combophy" }, + { .id = "sdmmc-ocp" }, +}; + +static int sdhci_cdns6_agilex5_init(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct reset_control_bulk_data *resets; + int ret; + + resets = devm_kmemdup(dev, sdhci_cdns6_agilex5_reset_ids, + sizeof(sdhci_cdns6_agilex5_reset_ids), GFP_KERNEL); + if (!resets) + return -ENOMEM; + + /* + * Assert SDHCI, SoftPHY (combophy), and SDMMC OCP/AXI resets together + * so their active periods overlap before all domains are released. + * SoftPHY is shared with NAND, but only one of SDMMC + * or NAND is enabled on a given board. + */ + ret = devm_reset_control_bulk_get_exclusive(dev, ARRAY_SIZE(sdhci_cdns6_agilex5_reset_ids), + resets); + if (ret) + return dev_err_probe(dev, ret, "failed to get resets\n"); + + ret = reset_control_bulk_assert(ARRAY_SIZE(sdhci_cdns6_agilex5_reset_ids), resets); + if (ret) + return dev_err_probe(dev, ret, "failed to assert resets\n"); + + /* Hold resets asserted long enough for all clock domains to capture. */ + usleep_range(10, 20); + + ret = reset_control_bulk_deassert(ARRAY_SIZE(sdhci_cdns6_agilex5_reset_ids), resets); + if (ret) + return dev_err_probe(dev, ret, "failed to deassert resets\n"); + + return 0; +} + +static const struct sdhci_ops sdhci_cdns4_ops = { .set_clock = sdhci_set_clock, .get_timeout_clock = sdhci_cdns_get_timeout_clock, .set_bus_width = sdhci_set_bus_width, @@ -479,9 +532,31 @@ static const struct sdhci_ops sdhci_cdns_ops = { .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, }; +static const struct sdhci_ops sdhci_cdns6_ops = { + .set_clock = sdhci_cdns6_set_clock, + .get_timeout_clock = sdhci_cdns_get_timeout_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .platform_execute_tuning = sdhci_cdns_execute_tuning, + .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, + .hw_reset = sdhci_cdns6_hw_reset, +}; + +static const struct sdhci_ops sdhci_cdns6_agilex5_ops = { + .set_clock = sdhci_cdns6_set_clock, + .get_max_clock = sdhci_pltfm_clk_get_max_clock, + .get_timeout_clock = sdhci_cdns_get_timeout_clock, + .set_bus_width = sdhci_set_bus_width, + .reset = sdhci_reset, + .platform_execute_tuning = sdhci_cdns_execute_tuning, + .set_uhs_signaling = sdhci_cdns_set_uhs_signaling, + .hw_reset = sdhci_cdns6_hw_reset, + .set_dma_mask = sdhci_cdns_set_dma_mask, +}; + static const struct sdhci_cdns_drv_data sdhci_cdns_uniphier_drv_data = { .pltfm_data = { - .ops = &sdhci_cdns_ops, + .ops = &sdhci_cdns4_ops, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }, }; @@ -495,14 +570,32 @@ static const struct sdhci_cdns_drv_data sdhci_elba_drv_data = { static const struct sdhci_cdns_drv_data sdhci_eyeq_drv_data = { .pltfm_data = { - .ops = &sdhci_cdns_ops, + .ops = &sdhci_cdns4_ops, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }, }; -static const struct sdhci_cdns_drv_data sdhci_cdns_drv_data = { +static const struct sdhci_cdns_drv_data sdhci_cdns4_drv_data = { + .pltfm_data = { + .ops = &sdhci_cdns4_ops, + }, +}; + +static const struct sdhci_cdns_drv_data sdhci_cdns6_agilex5_drv_data = { + .init = sdhci_cdns6_agilex5_init, .pltfm_data = { - .ops = &sdhci_cdns_ops, + .ops = &sdhci_cdns6_agilex5_ops, + .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | + SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12, + .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | + SDHCI_QUIRK2_ACMD23_BROKEN, + }, + .dma_mask = DMA_BIT_MASK(40), +}; + +static const struct sdhci_cdns_drv_data sdhci_cdns6_drv_data = { + .pltfm_data = { + .ops = &sdhci_cdns6_ops, }, }; @@ -524,6 +617,14 @@ static void sdhci_cdns_hs400_enhanced_strobe(struct mmc_host *mmc, if (mode == SDHCI_CDNS_HRS06_MODE_MMC_HS400ES && !ios->enhanced_strobe) sdhci_cdns_set_emmc_mode(priv, SDHCI_CDNS_HRS06_MODE_MMC_HS400); + + /* + * Reprogram the SD6HC PHY for the current timing. Enhanced strobe + * affects PHY delays, and this callback may run with either HS400 + * (enabling/disabling ES) or Legacy (initial bus state). + */ + if (host->version >= SDHCI_SPEC_420) + sdhci_cdns6_set_uhs_signaling(host, ios->timing); } static void sdhci_cdns_mmc_hw_reset(struct mmc_host *mmc) @@ -542,6 +643,24 @@ static void sdhci_cdns_mmc_hw_reset(struct mmc_host *mmc) usleep_range(300, 1000); } +static int sdhci_cdns4_phy_probe(struct platform_device *pdev, struct sdhci_cdns_priv *priv) +{ + struct device *dev = &pdev->dev; + struct sdhci_cdns4_phy *phy; + unsigned int nr_phy_params; + + nr_phy_params = sdhci_cdns4_phy_param_count(dev->of_node); + phy = devm_kzalloc(dev, struct_size(phy, phy_params, nr_phy_params), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->nr_phy_params = nr_phy_params; + sdhci_cdns4_phy_param_parse(dev->of_node, phy); + priv->phy = phy; + + return sdhci_cdns4_phy_init(priv); +} + static int sdhci_cdns_probe(struct platform_device *pdev) { struct sdhci_host *host; @@ -549,22 +668,30 @@ static int sdhci_cdns_probe(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host; struct sdhci_cdns_priv *priv; struct clk *clk; - unsigned int nr_phy_params; + struct clk *biu_clk; int ret; struct device *dev = &pdev->dev; static const u16 version = SDHCI_SPEC_400 << SDHCI_SPEC_VER_SHIFT; - clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); + if (of_device_is_compatible(dev->of_node, "cdns,sd6hc")) { + clk = devm_clk_get_enabled(dev, "ciu"); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "failed to enable ciu clock\n"); + + biu_clk = devm_clk_get_enabled(dev, "biu"); + if (IS_ERR(biu_clk)) + return dev_err_probe(dev, PTR_ERR(biu_clk), "failed to enable biu clock\n"); + } else { + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + } data = of_device_get_match_data(dev); if (!data) - data = &sdhci_cdns_drv_data; + return dev_err_probe(dev, -EINVAL, "missing platform driver data\n"); - nr_phy_params = sdhci_cdns_phy_param_count(dev->of_node); - host = sdhci_pltfm_init(pdev, &data->pltfm_data, - struct_size(priv, phy_params, nr_phy_params)); + host = sdhci_pltfm_init(pdev, &data->pltfm_data, sizeof(*priv)); if (IS_ERR(host)) return PTR_ERR(host); @@ -572,7 +699,6 @@ static int sdhci_cdns_probe(struct platform_device *pdev) pltfm_host->clk = clk; priv = sdhci_pltfm_priv(pltfm_host); - priv->nr_phy_params = nr_phy_params; priv->hrs_addr = host->ioaddr; priv->enhanced_strobe = false; priv->priv_writel = cdns_writel; @@ -585,32 +711,46 @@ static int sdhci_cdns_probe(struct platform_device *pdev) return ret; } sdhci_enable_v4_mode(host); - __sdhci_read_caps(host, &version, NULL, NULL); - sdhci_get_of_property(pdev); ret = mmc_of_parse(host->mmc); if (ret) return ret; - sdhci_cdns_phy_param_parse(dev->of_node, priv); - - ret = sdhci_cdns_phy_init(priv); - if (ret) - return ret; + /* + * For SD4HC, read capabilities with fixed version override and set up + * the optional eMMC card RST_n reset control. + * For SD6HC, sdhci_add_host() will automatically read capabilities + * and version from the host controller registers. + */ + if (of_device_is_compatible(dev->of_node, "cdns,sd4hc")) { + __sdhci_read_caps(host, &version, NULL, NULL); + ret = sdhci_cdns4_phy_probe(pdev, priv); + if (ret) + return ret; - if (host->mmc->caps & MMC_CAP_HW_RESET) { - priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL); - if (IS_ERR(priv->rst_hw)) - return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw), - "reset controller error\n"); - if (priv->rst_hw) - host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset; + if (host->mmc->caps & MMC_CAP_HW_RESET) { + priv->rst_hw = devm_reset_control_get_optional_exclusive(dev, NULL); + if (IS_ERR(priv->rst_hw)) + return dev_err_probe(mmc_dev(host->mmc), PTR_ERR(priv->rst_hw), + "reset controller error\n"); + if (priv->rst_hw) + host->mmc_host_ops.card_hw_reset = sdhci_cdns_mmc_hw_reset; + } + } else { + ret = sdhci_cdns6_phy_probe(pdev, priv); + if (ret) + return ret; } return sdhci_add_host(host); } +/* + * Only the CIU clock is gated on suspend. The SD6HC "biu" clock is not + * toggled here as it may be a shared bus clock; a dedicated biu clock + * would need explicit PM gating added here. + */ static int sdhci_cdns_resume(struct device *dev) { struct sdhci_host *host = dev_get_drvdata(dev); @@ -622,7 +762,11 @@ static int sdhci_cdns_resume(struct device *dev) if (ret) return ret; - ret = sdhci_cdns_phy_init(priv); + if (host->version >= SDHCI_SPEC_420) + ret = sdhci_cdns6_phy_init(priv); + else + ret = sdhci_cdns4_phy_init(priv); + if (ret) goto disable_clk; @@ -653,7 +797,18 @@ static const struct of_device_id sdhci_cdns_match[] = { .compatible = "mobileye,eyeq-sd4hc", .data = &sdhci_eyeq_drv_data, }, - { .compatible = "cdns,sd4hc" }, + { + .compatible = "cdns,sd4hc", + .data = &sdhci_cdns4_drv_data, + }, + { + .compatible = "altr,agilex5-sd6hc", + .data = &sdhci_cdns6_agilex5_drv_data, + }, + { + .compatible = "cdns,sd6hc", + .data = &sdhci_cdns6_drv_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sdhci_cdns_match); diff --git a/drivers/mmc/host/sdhci-cadence-phy-v6.c b/drivers/mmc/host/sdhci-cadence-phy-v6.c new file mode 100644 index 000000000000..22d56bb46d75 --- /dev/null +++ b/drivers/mmc/host/sdhci-cadence-phy-v6.c @@ -0,0 +1,975 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PHY and host controller support for Cadence SD6HC SDHCI + * + * This file provides support for Cadence's sixth-generation SDHCI controller (SD6HC). + * Implements PHY initialization, DLL management, per-speed-mode timing calculations, + * and host controller register programming for the SD6HC integrated combo-PHY. + * + * Copyright (C) 2026 Altera Corporation + * Author: Tanmay Kathpalia <tanmay.kathpalia@altera.com> + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/iopoll.h> +#include <linux/of.h> + +#include "sdhci-cadence.h" + +/* IO Delay Information */ +#define SDHCI_CDNS_HRS07 0x1c +#define SDHCI_CDNS_HRS07_RW_COMPENSATE GENMASK(20, 16) +#define SDHCI_CDNS_HRS07_IDELAY_VAL GENMASK(4, 0) + +/* PHY Control and Status */ +#define SDHCI_CDNS_HRS09 0x24 +#define SDHCI_CDNS_HRS09_RDDATA_EN BIT(16) +#define SDHCI_CDNS_HRS09_RDCMD_EN BIT(15) +#define SDHCI_CDNS_HRS09_EXTENDED_WR_MODE BIT(3) +#define SDHCI_CDNS_HRS09_EXTENDED_RD_MODE BIT(2) +#define SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE BIT(1) +#define SDHCI_CDNS_HRS09_PHY_SW_RESET BIT(0) + +/* SDCLK start point adjustment */ +#define SDHCI_CDNS_HRS10 0x28 +#define SDHCI_CDNS_HRS10_HCSDCLKADJ GENMASK(19, 16) + +/* eMMC Control */ +#define SDHCI_CDNS_HRS11 0x2c +#define SDHCI_CDNS_HRS11_EMMC_RST BIT(0) /* eMMC reset */ + +/* CMD/DAT output delay */ +#define SDHCI_CDNS_HRS16 0x40 +#define SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY GENMASK(31, 28) +#define SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY GENMASK(27, 24) +#define SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY GENMASK(23, 20) +#define SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY GENMASK(19, 16) +#define SDHCI_CDNS_HRS16_WRDATA1_DLY GENMASK(15, 12) +#define SDHCI_CDNS_HRS16_WRDATA0_DLY GENMASK(11, 8) +#define SDHCI_CDNS_HRS16_WRCMD1_DLY GENMASK(7, 4) +#define SDHCI_CDNS_HRS16_WRCMD0_DLY GENMASK(3, 0) + +/* PHY Special Function Registers */ +/* DQ timing */ +#define SDHCI_CDNS6_PHY_DQ_TIMING_REG 0x2000 +#define SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON BIT(31) +#define SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_END GENMASK(29, 27) +#define SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_START GENMASK(26, 24) +#define SDHCI_CDNS6_PHY_DQ_TIMING_DATA_SELECT_OE_END GENMASK(2, 0) + +/* DQS timing */ +#define SDHCI_CDNS6_PHY_DQS_TIMING_REG 0x2004 +#define SDHCI_CDNS6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS BIT(22) +#define SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS BIT(21) +#define SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS BIT(20) +#define SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD BIT(19) + +/* Gate and loopback control */ +#define SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_REG 0x2008 +#define SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_SYNC_METHOD BIT(31) +#define SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_RD_DEL_SEL GENMASK(24, 19) +#define SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_UNDERRUN_SUPPRESS BIT(18) +#define SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_GATE_CFG_ALWAYS_ON BIT(6) + +/* Master DLL logic */ +#define SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_REG 0x200c +#define SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_BYPASS_MODE BIT(23) +#define SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_PHASE_DETECT_SEL GENMASK(22, 20) +#define SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_DLL_LOCK_NUM GENMASK(18, 16) +#define SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_DLL_START_POINT GENMASK(7, 0) + +/* Slave DLL logic */ +#define SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_REG 0x2010 +#define SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_CMD_DELAY GENMASK(31, 24) +#define SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_CLK_WRDQS_DELAY GENMASK(23, 16) +#define SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_CLK_WR_DELAY GENMASK(15, 8) +#define SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY GENMASK(7, 0) + +/* Global control settings */ +#define SDHCI_CDNS6_PHY_CTRL_REG 0x2080 +#define SDHCI_CDNS6_PHY_CTRL_PHONY_DQS_TIMING GENMASK(9, 4) + +/* Default PHY settings */ +#define SDHCI_CDNS6_PHY_DEFAULT_IOCELL_DELAY 2500 +#define SDHCI_CDNS6_PHY_DEFAULT_DELAY_ELEMENT 24 +#define SDHCI_CDNS6_PHY_DEFAULT_RD_DEL_SEL 52 +#define SDHCI_CDNS6_PHY_DEFAULT_DLL_START 4 +#define SDHCI_CDNS6_PHY_DEFAULT_PHASE_DETECT_SEL 2 +#define SDHCI_CDNS6_PHY_DEFAULT_DLL_LOCK_NUM 0 +#define SDHCI_CDNS6_PHY_DEFAULT_DATA_SELECT_OE_END 1 + +/* Scale tuning tap (0..39) to 8-bit PHY DLL delay field (0..255) */ +#define SDHCI_CDNS6_PHY_DLL_FIELD_SIZE 256 + +struct sdhci_cdns6_phy { + /* + * Mode-specific timing constraints (in picoseconds) + * These define valid output windows per SD/eMMC spec + */ + u32 t_cmd_output_min; + u32 t_cmd_output_max; + u32 t_dat_output_min; + u32 t_dat_output_max; + + /* + * PHY delay configuration (in picoseconds) + * Derived from clock period and board-level IO cell delays + */ + u32 phy_sdclk_delay; + u32 phy_cmd_o_delay; + u32 phy_dat_o_delay; + u32 iocell_input_delay; + u32 iocell_output_delay; + /* Configured delay element (ps); preserved across clock changes */ + u32 delay_element_org; + /* Active delay element (ps); doubled when one SDMCLK requires > 256 steps */ + u32 delay_element; + + /* PHY_DLL_SLAVE_CTRL register fields */ + u8 cp_read_dqs_cmd_delay; /* bits [31:24] */ + u8 cp_clk_wrdqs_delay; /* bits [23:16] */ + u8 cp_clk_wr_delay; /* bits [15:8] */ + u8 cp_read_dqs_delay; /* bits [7:0] */ + + /* PHY_DLL_MASTER_CTRL register fields */ + bool cp_dll_bypass_mode; /* bit [23] */ + + /* PHY_DQ_TIMING register fields */ + u8 cp_io_mask_end; /* bits [29:27] */ + u8 cp_io_mask_start; /* bits [26:24] */ + + /* PHY_DQS_TIMING register fields */ + bool cp_use_phony_dqs; /* bit [20] */ + bool cp_use_phony_dqs_cmd; /* bit [19] */ + + /* HRS07 register - IO delay Information */ + u8 sdhc_rw_compensate; /* bits [20:16] */ + u8 sdhc_idelay_val; /* bits [4:0] */ + + /* HRS09 register - PHY control and Status */ + bool sdhc_extended_wr_mode; /* bit [3] */ + bool sdhc_extended_rd_mode; /* bit [2] */ + + /* HRS10 register - SDCLK start point adjustment */ + u8 sdhc_hcsdclkadj; /* bits [19:16] */ + + /* HRS16 register fields - CMD/DAT output delay control */ + u8 sdhc_wrdata1_sdclk_dly; /* bits [31:28] */ + u8 sdhc_wrdata0_sdclk_dly; /* bits [27:24] */ + u8 sdhc_wrcmd1_sdclk_dly; /* bits [23:20] */ + u8 sdhc_wrcmd0_sdclk_dly; /* bits [19:16] */ + u8 sdhc_wrdata1_dly; /* bits [15:12] */ + u8 sdhc_wrdata0_dly; /* bits [11:8] */ + u8 sdhc_wrcmd1_dly; /* bits [7:4] */ + u8 sdhc_wrcmd0_dly; /* bits [3:0] */ + + /* DLL calculation intermediate values, used during PHY timing calculations */ + u32 t_sdmclk_calc; /* DLL-quantized SDMCLK period */ + u32 dll_max_value; /* DLL delay field ceiling */ + + /* Tuning value for HS200/HS400 modes */ + u8 hs200_tune_val; + + /* Clock periods (in picoseconds) */ + u32 t_sdmclk; /* Master clock period */ + u32 t_sdclk; /* SD card clock period */ + + /* Current operating state */ + bool strobe_cmd; /* Enhanced strobe for CMD line */ + unsigned int mode; /* Current MMC_TIMING_* mode */ +}; + +/** + * init_ds() - Initialize PHY timing for Default Speed mode (25 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_ds(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 5000; + phy->t_cmd_output_max = t_sdclk - 5000; + phy->t_dat_output_min = 5000; + phy->t_dat_output_max = t_sdclk - 5000; +} + +/** + * init_hs() - Initialize PHY timing for High Speed mode (50 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_hs(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 2000; + phy->t_cmd_output_max = t_sdclk - 6000; + phy->t_dat_output_min = 2000; + phy->t_dat_output_max = t_sdclk - 6000; +} + +/** + * init_uhs_sdr12() - Initialize PHY timing for UHS SDR12 mode (25 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_uhs_sdr12(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 800; + phy->t_cmd_output_max = t_sdclk - 3000; + phy->t_dat_output_min = 800; + phy->t_dat_output_max = t_sdclk - 3000; +} + +/** + * init_uhs_sdr25() - Initialize PHY timing for UHS SDR25 mode (50 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_uhs_sdr25(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 800; + phy->t_cmd_output_max = t_sdclk - 3000; + phy->t_dat_output_min = 800; + phy->t_dat_output_max = t_sdclk - 3000; +} + +/** + * init_uhs_sdr50() - Initialize PHY timing for UHS SDR50 mode (100 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_uhs_sdr50(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 800; + phy->t_cmd_output_max = t_sdclk - 3000; + phy->t_dat_output_min = 800; + phy->t_dat_output_max = t_sdclk - 3000; +} + +/** + * init_uhs_sdr104() - Initialize PHY timing for UHS SDR104 mode (200 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_uhs_sdr104(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 800; + phy->t_cmd_output_max = t_sdclk - 1400; + phy->t_dat_output_min = 800; + phy->t_dat_output_max = t_sdclk - 1400; +} + +/** + * init_uhs_ddr50() - Initialize PHY timing for UHS DDR50 mode (50 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_uhs_ddr50(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 800; + phy->t_cmd_output_max = t_sdclk - 3000; + phy->t_dat_output_min = 800; + phy->t_dat_output_max = t_sdclk - 3000; +} + +/** + * init_emmc_sdr() - Initialize PHY timing for eMMC legacy/SDR mode. + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_emmc_sdr(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 3000; + phy->t_cmd_output_max = t_sdclk - 3000; + phy->t_dat_output_min = 3000; + phy->t_dat_output_max = t_sdclk - 3000; +} + +/** + * init_emmc_ddr() - Initialize PHY timing for eMMC DDR52 mode. + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_emmc_ddr(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 3000; + phy->t_cmd_output_max = t_sdclk - 3000; + phy->t_dat_output_min = 2500; + phy->t_dat_output_max = t_sdclk - 2500; +} + +/** + * init_emmc_hs200() - Initialize PHY timing for eMMC HS200 mode (200 MHz). + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_emmc_hs200(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 800; + phy->t_cmd_output_max = t_sdclk - 1400; + phy->t_dat_output_min = 800; + phy->t_dat_output_max = t_sdclk - 1400; +} + +/** + * init_emmc_hs400() - Initialize PHY timing for eMMC HS400/HS400ES mode. + * @phy: Pointer to SD6HC PHY state. + * @t_sdclk: SD clock period in picoseconds. + */ +static void init_emmc_hs400(struct sdhci_cdns6_phy *phy, u32 t_sdclk) +{ + phy->t_cmd_output_min = 800; + phy->t_cmd_output_max = t_sdclk - 1400; + phy->t_dat_output_min = 400; + phy->t_dat_output_max = t_sdclk - 400; +} + +/* + * init_timings - PHY timing initializers indexed by MMC_TIMING_* value. + * + * Each entry corresponds to a MMC_TIMING_* constant and sets the appropriate cmd/dat + * output timing windows in the PHY state struct. + */ +static void (* const init_timings[])(struct sdhci_cdns6_phy *, u32) = { + [MMC_TIMING_LEGACY] = init_ds, + [MMC_TIMING_MMC_HS] = init_emmc_sdr, + [MMC_TIMING_SD_HS] = init_hs, + [MMC_TIMING_UHS_SDR12] = init_uhs_sdr12, + [MMC_TIMING_UHS_SDR25] = init_uhs_sdr25, + [MMC_TIMING_UHS_SDR50] = init_uhs_sdr50, + [MMC_TIMING_UHS_SDR104] = init_uhs_sdr104, + [MMC_TIMING_UHS_DDR50] = init_uhs_ddr50, + [MMC_TIMING_MMC_DDR52] = init_emmc_ddr, + [MMC_TIMING_MMC_HS200] = init_emmc_hs200, + [MMC_TIMING_MMC_HS400] = init_emmc_hs400, +}; + +static unsigned int sdhci_cdns6_read_phy_reg(struct sdhci_cdns_priv *priv, const u32 address) +{ + writel(address, priv->hrs_addr + SDHCI_CDNS_HRS04); + return readl(priv->hrs_addr + SDHCI_CDNS_HRS05); +} + +static void sdhci_cdns6_write_phy_reg(struct sdhci_cdns_priv *priv, const u32 address, + const u32 value) +{ + writel(address, priv->hrs_addr + SDHCI_CDNS_HRS04); + writel(value, priv->hrs_addr + SDHCI_CDNS_HRS05); +} + +static int sdhci_cdns6_phy_lock_dll(struct sdhci_cdns6_phy *phy) +{ + u32 delay_element = phy->delay_element_org; + u32 delay_elements_in_sdmclk; + + delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk, delay_element); + if (delay_elements_in_sdmclk > 256) { + delay_element *= 2; + delay_elements_in_sdmclk = DIV_ROUND_UP(phy->t_sdmclk, delay_element); + + if (delay_elements_in_sdmclk > 256) + return -EINVAL; + + phy->dll_max_value = 127; + } else { + phy->dll_max_value = 255; + } + + phy->t_sdmclk_calc = delay_element * delay_elements_in_sdmclk; + phy->delay_element = delay_element; + phy->cp_dll_bypass_mode = false; + + return 0; +} + +static void sdhci_cdns6_phy_dll_bypass(struct sdhci_cdns6_phy *phy) +{ + phy->dll_max_value = 255; + phy->cp_dll_bypass_mode = true; +} + +static void sdhci_cdns6_phy_configure_dll(struct sdhci_cdns6_phy *phy) +{ + if (!phy->sdhc_extended_wr_mode) { + if (sdhci_cdns6_phy_lock_dll(phy) == 0) + return; + } + sdhci_cdns6_phy_dll_bypass(phy); +} + +static void sdhci_cdns6_phy_calc_out(struct sdhci_cdns6_phy *phy, bool cmd_not_dat) +{ + u32 wr0_dly = 0, wr1_dly = 0, output_min, output_max, phy_o_delay, + clk_wr_delay = 0, wr0_sdclk_dly = 0, wr1_sdclk_dly = 0; + bool ddr = (phy->mode == MMC_TIMING_UHS_DDR50) || (phy->mode == MMC_TIMING_MMC_DDR52) || + (phy->mode == MMC_TIMING_MMC_HS400); + bool data_ddr = ddr && !cmd_not_dat; + int t; + + if (cmd_not_dat) { + output_min = phy->t_cmd_output_min; + output_max = phy->t_cmd_output_max; + phy_o_delay = phy->phy_cmd_o_delay; + } else { + output_min = phy->t_dat_output_min; + output_max = phy->t_dat_output_max; + phy_o_delay = phy->phy_dat_o_delay; + } + + if (data_ddr) { + wr0_sdclk_dly = 1; + wr1_sdclk_dly = 1; + } + + t = phy_o_delay - phy->phy_sdclk_delay - output_min; + if (t < 0 && phy->sdhc_extended_wr_mode) { + u32 n_half_cycle = DIV_ROUND_UP(-t * 2, phy->t_sdmclk); + + wr0_dly = (n_half_cycle + 1) / 2; + if (data_ddr) + wr1_dly = (n_half_cycle + 1) / 2; + else + wr1_dly = (n_half_cycle + 1) % 2 + wr0_dly - 1; + } + + if (!phy->sdhc_extended_wr_mode) { + u32 out_hold, out_setup, out_hold_margin; + u32 n; + + if (!data_ddr) + wr0_dly = 1; + + out_setup = output_max; + out_hold = output_min; + out_hold_margin = DIV_ROUND_UP(out_setup - out_hold, 4); + out_hold += out_hold_margin; + + if (!phy->cp_dll_bypass_mode) + n = DIV_ROUND_UP(256 * out_hold, phy->t_sdmclk_calc); + else + n = DIV_ROUND_UP(out_hold, phy->delay_element) - 1; + + if (n <= phy->dll_max_value) + clk_wr_delay = n; + else + clk_wr_delay = 255; + } else { + /* sdhc_extended_wr_mode set => PHY IO cell work in SDR mode */ + clk_wr_delay = 0; + } + + if (cmd_not_dat) { + phy->sdhc_wrcmd0_dly = wr0_dly; + phy->sdhc_wrcmd1_dly = wr1_dly; + phy->cp_clk_wrdqs_delay = clk_wr_delay; + phy->sdhc_wrcmd0_sdclk_dly = wr0_sdclk_dly; + phy->sdhc_wrcmd1_sdclk_dly = wr1_sdclk_dly; + } else { + phy->sdhc_wrdata0_dly = wr0_dly; + phy->sdhc_wrdata1_dly = wr1_dly; + phy->cp_clk_wr_delay = clk_wr_delay; + phy->sdhc_wrdata0_sdclk_dly = wr0_sdclk_dly; + phy->sdhc_wrdata1_sdclk_dly = wr1_sdclk_dly; + } +} + +static void sdhci_cdns6_phy_calc_cmd_out(struct sdhci_cdns6_phy *phy) +{ + sdhci_cdns6_phy_calc_out(phy, true); +} + +static void sdhci_cdns6_phy_calc_cmd_in(struct sdhci_cdns6_phy *phy) +{ + phy->cp_io_mask_end = ((phy->iocell_output_delay + phy->iocell_input_delay) * 2) / + phy->t_sdmclk; + + /* cp_io_mask_end is a 3-bit field, clamp to max value of 7 */ + phy->cp_io_mask_end = min_t(u8, phy->cp_io_mask_end, 7); + + if (phy->strobe_cmd && phy->cp_io_mask_end > 0) + phy->cp_io_mask_end--; + + if (phy->strobe_cmd) { + phy->cp_use_phony_dqs_cmd = false; + phy->cp_read_dqs_cmd_delay = 64; + } else { + phy->cp_use_phony_dqs_cmd = true; + phy->cp_read_dqs_cmd_delay = 0; + } + + if ((phy->mode == MMC_TIMING_MMC_HS400 && !phy->strobe_cmd) || + phy->mode == MMC_TIMING_MMC_HS200) + phy->cp_read_dqs_cmd_delay = phy->hs200_tune_val; +} + +static void sdhci_cdns6_phy_calc_dat_in(struct sdhci_cdns6_phy *phy) +{ + u32 hcsdclkadj = 0; + bool strobe_dat = (phy->mode == MMC_TIMING_MMC_HS400); + + if (strobe_dat) { + phy->cp_use_phony_dqs = false; + phy->cp_read_dqs_delay = 64; + } else { + phy->cp_use_phony_dqs = true; + phy->cp_read_dqs_delay = 0; + } + + if (phy->mode == MMC_TIMING_MMC_HS200) + phy->cp_read_dqs_delay = phy->hs200_tune_val; + + if (strobe_dat) { + /* dqs loopback input via IO cell */ + hcsdclkadj += phy->iocell_input_delay; + /* dfi_dqs_in: mem_dqs -> clean_dqs_mod; delay of hic_dll_dqs_nand2 */ + hcsdclkadj += phy->delay_element / 2; + /* delay line */ + hcsdclkadj += phy->t_sdclk / 2; + /* PHY FIFO write pointer */ + hcsdclkadj += phy->t_sdclk / 2 + phy->delay_element; + /* 1st synchronizer */ + hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk) * phy->t_sdmclk - hcsdclkadj; + /* + * 2nd synchronizer + PHY FIFO read pointer + PHY rddata + * + PHY rddata registered, + FIFO 1st ciu_en + */ + hcsdclkadj += 5 * phy->t_sdmclk; + /* FIFO 2nd ciu_en */ + hcsdclkadj += phy->t_sdclk; + hcsdclkadj /= phy->t_sdclk; + } else { + u32 n; + + /* rebar PHY delay */ + hcsdclkadj += 2 * phy->t_sdmclk; + /* rebar output via IO cell */ + hcsdclkadj += phy->iocell_output_delay; + /* dqs loopback input via IO cell */ + hcsdclkadj += phy->iocell_input_delay; + /* dfi_dqs_in: mem_dqs -> clean_dqs_mod delay of hic_dll_dqs_nand2 */ + hcsdclkadj += phy->delay_element / 2; + /* dll: one delay element between SIGI_0 and SIGO_0 */ + hcsdclkadj += phy->delay_element; + /* dfi_dqs_in: mem_dqs_delayed -> clk_dqs delay of hic_dll_dqs_nand2 */ + hcsdclkadj += phy->delay_element / 2; + /* deskew DLL: clk_dqs -> clk_dqN: one delay element */ + hcsdclkadj += phy->delay_element; + + if (phy->t_sdclk == phy->t_sdmclk) + n = (hcsdclkadj - 2 * phy->t_sdmclk) / phy->t_sdclk; + else + n = hcsdclkadj / phy->t_sdclk; + + /* phase shift within one t_sdclk clock cycle caused by rebar - lbk dqs delay */ + hcsdclkadj = hcsdclkadj % phy->t_sdclk; + /* PHY FIFO write pointer */ + hcsdclkadj += phy->t_sdclk / 2; + /* 1st synchronizer */ + hcsdclkadj += DIV_ROUND_UP(hcsdclkadj, phy->t_sdmclk) * phy->t_sdmclk - hcsdclkadj; + /* + * 2nd synchronizer + PHY FIFO read pointer + PHY rddata + PHY rddata registered + */ + hcsdclkadj += 4 * phy->t_sdmclk; + + if ((phy->t_sdclk / phy->t_sdmclk) > 1) { + u32 tmp1, tmp2; + + tmp1 = hcsdclkadj; + tmp2 = (hcsdclkadj / phy->t_sdclk) * phy->t_sdclk + phy->t_sdclk - + phy->t_sdmclk; + if (tmp1 == tmp2) + tmp2 += phy->t_sdclk; + + /* FIFO aligns to clock cycle before ciu_en */ + hcsdclkadj += tmp2 - tmp1; + } + + /* FIFO 1st ciu_en */ + hcsdclkadj += phy->t_sdmclk; + /* FIFO 2nd ciu_en */ + hcsdclkadj += phy->t_sdclk; + hcsdclkadj /= phy->t_sdclk; + hcsdclkadj += n; + + if ((phy->t_sdclk / phy->t_sdmclk) >= 2) { + if (phy->mode == MMC_TIMING_UHS_DDR50 || phy->mode == MMC_TIMING_MMC_DDR52) + hcsdclkadj -= 2; + else + hcsdclkadj -= 1; + } else if ((phy->t_sdclk / phy->t_sdmclk) == 1) { + hcsdclkadj += 2; + } + + if (phy->mode == MMC_TIMING_UHS_SDR104 || phy->mode == MMC_TIMING_MMC_HS200) + hcsdclkadj -= 1; + } + + /* hcsdclkadj is a 4-bit field, clamp to max value of 15 */ + if (hcsdclkadj > 15) + hcsdclkadj = 15; + + phy->sdhc_hcsdclkadj = hcsdclkadj; +} + +static void sdhci_cdns6_phy_calc_dat_out(struct sdhci_cdns6_phy *phy) +{ + sdhci_cdns6_phy_calc_out(phy, false); +} + +static void sdhci_cdns6_phy_calc_io(struct sdhci_cdns6_phy *phy) +{ + u32 rw_compensate; + + rw_compensate = ((phy->iocell_input_delay + phy->iocell_output_delay) / phy->t_sdmclk) + + phy->sdhc_wrdata0_dly + 5 + 3; + + phy->sdhc_idelay_val = (2 * phy->iocell_input_delay) / phy->t_sdmclk; + + phy->cp_io_mask_start = 0; + if (phy->t_sdclk == phy->t_sdmclk && rw_compensate > 10) + phy->cp_io_mask_start = 2 * (rw_compensate - 10); + + if (phy->mode == MMC_TIMING_UHS_SDR104) + phy->cp_io_mask_start++; + + if (phy->t_sdclk == phy->t_sdmclk && phy->mode == MMC_TIMING_UHS_SDR50) + phy->cp_io_mask_start++; + + /* cp_io_mask_start is a 3-bit field, clamp to max value of 7 */ + phy->cp_io_mask_start = min_t(u8, phy->cp_io_mask_start, 7); + + phy->sdhc_rw_compensate = rw_compensate; +} + +static void sdhci_cdns6_phy_calc_settings(struct sdhci_cdns6_phy *phy) +{ + sdhci_cdns6_phy_calc_cmd_out(phy); + sdhci_cdns6_phy_calc_cmd_in(phy); + sdhci_cdns6_phy_calc_dat_out(phy); + sdhci_cdns6_phy_calc_dat_in(phy); + sdhci_cdns6_phy_calc_io(phy); +} + +static int sdhci_cdns6_dll_reset(struct sdhci_cdns_priv *priv, bool reset) +{ + u32 reg; + int ret = 0; + + reg = readl(priv->hrs_addr + SDHCI_CDNS_HRS09); + if (reset) + reg &= ~SDHCI_CDNS_HRS09_PHY_SW_RESET; + else + reg |= SDHCI_CDNS_HRS09_PHY_SW_RESET; + + writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS09); + + /* After releasing PHY from reset, wait until PHY_INIT_COMPLETE is set within 3000us */ + if (!reset) { + ret = readl_poll_timeout(priv->hrs_addr + SDHCI_CDNS_HRS09, reg, (reg & + SDHCI_CDNS_HRS09_PHY_INIT_COMPLETE), 0, 3000); + } + + return ret; +} + +int sdhci_cdns6_phy_init(struct sdhci_cdns_priv *priv) +{ + struct sdhci_cdns6_phy *phy = priv->phy; + u32 reg; + int ret; + + sdhci_cdns6_dll_reset(priv, true); + + reg = sdhci_cdns6_read_phy_reg(priv, SDHCI_CDNS6_PHY_DQS_TIMING_REG); + reg &= ~SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS; + reg &= ~SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD; + reg |= SDHCI_CDNS6_PHY_DQS_TIMING_USE_EXT_LPBK_DQS; + reg |= SDHCI_CDNS6_PHY_DQS_TIMING_USE_LPBK_DQS; + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS, phy->cp_use_phony_dqs); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQS_TIMING_USE_PHONY_DQS_CMD, phy->cp_use_phony_dqs_cmd); + sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_DQS_TIMING_REG, reg); + + reg = sdhci_cdns6_read_phy_reg(priv, SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_REG); + reg &= ~SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_RD_DEL_SEL; + reg |= SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_UNDERRUN_SUPPRESS; + reg |= SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_GATE_CFG_ALWAYS_ON; + reg |= SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_SYNC_METHOD; + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_RD_DEL_SEL, + SDHCI_CDNS6_PHY_DEFAULT_RD_DEL_SEL); + sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_GATE_LPBK_CTRL_REG, reg); + + reg = FIELD_PREP(SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_BYPASS_MODE, phy->cp_dll_bypass_mode); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_PHASE_DETECT_SEL, + SDHCI_CDNS6_PHY_DEFAULT_PHASE_DETECT_SEL); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_DLL_LOCK_NUM, + SDHCI_CDNS6_PHY_DEFAULT_DLL_LOCK_NUM); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_DLL_START_POINT, + SDHCI_CDNS6_PHY_DEFAULT_DLL_START); + sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_DLL_MASTER_CTRL_REG, reg); + + reg = FIELD_PREP(SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_CMD_DELAY, + phy->cp_read_dqs_cmd_delay); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_CLK_WRDQS_DELAY, phy->cp_clk_wrdqs_delay); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_CLK_WR_DELAY, phy->cp_clk_wr_delay); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY, phy->cp_read_dqs_delay); + sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_REG, reg); + + reg = sdhci_cdns6_read_phy_reg(priv, SDHCI_CDNS6_PHY_CTRL_REG); + reg &= ~SDHCI_CDNS6_PHY_CTRL_PHONY_DQS_TIMING; + sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_CTRL_REG, reg); + + /* + * Ensure all preceding PHY register writes complete and reach the controller before + * releasing the PHY from reset. Without this, SDR104 has been observed to fail + * intermittently on some boards. + */ + wmb(); + + ret = sdhci_cdns6_dll_reset(priv, false); + if (ret) + return ret; + + reg = sdhci_cdns6_read_phy_reg(priv, SDHCI_CDNS6_PHY_DQ_TIMING_REG); + reg &= ~SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_ALWAYS_ON; + reg &= ~SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_END; + reg &= ~SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_START; + reg &= ~SDHCI_CDNS6_PHY_DQ_TIMING_DATA_SELECT_OE_END; + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_END, phy->cp_io_mask_end); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQ_TIMING_IO_MASK_START, phy->cp_io_mask_start); + reg |= FIELD_PREP(SDHCI_CDNS6_PHY_DQ_TIMING_DATA_SELECT_OE_END, + SDHCI_CDNS6_PHY_DEFAULT_DATA_SELECT_OE_END); + sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_DQ_TIMING_REG, reg); + + /* Ensure DQ timing programming is visible before HRS09 follow-up writes */ + wmb(); + + reg = readl(priv->hrs_addr + SDHCI_CDNS_HRS09); + if (phy->sdhc_extended_wr_mode) + reg |= SDHCI_CDNS_HRS09_EXTENDED_WR_MODE; + else + reg &= ~SDHCI_CDNS_HRS09_EXTENDED_WR_MODE; + + if (phy->sdhc_extended_rd_mode) + reg |= SDHCI_CDNS_HRS09_EXTENDED_RD_MODE; + else + reg &= ~SDHCI_CDNS_HRS09_EXTENDED_RD_MODE; + + reg |= SDHCI_CDNS_HRS09_RDDATA_EN; + reg |= SDHCI_CDNS_HRS09_RDCMD_EN; + writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS09); + + reg = FIELD_PREP(SDHCI_CDNS_HRS10_HCSDCLKADJ, phy->sdhc_hcsdclkadj); + writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS10); + + reg = FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_SDCLK_DLY, phy->sdhc_wrdata1_sdclk_dly); + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_SDCLK_DLY, phy->sdhc_wrdata0_sdclk_dly); + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_SDCLK_DLY, phy->sdhc_wrcmd1_sdclk_dly); + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_SDCLK_DLY, phy->sdhc_wrcmd0_sdclk_dly); + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA1_DLY, phy->sdhc_wrdata1_dly); + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRDATA0_DLY, phy->sdhc_wrdata0_dly); + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD1_DLY, phy->sdhc_wrcmd1_dly); + reg |= FIELD_PREP(SDHCI_CDNS_HRS16_WRCMD0_DLY, phy->sdhc_wrcmd0_dly); + writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS16); + + reg = FIELD_PREP(SDHCI_CDNS_HRS07_RW_COMPENSATE, phy->sdhc_rw_compensate); + reg |= FIELD_PREP(SDHCI_CDNS_HRS07_IDELAY_VAL, phy->sdhc_idelay_val); + writel(reg, priv->hrs_addr + SDHCI_CDNS_HRS07); + + /* Allow 5 to 5.5 ms for clock and PHY signals to stabilize after configuration */ + usleep_range(5000, 5500); + + return 0; +} + +int sdhci_cdns6_set_tune_val(struct sdhci_host *host, unsigned int val) +{ + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + struct sdhci_cdns6_phy *phy = priv->phy; + u32 tmp, tuneval; + int ret; + + /* + * Scale tuning tap (val in [0, SDHCI_CDNS_MAX_TUNING_LOOP-1]) to the + * 8-bit PHY DLL slave delay field [0, 255]. With MAX_TUNING_LOOP=40 + * and FIELD_SIZE=256, the result fits in 8 bits. + */ + tuneval = (val * SDHCI_CDNS6_PHY_DLL_FIELD_SIZE) / SDHCI_CDNS_MAX_TUNING_LOOP; + + phy->hs200_tune_val = tuneval; + phy->cp_read_dqs_cmd_delay = tuneval; + phy->cp_read_dqs_delay = tuneval; + + tmp = sdhci_cdns6_read_phy_reg(priv, SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_REG); + tmp &= ~(SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_CMD_DELAY | + SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY); + tmp |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_CMD_DELAY, tuneval); + tmp |= FIELD_PREP(SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_READ_DQS_DELAY, tuneval); + + sdhci_cdns6_dll_reset(priv, true); + sdhci_cdns6_write_phy_reg(priv, SDHCI_CDNS6_PHY_DLL_SLAVE_CTRL_REG, tmp); + + ret = sdhci_cdns6_dll_reset(priv, false); + if (ret) + dev_warn(mmc_dev(host->mmc), "%s: DLL reset release failed: %d\n", __func__, ret); + + return ret; +} + +static int sdhci_cdns6_phy_update_timings(struct sdhci_host *host) +{ + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + struct sdhci_cdns6_phy *phy = priv->phy; + u32 t_sdmclk = phy->t_sdmclk; + + /* Validate mode is within supported range */ + if (phy->mode >= ARRAY_SIZE(init_timings)) + return -EINVAL; + + /* initialize input */ + init_timings[phy->mode](phy, phy->t_sdclk); + + phy->strobe_cmd = false; + + if (priv->enhanced_strobe) + phy->strobe_cmd = true; + + phy->phy_sdclk_delay = 2 * t_sdmclk; + + /* + * CMD and DAT output delays are currently identical, but kept separate to allow + * independent tuning for specific modes (e.g., HS400) or board-specific optimizations + * in the future. + */ + phy->phy_cmd_o_delay = 2 * t_sdmclk + t_sdmclk / 2; + phy->phy_dat_o_delay = 2 * t_sdmclk + t_sdmclk / 2; + + if (phy->t_sdclk == phy->t_sdmclk) { + phy->sdhc_extended_wr_mode = false; + phy->sdhc_extended_rd_mode = false; + } else { + phy->sdhc_extended_wr_mode = true; + phy->sdhc_extended_rd_mode = true; + } + + sdhci_cdns6_phy_configure_dll(phy); + sdhci_cdns6_phy_calc_settings(phy); + + return 0; +} + +int sdhci_cdns6_phy_probe(struct platform_device *pdev, struct sdhci_cdns_priv *priv) +{ + struct device *dev = &pdev->dev; + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_cdns6_phy *phy; + unsigned long val; + int ret; + + phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + val = clk_get_rate(pltfm_host->clk); + if (!val) + return dev_err_probe(dev, -EINVAL, "failed to get controller clock rate\n"); + + phy->t_sdmclk = DIV_ROUND_DOWN_ULL(1000000000000ULL, val); + + /* + * Optional board-level PHY timing properties. When omitted, use the + * Cadence-recommended defaults (2500 ps IO-cell delays, 24 ps DLL + * delay element) that are valid for typical SoC packaging. + */ + ret = of_property_read_u32(dev->of_node, "cdns,iocell-input-delay-ps", + &phy->iocell_input_delay); + if (ret) + phy->iocell_input_delay = SDHCI_CDNS6_PHY_DEFAULT_IOCELL_DELAY; + + ret = of_property_read_u32(dev->of_node, "cdns,iocell-output-delay-ps", + &phy->iocell_output_delay); + if (ret) + phy->iocell_output_delay = SDHCI_CDNS6_PHY_DEFAULT_IOCELL_DELAY; + + ret = of_property_read_u32(dev->of_node, "cdns,delay-element-ps", &phy->delay_element); + if (ret) { + phy->delay_element = SDHCI_CDNS6_PHY_DEFAULT_DELAY_ELEMENT; + } else if (!phy->delay_element) { + dev_warn(dev, "cdns,delay-element-ps is 0, using default %u\n", + SDHCI_CDNS6_PHY_DEFAULT_DELAY_ELEMENT); + phy->delay_element = SDHCI_CDNS6_PHY_DEFAULT_DELAY_ELEMENT; + } + + phy->delay_element_org = phy->delay_element; + + priv->phy = phy; + + return 0; +} + +static void sdhci_cdns6_phy_reconfigure(struct sdhci_host *host, unsigned int timing) +{ + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + struct sdhci_cdns6_phy *phy = priv->phy; + unsigned int rate = host->mmc->actual_clock ?: host->mmc->ios.clock; + u32 t_sdclk; + int ret; + + if (!rate) + return; + + t_sdclk = DIV_ROUND_DOWN_ULL(1000000000000ULL, rate); + if (phy->mode == timing && phy->t_sdclk == t_sdclk && + phy->strobe_cmd == priv->enhanced_strobe) + return; + + phy->t_sdclk = t_sdclk; + phy->mode = timing; + + ret = sdhci_cdns6_phy_update_timings(host); + if (ret) { + phy->t_sdclk = 0; + dev_warn(mmc_dev(host->mmc), "%s: update timings failed: %d\n", __func__, ret); + return; + } + + ret = sdhci_cdns6_phy_init(priv); + if (ret) { + phy->t_sdclk = 0; + dev_warn(mmc_dev(host->mmc), "%s: phy init failed: %d\n", __func__, ret); + } +} + +void sdhci_cdns6_set_clock(struct sdhci_host *host, unsigned int clock) +{ + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + struct sdhci_cdns6_phy *phy = priv->phy; + + sdhci_set_clock(host, clock); + + if (!clock) { + /* Force PHY reprogramming on the next non-zero clock. */ + phy->t_sdclk = 0; + return; + } + + sdhci_cdns6_phy_reconfigure(host, host->mmc->ios.timing); +} + +void sdhci_cdns6_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) +{ + sdhci_cdns6_phy_reconfigure(host, timing); +} + +void sdhci_cdns6_hw_reset(struct sdhci_host *host) +{ + struct sdhci_cdns_priv *priv = sdhci_cdns_priv(host); + void __iomem *reg; + + reg = priv->hrs_addr + SDHCI_CDNS_HRS11; + writel(SDHCI_CDNS_HRS11_EMMC_RST, reg); + /* eMMC HW reset assertion: spec requires >= 1us, give margin */ + usleep_range(10, 20); + writel(0, reg); + /* For eMMC, minimum is 200us but give it 300us for good measure */ + usleep_range(300, 1000); +} diff --git a/drivers/mmc/host/sdhci-cadence.h b/drivers/mmc/host/sdhci-cadence.h new file mode 100644 index 000000000000..2ac11befb53b --- /dev/null +++ b/drivers/mmc/host/sdhci-cadence.h @@ -0,0 +1,118 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2026 Altera Corporation + * Author: Tanmay Kathpalia <tanmay.kathpalia@altera.com> + * + * Cadence SD/SDIO/eMMC Host Controller driver - common header + * Shared definitions and structures for the Cadence SDHCI driver. + * Contains private data and declarations for SD6HC-specific functions + * called by the main driver in sdhci-cadence-core.c. + */ + +#ifndef _MMC_HOST_SDHCI_CADENCE_H +#define _MMC_HOST_SDHCI_CADENCE_H + +#include <linux/compiler.h> +#include <linux/spinlock.h> +#include <linux/types.h> + +#include "sdhci-pltfm.h" + +struct reset_control; + +/* HRS - Host Register Set (specific to Cadence) */ +#define SDHCI_CDNS_HRS04 0x10 /* PHY access: address port */ +#define SDHCI_CDNS_HRS05 0x14 /* PHY access: data port */ + +/* + * The tuned val register is 6 bit-wide, but not the whole of the range is + * available. The range 0-42 seems to be available (then 43 wraps around to 0) + * but I am not quite sure if it is official. Use only 0 to 39 for safety. + */ +#define SDHCI_CDNS_MAX_TUNING_LOOP 40 + +/** + * struct sdhci_cdns_priv - Cadence SDHCI private controller data + * @hrs_addr: Base address of Cadence Host Register Set (HRS) registers. + * @ctl_addr: Base address for write control registers. + * Used only for "amd,pensando-elba-sd4hc" compatible controllers to enable + * byte-lane writes. + * @wrlock: Spinlock for protecting register writes (Elba only). + * @enhanced_strobe: Flag indicating if Enhanced Strobe (HS400ES) is enabled. + * @priv_writel: Optional SoC-specific write function for register access. + * Used for Elba to ensure correct byte-lane enable. + * @rst_hw: Hardware reset control for the eMMC card RST_n pin (SD4HC only). + * @phy: Opaque pointer to variant-specific PHY data. + * For SD4HC: points to struct sdhci_cdns4_phy. + * For SD6HC: points to struct sdhci_cdns6_phy. + */ +struct sdhci_cdns_priv { + void __iomem *hrs_addr; + void __iomem *ctl_addr; /* write control */ + spinlock_t wrlock; /* write lock */ + bool enhanced_strobe; + void (*priv_writel)(struct sdhci_cdns_priv *priv, u32 val, void __iomem *reg); + struct reset_control *rst_hw; + void *phy; +}; + +/** + * sdhci_cdns_priv - Helper to retrieve Cadence private data from sdhci_host + * @host: Pointer to struct sdhci_host. + * + * Return: Pointer to struct sdhci_cdns_priv. + */ +static inline void *sdhci_cdns_priv(struct sdhci_host *host) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + + return sdhci_pltfm_priv(pltfm_host); +} + +/** + * sdhci_cdns6_set_clock - Set SDCLK and reprogram SD6HC PHY timings. + * @host: Pointer to struct sdhci_host. + * @clock: Requested clock frequency in Hz (0 to disable). + */ +void sdhci_cdns6_set_clock(struct sdhci_host *host, unsigned int clock); + +/** + * sdhci_cdns6_set_uhs_signaling - Program PHY registers for a specific timing mode. + * @host: Pointer to struct sdhci_host. + * @timing: MMC timing mode (MMC_TIMING_*). + */ +void sdhci_cdns6_set_uhs_signaling(struct sdhci_host *host, unsigned int timing); + +/** + * sdhci_cdns6_set_tune_val - Set the PHY tuning value. + * @host: Pointer to struct sdhci_host. + * @val: Tuning value to program. + * + * Return: 0 on success, -ETIMEDOUT if DLL reset release times out. + */ +int sdhci_cdns6_set_tune_val(struct sdhci_host *host, unsigned int val); + +/** + * sdhci_cdns6_phy_probe - Probe and initialize Cadence SD6HC PHY parameters + * @pdev: Platform device pointer + * @priv: Pointer to Cadence private data structure + * + * Return: 0 on success or a negative error code. + */ +int sdhci_cdns6_phy_probe(struct platform_device *pdev, struct sdhci_cdns_priv *priv); + +/** + * sdhci_cdns6_hw_reset - Perform hardware reset of the Cadence SDHCI controller. + * @host: Pointer to struct sdhci_host. + */ +void sdhci_cdns6_hw_reset(struct sdhci_host *host); + +/** + * sdhci_cdns6_phy_init - Initialize the SD6HC PHY with current settings. + * @priv: Pointer to Cadence private data structure. + * + * Return: 0 on success, -ETIMEDOUT if PHY initialization times out. + */ +int sdhci_cdns6_phy_init(struct sdhci_cdns_priv *priv); + +#endif /* _MMC_HOST_SDHCI_CADENCE_H */ diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index 4aff965f0e2e..aecd425bf095 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -1770,7 +1770,7 @@ static void sdhci_msm_handle_pwr_irq(struct sdhci_host *host, int irq) /* * The driver has to acknowledge the interrupt, switch voltages and - * report back if it succeded or not to this register. The voltage + * report back if it succeeded or not to this register. The voltage * switches are handled by the sdhci core, so just report success. */ msm_host_writel(msm_host, irq_ack, host, @@ -1959,6 +1959,7 @@ static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host, } mmc->caps2 |= MMC_CAP2_CRYPTO; + mmc->caps2 |= MMC_CAP2_CRYPTO_NO_REPROG; return 0; } @@ -2055,6 +2056,12 @@ static int sdhci_msm_ice_prepare_key(struct blk_crypto_profile *profile, return qcom_ice_prepare_key(msm_host->ice, lt_key, lt_key_size, eph_key); } +static void sdhci_msm_ice_restore(struct sdhci_host *host) +{ + if (host->mmc->caps2 & MMC_CAP2_CRYPTO) + blk_crypto_reprogram_all_keys(&host->mmc->crypto_profile); +} + static void sdhci_msm_non_cqe_ice_init(struct sdhci_host *host) { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -2149,6 +2156,10 @@ sdhci_msm_ice_suspend(struct sdhci_msm_host *msm_host) { return 0; } + +static inline void sdhci_msm_ice_restore(struct sdhci_host *host) +{ +} #endif /* !CONFIG_MMC_CRYPTO */ /*****************************************************************************\ @@ -2983,9 +2994,28 @@ static int sdhci_msm_runtime_resume(struct device *dev) return ret; } +static int sdhci_msm_restore(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + int ret; + + ret = pm_runtime_force_resume(dev); + if (ret) + return ret; + + sdhci_msm_ice_restore(host); + + return ret; +} + static const struct dev_pm_ops sdhci_msm_pm_ops = { - SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) RUNTIME_PM_OPS(sdhci_msm_runtime_suspend, sdhci_msm_runtime_resume, NULL) + .suspend = pm_sleep_ptr(pm_runtime_force_suspend), + .resume = pm_sleep_ptr(pm_runtime_force_resume), + .freeze = pm_sleep_ptr(pm_runtime_force_suspend), + .restore = pm_sleep_ptr(sdhci_msm_restore), + .thaw = pm_sleep_ptr(pm_runtime_force_resume), + .poweroff = pm_sleep_ptr(pm_runtime_force_suspend), }; static struct platform_driver sdhci_msm_driver = { diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 4ca73e7d799e..0ee22923ad56 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1552,8 +1552,8 @@ sdhci_arasan_register_sdcardclk(struct sdhci_arasan_data *sdhci_arasan, struct device *dev) { struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; + struct clk_init_data sdcardclk_init = {}; struct device_node *np = dev->of_node; - struct clk_init_data sdcardclk_init; const char *parent_clk_name; int ret; @@ -1604,8 +1604,8 @@ sdhci_arasan_register_sampleclk(struct sdhci_arasan_data *sdhci_arasan, struct device *dev) { struct sdhci_arasan_clk_data *clk_data = &sdhci_arasan->clk_data; + struct clk_init_data sampleclk_init = {}; struct device_node *np = dev->of_node; - struct clk_init_data sampleclk_init; const char *parent_clk_name; int ret; diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index 1baa2379c9c3..09702022e708 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -239,6 +239,8 @@ SDHCI_TRNS_BLK_CNT_EN | \ SDHCI_TRNS_DMA) +#define CQHCI_SW_ERR_HALT_REQ_DISABLE BIT(1) + #define to_pltfm_data(priv, name) \ container_of((priv)->dwcmshc_pdata, struct name##_pltfm_data, dwcmshc_pdata) @@ -702,6 +704,8 @@ static void rk35xx_sdhci_cqe_pre_enable(struct mmc_host *mmc) static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc) { struct sdhci_host *host = mmc_priv(mmc); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); u32 reg; reg = sdhci_readl(host, SDHCI_PRESENT_STATE); @@ -712,6 +716,15 @@ static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc) sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE); + /* + * Disable the halt request raised on an I/O error, otherwise a + * doorbell write gets a slave response error while the halt request + * is pending and hangs the CPU. This bit is reserved on older SoCs, + * so the write is a no-op there. + */ + sdhci_writel(host, CQHCI_SW_ERR_HALT_REQ_DISABLE, + dwc_priv->vendor_specific_area2 + CQHCI_CTL); + sdhci_cqe_enable(mmc); } diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index 8345e2c5a034..1ca3562d8f73 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -1381,7 +1381,7 @@ static void esdhc_init(struct platform_device *pdev, struct sdhci_host *host) if (!IS_ERR(clk)) { /* * esdhc->peripheral_clock would be assigned with a value - * which is eSDHC base clock when use periperal clock. + * which is eSDHC base clock when use peripheral clock. * For some platforms, the clock value got by common clk * API is peripheral clock while the eSDHC base clock is * 1/2 peripheral clock. diff --git a/drivers/mmc/host/sdhci-omap.c b/drivers/mmc/host/sdhci-omap.c index b5d7c1a80a92..7a198fb26628 100644 --- a/drivers/mmc/host/sdhci-omap.c +++ b/drivers/mmc/host/sdhci-omap.c @@ -332,7 +332,7 @@ static int sdhci_omap_execute_tuning(struct mmc_host *mmc, u32 opcode) u32 reg; int i; - /* clock tuning is not needed for upto 52MHz */ + /* clock tuning is not needed for up to 52MHz */ if (ios->clock <= 52000000) return 0; diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index b121d896a804..a9000f3bd419 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -2170,14 +2170,12 @@ static struct sdhci_pci_slot *sdhci_pci_probe_slot( host->irq = pdev->irq; - ret = pcim_iomap_regions(pdev, BIT(bar), mmc_hostname(host->mmc)); - if (ret) { + host->ioaddr = pcim_iomap_region(pdev, bar, mmc_hostname(host->mmc)); + if (IS_ERR(host->ioaddr)) { dev_err(&pdev->dev, "cannot request region\n"); - return ERR_PTR(ret); + return ERR_CAST(host->ioaddr); } - host->ioaddr = pcim_iomap_table(pdev)[bar]; - if (chip->fixes && chip->fixes->probe_slot) { ret = chip->fixes->probe_slot(slot); if (ret) diff --git a/drivers/mmc/host/sdhci-pxav3.c b/drivers/mmc/host/sdhci-pxav3.c index d082c4e21aa9..f39764ffd4a0 100644 --- a/drivers/mmc/host/sdhci-pxav3.c +++ b/drivers/mmc/host/sdhci-pxav3.c @@ -123,7 +123,7 @@ static int mv_conf_mbus_windows(struct platform_device *pdev, static int armada_38x_quirks(struct platform_device *pdev, struct sdhci_host *host) { - struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_pxa *pxa = sdhci_pltfm_priv(pltfm_host); struct resource *res; @@ -155,7 +155,7 @@ static int armada_38x_quirks(struct platform_device *pdev, * controller has different capabilities than the ones shown * in its registers */ - if (of_property_read_bool(np, "no-1-8-v")) { + if (device_property_present(dev, "no-1-8-v")) { host->caps &= ~SDHCI_CAN_VDD_180; host->mmc->caps &= ~MMC_CAP_1_8V_DDR; } else { @@ -284,7 +284,8 @@ static void pxav3_set_uhs_signaling(struct sdhci_host *host, unsigned int uhs) uhs == MMC_TIMING_UHS_DDR50) { reg_val &= ~SDIO3_CONF_CLK_INV; reg_val |= SDIO3_CONF_SD_FB_CLK; - } else if (uhs == MMC_TIMING_MMC_HS) { + } else if (uhs == MMC_TIMING_MMC_HS || + uhs == MMC_TIMING_SD_HS) { reg_val &= ~SDIO3_CONF_CLK_INV; reg_val &= ~SDIO3_CONF_SD_FB_CLK; } else { @@ -363,14 +364,13 @@ MODULE_DEVICE_TABLE(of, sdhci_pxav3_of_match); static struct sdhci_pxa_platdata *pxav3_get_mmc_pdata(struct device *dev) { struct sdhci_pxa_platdata *pdata; - struct device_node *np = dev->of_node; u32 clk_delay_cycles; pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; - if (!of_property_read_u32(np, "mrvl,clk-delay-cycles", + if (!device_property_read_u32(dev, "mrvl,clk-delay-cycles", &clk_delay_cycles)) pdata->clk_delay_cycles = clk_delay_cycles; @@ -401,7 +401,6 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host; struct sdhci_pxa_platdata *pdata = pdev->dev.platform_data; struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; struct sdhci_host *host = NULL; struct sdhci_pxa *pxa = NULL; const struct of_device_id *match; @@ -432,7 +431,7 @@ static int sdhci_pxav3_probe(struct platform_device *pdev) /* enable 1/8V DDR capable */ host->mmc->caps |= MMC_CAP_1_8V_DDR; - if (of_device_is_compatible(np, "marvell,armada-380-sdhci")) { + if (device_is_compatible(dev, "marvell,armada-380-sdhci")) { ret = armada_38x_quirks(pdev, host); if (ret < 0) goto err_mbus_win; diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index f0fd81409a86..98c1fe20bfcd 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -409,7 +409,7 @@ struct sdhci_adma2_64_desc { #define SDHCI_MAX_MRQS 2 /* - * 48bit command and 136 bit response in 100KHz clock could take upto 2.48ms. + * 48bit command and 136 bit response in 100KHz clock could take up to 2.48ms. * However since the start time of the command, the time between * command and response, and the time between response and start of data is * not known, set the command transfer time to 10ms. diff --git a/drivers/mmc/host/sunplus-mmc.c b/drivers/mmc/host/sunplus-mmc.c index 63279760239c..e947c84eef94 100644 --- a/drivers/mmc/host/sunplus-mmc.c +++ b/drivers/mmc/host/sunplus-mmc.c @@ -294,6 +294,11 @@ static void spmmc_set_bus_timing(struct spmmc_host *host, unsigned int timing) value = readl(host->base + SPMMC_SD_CONFIG0_REG); value |= SPMMC_SD_DDR_MODE; writel(value, host->base + SPMMC_SD_CONFIG0_REG); + /* DDR mode needs rd_crc_dly=1 */ + value = readl(host->base + SPMMC_SD_TIMING_CONFIG0_REG); + value &= ~SPMMC_SD_READ_CRC_DELAY; + value |= FIELD_PREP(SPMMC_SD_READ_CRC_DELAY, 1); + writel(value, host->base + SPMMC_SD_TIMING_CONFIG0_REG); } else { value = readl(host->base + SPMMC_SD_CONFIG0_REG); value &= ~SPMMC_SD_DDR_MODE; diff --git a/drivers/mmc/host/ushc.c b/drivers/mmc/host/ushc.c index 7e8af5a06ac9..410e5bb62752 100644 --- a/drivers/mmc/host/ushc.c +++ b/drivers/mmc/host/ushc.c @@ -388,10 +388,7 @@ static void ushc_enable_sdio_irq(struct mmc_host *mmc, int enable) { struct ushc_data *ushc = mmc_priv(mmc); - if (enable) - set_bit(INT_EN, &ushc->flags); - else - clear_bit(INT_EN, &ushc->flags); + assign_bit(INT_EN, &ushc->flags, enable); } static void ushc_clean_up(struct ushc_data *ushc) diff --git a/drivers/mmc/host/vub300.c b/drivers/mmc/host/vub300.c deleted file mode 100644 index 2dae474dcd06..000000000000 --- a/drivers/mmc/host/vub300.c +++ /dev/null @@ -1,2493 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Remote VUB300 SDIO/SDmem Host Controller Driver - * - * Copyright (C) 2010 Elan Digital Systems Limited - * - * based on USB Skeleton driver - 2.2 - * - * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com) - * - * VUB300: is a USB 2.0 client device with a single SDIO/SDmem/MMC slot - * Any SDIO/SDmem/MMC device plugged into the VUB300 will appear, - * by virtue of this driver, to have been plugged into a local - * SDIO host controller, similar to, say, a PCI Ricoh controller - * This is because this kernel device driver is both a USB 2.0 - * client device driver AND an MMC host controller driver. Thus - * if there is an existing driver for the inserted SDIO/SDmem/MMC - * device then that driver will be used by the kernel to manage - * the device in exactly the same fashion as if it had been - * directly plugged into, say, a local pci bus Ricoh controller - * - * RANT: this driver was written using a display 128x48 - converting it - * to a line width of 80 makes it very difficult to support. In - * particular functions have been broken down into sub functions - * and the original meaningful names have been shortened into - * cryptic ones. - * The problem is that executing a fragment of code subject to - * two conditions means an indentation of 24, thus leaving only - * 56 characters for a C statement. And that is quite ridiculous! - * - * Data types: data passed to/from the VUB300 is fixed to a number of - * bits and driver data fields reflect that limit by using - * u8, u16, u32 - */ -#include <linux/kernel.h> -#include <linux/errno.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/module.h> -#include <linux/kref.h> -#include <linux/uaccess.h> -#include <linux/usb.h> -#include <linux/mutex.h> -#include <linux/mmc/host.h> -#include <linux/mmc/card.h> -#include <linux/mmc/sdio_func.h> -#include <linux/mmc/sdio_ids.h> -#include <linux/workqueue.h> -#include <linux/ctype.h> -#include <linux/firmware.h> -#include <linux/scatterlist.h> - -struct host_controller_info { - u8 info_size; - u16 firmware_version; - u8 number_of_ports; -} __packed; - -#define FIRMWARE_BLOCK_BOUNDARY 1024 -struct sd_command_header { - u8 header_size; - u8 header_type; - u8 port_number; - u8 command_type; /* Bit7 - Rd/Wr */ - u8 command_index; - u8 transfer_size[4]; /* ReadSize + ReadSize */ - u8 response_type; - u8 arguments[4]; - u8 block_count[2]; - u8 block_size[2]; - u8 block_boundary[2]; - u8 reserved[44]; /* to pad out to 64 bytes */ -} __packed; - -struct sd_irqpoll_header { - u8 header_size; - u8 header_type; - u8 port_number; - u8 command_type; /* Bit7 - Rd/Wr */ - u8 padding[16]; /* don't ask why !! */ - u8 poll_timeout_msb; - u8 poll_timeout_lsb; - u8 reserved[42]; /* to pad out to 64 bytes */ -} __packed; - -struct sd_common_header { - u8 header_size; - u8 header_type; - u8 port_number; -} __packed; - -struct sd_response_header { - u8 header_size; - u8 header_type; - u8 port_number; - u8 command_type; - u8 command_index; - u8 command_response[]; -} __packed; - -struct sd_status_header { - u8 header_size; - u8 header_type; - u8 port_number; - u16 port_flags; - u32 sdio_clock; - u16 host_header_size; - u16 func_header_size; - u16 ctrl_header_size; -} __packed; - -struct sd_error_header { - u8 header_size; - u8 header_type; - u8 port_number; - u8 error_code; -} __packed; - -struct sd_interrupt_header { - u8 header_size; - u8 header_type; - u8 port_number; -} __packed; - -struct offload_registers_access { - u8 command_byte[4]; - u8 Respond_Byte[4]; -} __packed; - -#define INTERRUPT_REGISTER_ACCESSES 15 -struct sd_offloaded_interrupt { - u8 header_size; - u8 header_type; - u8 port_number; - struct offload_registers_access reg[INTERRUPT_REGISTER_ACCESSES]; -} __packed; - -struct sd_register_header { - u8 header_size; - u8 header_type; - u8 port_number; - u8 command_type; - u8 command_index; - u8 command_response[6]; -} __packed; - -#define PIGGYBACK_REGISTER_ACCESSES 14 -struct sd_offloaded_piggyback { - struct sd_register_header sdio; - struct offload_registers_access reg[PIGGYBACK_REGISTER_ACCESSES]; -} __packed; - -union sd_response { - struct sd_common_header common; - struct sd_status_header status; - struct sd_error_header error; - struct sd_interrupt_header interrupt; - struct sd_response_header response; - struct sd_offloaded_interrupt irq; - struct sd_offloaded_piggyback pig; -} __packed; - -union sd_command { - struct sd_command_header head; - struct sd_irqpoll_header poll; -} __packed; - -enum SD_RESPONSE_TYPE { - SDRT_UNSPECIFIED = 0, - SDRT_NONE, - SDRT_1, - SDRT_1B, - SDRT_2, - SDRT_3, - SDRT_4, - SDRT_5, - SDRT_5B, - SDRT_6, - SDRT_7, -}; - -#define RESPONSE_INTERRUPT 0x01 -#define RESPONSE_ERROR 0x02 -#define RESPONSE_STATUS 0x03 -#define RESPONSE_IRQ_DISABLED 0x05 -#define RESPONSE_IRQ_ENABLED 0x06 -#define RESPONSE_PIGGYBACKED 0x07 -#define RESPONSE_NO_INTERRUPT 0x08 -#define RESPONSE_PIG_DISABLED 0x09 -#define RESPONSE_PIG_ENABLED 0x0A -#define SD_ERROR_1BIT_TIMEOUT 0x01 -#define SD_ERROR_4BIT_TIMEOUT 0x02 -#define SD_ERROR_1BIT_CRC_WRONG 0x03 -#define SD_ERROR_4BIT_CRC_WRONG 0x04 -#define SD_ERROR_1BIT_CRC_ERROR 0x05 -#define SD_ERROR_4BIT_CRC_ERROR 0x06 -#define SD_ERROR_NO_CMD_ENDBIT 0x07 -#define SD_ERROR_NO_1BIT_DATEND 0x08 -#define SD_ERROR_NO_4BIT_DATEND 0x09 -#define SD_ERROR_1BIT_UNEXPECTED_TIMEOUT 0x0A -#define SD_ERROR_4BIT_UNEXPECTED_TIMEOUT 0x0B -#define SD_ERROR_ILLEGAL_COMMAND 0x0C -#define SD_ERROR_NO_DEVICE 0x0D -#define SD_ERROR_TRANSFER_LENGTH 0x0E -#define SD_ERROR_1BIT_DATA_TIMEOUT 0x0F -#define SD_ERROR_4BIT_DATA_TIMEOUT 0x10 -#define SD_ERROR_ILLEGAL_STATE 0x11 -#define SD_ERROR_UNKNOWN_ERROR 0x12 -#define SD_ERROR_RESERVED_ERROR 0x13 -#define SD_ERROR_INVALID_FUNCTION 0x14 -#define SD_ERROR_OUT_OF_RANGE 0x15 -#define SD_ERROR_STAT_CMD 0x16 -#define SD_ERROR_STAT_DATA 0x17 -#define SD_ERROR_STAT_CMD_TIMEOUT 0x18 -#define SD_ERROR_SDCRDY_STUCK 0x19 -#define SD_ERROR_UNHANDLED 0x1A -#define SD_ERROR_OVERRUN 0x1B -#define SD_ERROR_PIO_TIMEOUT 0x1C - -#define FUN(c) (0x000007 & (c->arg>>28)) -#define REG(c) (0x01FFFF & (c->arg>>9)) - -static bool limit_speed_to_24_MHz; -module_param(limit_speed_to_24_MHz, bool, 0644); -MODULE_PARM_DESC(limit_speed_to_24_MHz, "Limit Max SDIO Clock Speed to 24 MHz"); - -static bool pad_input_to_usb_pkt; -module_param(pad_input_to_usb_pkt, bool, 0644); -MODULE_PARM_DESC(pad_input_to_usb_pkt, - "Pad USB data input transfers to whole USB Packet"); - -static bool disable_offload_processing; -module_param(disable_offload_processing, bool, 0644); -MODULE_PARM_DESC(disable_offload_processing, "Disable Offload Processing"); - -static bool force_1_bit_data_xfers; -module_param(force_1_bit_data_xfers, bool, 0644); -MODULE_PARM_DESC(force_1_bit_data_xfers, - "Force SDIO Data Transfers to 1-bit Mode"); - -static bool force_polling_for_irqs; -module_param(force_polling_for_irqs, bool, 0644); -MODULE_PARM_DESC(force_polling_for_irqs, "Force Polling for SDIO interrupts"); - -static int firmware_irqpoll_timeout = 1024; -module_param(firmware_irqpoll_timeout, int, 0644); -MODULE_PARM_DESC(firmware_irqpoll_timeout, "VUB300 firmware irqpoll timeout"); - -static int force_max_req_size = 128; -module_param(force_max_req_size, int, 0644); -MODULE_PARM_DESC(force_max_req_size, "set max request size in kBytes"); - -#ifdef SMSC_DEVELOPMENT_BOARD -static int firmware_rom_wait_states = 0x04; -#else -static int firmware_rom_wait_states = 0x1C; -#endif - -module_param(firmware_rom_wait_states, int, 0644); -MODULE_PARM_DESC(firmware_rom_wait_states, - "ROM wait states byte=RRRIIEEE (Reserved Internal External)"); - -#define ELAN_VENDOR_ID 0x2201 -#define VUB300_VENDOR_ID 0x0424 -#define VUB300_PRODUCT_ID 0x012C -static const struct usb_device_id vub300_table[] = { - {USB_DEVICE(ELAN_VENDOR_ID, VUB300_PRODUCT_ID)}, - {USB_DEVICE(VUB300_VENDOR_ID, VUB300_PRODUCT_ID)}, - {} /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, vub300_table); - -static struct workqueue_struct *cmndworkqueue; -static struct workqueue_struct *pollworkqueue; -static struct workqueue_struct *deadworkqueue; - -static inline int interface_to_InterfaceNumber(struct usb_interface *interface) -{ - if (!interface) - return -1; - if (!interface->cur_altsetting) - return -1; - return interface->cur_altsetting->desc.bInterfaceNumber; -} - -struct sdio_register { - unsigned func_num:3; - unsigned sdio_reg:17; - unsigned activate:1; - unsigned prepared:1; - unsigned regvalue:8; - unsigned response:8; - unsigned sparebit:26; -}; - -struct vub300_mmc_host { - struct usb_device *udev; - struct usb_interface *interface; - struct kref kref; - struct mutex cmd_mutex; - struct mutex irq_mutex; - char vub_name[3 + (9 * 8) + 4 + 1]; /* max of 7 sdio fn's */ - u8 cmnd_out_ep; /* EndPoint for commands */ - u8 cmnd_res_ep; /* EndPoint for responses */ - u8 data_out_ep; /* EndPoint for out data */ - u8 data_inp_ep; /* EndPoint for inp data */ - bool card_powered; - bool card_present; - bool read_only; - bool large_usb_packets; - bool app_spec; /* ApplicationSpecific */ - bool irq_enabled; /* by the MMC CORE */ - bool irq_disabled; /* in the firmware */ - unsigned bus_width:4; - u8 total_offload_count; - u8 dynamic_register_count; - u8 resp_len; - u32 datasize; - int errors; - int usb_transport_fail; - int usb_timed_out; - int irqs_queued; - struct sdio_register sdio_register[16]; - struct offload_interrupt_function_register { -#define MAXREGBITS 4 -#define MAXREGS (1<<MAXREGBITS) -#define MAXREGMASK (MAXREGS-1) - u8 offload_count; - u32 offload_point; - struct offload_registers_access reg[MAXREGS]; - } fn[8]; - u16 fbs[8]; /* Function Block Size */ - struct mmc_command *cmd; - struct mmc_request *req; - struct mmc_data *data; - struct mmc_host *mmc; - struct urb *urb; - struct urb *command_out_urb; - struct urb *command_res_urb; - struct completion command_complete; - struct completion irqpoll_complete; - union sd_command cmnd; - union sd_response resp; - struct timer_list sg_transfer_timer; - struct usb_sg_request sg_request; - struct timer_list inactivity_timer; - struct work_struct deadwork; - struct work_struct cmndwork; - struct delayed_work pollwork; - struct host_controller_info hc_info; - struct sd_status_header system_port_status; - u8 padded_buffer[64]; -}; - -#define kref_to_vub300_mmc_host(d) container_of(d, struct vub300_mmc_host, kref) -#define SET_TRANSFER_PSEUDOCODE 21 -#define SET_INTERRUPT_PSEUDOCODE 20 -#define SET_FAILURE_MODE 18 -#define SET_ROM_WAIT_STATES 16 -#define SET_IRQ_ENABLE 13 -#define SET_CLOCK_SPEED 11 -#define SET_FUNCTION_BLOCK_SIZE 9 -#define SET_SD_DATA_MODE 6 -#define SET_SD_POWER 4 -#define ENTER_DFU_MODE 3 -#define GET_HC_INF0 1 -#define GET_SYSTEM_PORT_STATUS 0 - -static void vub300_delete(struct kref *kref) -{ /* kref callback - softirq */ - struct vub300_mmc_host *vub300 = kref_to_vub300_mmc_host(kref); - struct mmc_host *mmc = vub300->mmc; - - usb_free_urb(vub300->command_out_urb); - vub300->command_out_urb = NULL; - usb_free_urb(vub300->command_res_urb); - vub300->command_res_urb = NULL; - usb_put_dev(vub300->udev); - mmc_free_host(mmc); - /* - * and hence also frees vub300 - * which is contained at the end of struct mmc - */ -} - -static void vub300_queue_cmnd_work(struct vub300_mmc_host *vub300) -{ - kref_get(&vub300->kref); - if (queue_work(cmndworkqueue, &vub300->cmndwork)) { - /* - * then the cmndworkqueue was not previously - * running and the above get ref is obvious - * required and will be put when the thread - * terminates by a specific call - */ - } else { - /* - * the cmndworkqueue was already running from - * a previous invocation and thus to keep the - * kref counts correct we must undo the get - */ - kref_put(&vub300->kref, vub300_delete); - } -} - -static void vub300_queue_poll_work(struct vub300_mmc_host *vub300, int delay) -{ - kref_get(&vub300->kref); - if (queue_delayed_work(pollworkqueue, &vub300->pollwork, delay)) { - /* - * then the pollworkqueue was not previously - * running and the above get ref is obvious - * required and will be put when the thread - * terminates by a specific call - */ - } else { - /* - * the pollworkqueue was already running from - * a previous invocation and thus to keep the - * kref counts correct we must undo the get - */ - kref_put(&vub300->kref, vub300_delete); - } -} - -static void vub300_queue_dead_work(struct vub300_mmc_host *vub300) -{ - kref_get(&vub300->kref); - if (queue_work(deadworkqueue, &vub300->deadwork)) { - /* - * then the deadworkqueue was not previously - * running and the above get ref is obvious - * required and will be put when the thread - * terminates by a specific call - */ - } else { - /* - * the deadworkqueue was already running from - * a previous invocation and thus to keep the - * kref counts correct we must undo the get - */ - kref_put(&vub300->kref, vub300_delete); - } -} - -static void irqpoll_res_completed(struct urb *urb) -{ /* urb completion handler - hardirq */ - struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; - if (urb->status) - vub300->usb_transport_fail = urb->status; - complete(&vub300->irqpoll_complete); -} - -static void irqpoll_out_completed(struct urb *urb) -{ /* urb completion handler - hardirq */ - struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; - if (urb->status) { - vub300->usb_transport_fail = urb->status; - complete(&vub300->irqpoll_complete); - return; - } else { - int ret; - unsigned int pipe = - usb_rcvbulkpipe(vub300->udev, vub300->cmnd_res_ep); - usb_fill_bulk_urb(vub300->command_res_urb, vub300->udev, pipe, - &vub300->resp, sizeof(vub300->resp), - irqpoll_res_completed, vub300); - vub300->command_res_urb->actual_length = 0; - ret = usb_submit_urb(vub300->command_res_urb, GFP_ATOMIC); - if (ret) { - vub300->usb_transport_fail = ret; - complete(&vub300->irqpoll_complete); - } - return; - } -} - -static void send_irqpoll(struct vub300_mmc_host *vub300) -{ - /* cmd_mutex is held by vub300_pollwork_thread */ - int retval; - int timeout = 0xFFFF & (0x0001FFFF - firmware_irqpoll_timeout); - vub300->cmnd.poll.header_size = 22; - vub300->cmnd.poll.header_type = 1; - vub300->cmnd.poll.port_number = 0; - vub300->cmnd.poll.command_type = 2; - vub300->cmnd.poll.poll_timeout_lsb = 0xFF & (unsigned)timeout; - vub300->cmnd.poll.poll_timeout_msb = 0xFF & (unsigned)(timeout >> 8); - usb_fill_bulk_urb(vub300->command_out_urb, vub300->udev, - usb_sndbulkpipe(vub300->udev, vub300->cmnd_out_ep) - , &vub300->cmnd, sizeof(vub300->cmnd) - , irqpoll_out_completed, vub300); - retval = usb_submit_urb(vub300->command_out_urb, GFP_KERNEL); - if (0 > retval) { - vub300->usb_transport_fail = retval; - vub300_queue_poll_work(vub300, 1); - complete(&vub300->irqpoll_complete); - return; - } else { - return; - } -} - -static void new_system_port_status(struct vub300_mmc_host *vub300) -{ - int old_card_present = vub300->card_present; - int new_card_present = - (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; - vub300->read_only = - (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; - if (new_card_present && !old_card_present) { - dev_info(&vub300->udev->dev, "card just inserted\n"); - vub300->card_present = 1; - vub300->bus_width = 0; - if (disable_offload_processing) - strscpy(vub300->vub_name, "EMPTY Processing Disabled", - sizeof(vub300->vub_name)); - else - vub300->vub_name[0] = 0; - mmc_detect_change(vub300->mmc, 1); - } else if (!new_card_present && old_card_present) { - dev_info(&vub300->udev->dev, "card just ejected\n"); - vub300->card_present = 0; - mmc_detect_change(vub300->mmc, 0); - } else { - /* no change */ - } -} - -static void __add_offloaded_reg_to_fifo(struct vub300_mmc_host *vub300, - struct offload_registers_access - *register_access, u8 func) -{ - u8 r = vub300->fn[func].offload_point + vub300->fn[func].offload_count; - memcpy(&vub300->fn[func].reg[MAXREGMASK & r], register_access, - sizeof(struct offload_registers_access)); - vub300->fn[func].offload_count += 1; - vub300->total_offload_count += 1; -} - -static void add_offloaded_reg(struct vub300_mmc_host *vub300, - struct offload_registers_access *register_access) -{ - u32 Register = ((0x03 & register_access->command_byte[0]) << 15) - | ((0xFF & register_access->command_byte[1]) << 7) - | ((0xFE & register_access->command_byte[2]) >> 1); - u8 func = ((0x70 & register_access->command_byte[0]) >> 4); - u8 regs = vub300->dynamic_register_count; - u8 i = 0; - while (0 < regs-- && 1 == vub300->sdio_register[i].activate) { - if (vub300->sdio_register[i].func_num == func && - vub300->sdio_register[i].sdio_reg == Register) { - if (vub300->sdio_register[i].prepared == 0) - vub300->sdio_register[i].prepared = 1; - vub300->sdio_register[i].response = - register_access->Respond_Byte[2]; - vub300->sdio_register[i].regvalue = - register_access->Respond_Byte[3]; - return; - } else { - i += 1; - continue; - } - } - __add_offloaded_reg_to_fifo(vub300, register_access, func); -} - -static void check_vub300_port_status(struct vub300_mmc_host *vub300) -{ - /* - * cmd_mutex is held by vub300_pollwork_thread, - * vub300_deadwork_thread or vub300_cmndwork_thread - */ - int retval; - retval = - usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), - GET_SYSTEM_PORT_STATUS, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, 0x0000, &vub300->system_port_status, - sizeof(vub300->system_port_status), 1000); - if (sizeof(vub300->system_port_status) == retval) - new_system_port_status(vub300); -} - -static void __vub300_irqpoll_response(struct vub300_mmc_host *vub300) -{ - /* cmd_mutex is held by vub300_pollwork_thread */ - if (vub300->command_res_urb->actual_length == 0) - return; - - switch (vub300->resp.common.header_type) { - case RESPONSE_INTERRUPT: - mutex_lock(&vub300->irq_mutex); - if (vub300->irq_enabled) - mmc_signal_sdio_irq(vub300->mmc); - else - vub300->irqs_queued += 1; - vub300->irq_disabled = 1; - mutex_unlock(&vub300->irq_mutex); - break; - case RESPONSE_ERROR: - if (vub300->resp.error.error_code == SD_ERROR_NO_DEVICE) - check_vub300_port_status(vub300); - break; - case RESPONSE_STATUS: - vub300->system_port_status = vub300->resp.status; - new_system_port_status(vub300); - if (!vub300->card_present) - vub300_queue_poll_work(vub300, HZ / 5); - break; - case RESPONSE_IRQ_DISABLED: - { - int offloaded_data_length = vub300->resp.common.header_size - 3; - int register_count = offloaded_data_length >> 3; - int ri = 0; - while (register_count--) { - add_offloaded_reg(vub300, &vub300->resp.irq.reg[ri]); - ri += 1; - } - mutex_lock(&vub300->irq_mutex); - if (vub300->irq_enabled) - mmc_signal_sdio_irq(vub300->mmc); - else - vub300->irqs_queued += 1; - vub300->irq_disabled = 1; - mutex_unlock(&vub300->irq_mutex); - break; - } - case RESPONSE_IRQ_ENABLED: - { - int offloaded_data_length = vub300->resp.common.header_size - 3; - int register_count = offloaded_data_length >> 3; - int ri = 0; - while (register_count--) { - add_offloaded_reg(vub300, &vub300->resp.irq.reg[ri]); - ri += 1; - } - mutex_lock(&vub300->irq_mutex); - if (vub300->irq_enabled) - mmc_signal_sdio_irq(vub300->mmc); - else - vub300->irqs_queued += 1; - vub300->irq_disabled = 0; - mutex_unlock(&vub300->irq_mutex); - break; - } - case RESPONSE_NO_INTERRUPT: - vub300_queue_poll_work(vub300, 1); - break; - default: - break; - } -} - -static void __do_poll(struct vub300_mmc_host *vub300) -{ - /* cmd_mutex is held by vub300_pollwork_thread */ - unsigned long commretval; - mod_timer(&vub300->inactivity_timer, jiffies + HZ); - init_completion(&vub300->irqpoll_complete); - send_irqpoll(vub300); - commretval = wait_for_completion_timeout(&vub300->irqpoll_complete, - msecs_to_jiffies(500)); - if (vub300->usb_transport_fail) { - /* no need to do anything */ - } else if (commretval == 0) { - vub300->usb_timed_out = 1; - usb_kill_urb(vub300->command_out_urb); - usb_kill_urb(vub300->command_res_urb); - } else { /* commretval > 0 */ - __vub300_irqpoll_response(vub300); - } -} - -/* this thread runs only when the driver - * is trying to poll the device for an IRQ - */ -static void vub300_pollwork_thread(struct work_struct *work) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = container_of(work, - struct vub300_mmc_host, pollwork.work); - if (!vub300->interface) { - kref_put(&vub300->kref, vub300_delete); - return; - } - mutex_lock(&vub300->cmd_mutex); - if (vub300->cmd) { - vub300_queue_poll_work(vub300, 1); - } else if (!vub300->card_present) { - /* no need to do anything */ - } else { /* vub300->card_present */ - mutex_lock(&vub300->irq_mutex); - if (!vub300->irq_enabled) { - mutex_unlock(&vub300->irq_mutex); - } else if (vub300->irqs_queued) { - vub300->irqs_queued -= 1; - mmc_signal_sdio_irq(vub300->mmc); - mod_timer(&vub300->inactivity_timer, jiffies + HZ); - mutex_unlock(&vub300->irq_mutex); - } else { /* NOT vub300->irqs_queued */ - mutex_unlock(&vub300->irq_mutex); - __do_poll(vub300); - } - } - mutex_unlock(&vub300->cmd_mutex); - kref_put(&vub300->kref, vub300_delete); -} - -static void vub300_deadwork_thread(struct work_struct *work) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = - container_of(work, struct vub300_mmc_host, deadwork); - if (!vub300->interface) { - kref_put(&vub300->kref, vub300_delete); - return; - } - mutex_lock(&vub300->cmd_mutex); - if (vub300->cmd) { - /* - * a command got in as the inactivity - * timer expired - so we just let the - * processing of the command show if - * the device is dead - */ - } else if (vub300->card_present) { - check_vub300_port_status(vub300); - } else if (vub300->mmc && vub300->mmc->card) { - /* - * the MMC core must not have responded - * to the previous indication - lets - * hope that it eventually does so we - * will just ignore this for now - */ - } else { - check_vub300_port_status(vub300); - } - mod_timer(&vub300->inactivity_timer, jiffies + HZ); - mutex_unlock(&vub300->cmd_mutex); - kref_put(&vub300->kref, vub300_delete); -} - -static void vub300_inactivity_timer_expired(struct timer_list *t) -{ /* softirq */ - struct vub300_mmc_host *vub300 = timer_container_of(vub300, t, - inactivity_timer); - if (!vub300->interface) { - kref_put(&vub300->kref, vub300_delete); - } else if (vub300->cmd) { - mod_timer(&vub300->inactivity_timer, jiffies + HZ); - } else { - vub300_queue_dead_work(vub300); - mod_timer(&vub300->inactivity_timer, jiffies + HZ); - } -} - -static int vub300_response_error(u8 error_code) -{ - switch (error_code) { - case SD_ERROR_PIO_TIMEOUT: - case SD_ERROR_1BIT_TIMEOUT: - case SD_ERROR_4BIT_TIMEOUT: - return -ETIMEDOUT; - case SD_ERROR_STAT_DATA: - case SD_ERROR_OVERRUN: - case SD_ERROR_STAT_CMD: - case SD_ERROR_STAT_CMD_TIMEOUT: - case SD_ERROR_SDCRDY_STUCK: - case SD_ERROR_UNHANDLED: - case SD_ERROR_1BIT_CRC_WRONG: - case SD_ERROR_4BIT_CRC_WRONG: - case SD_ERROR_1BIT_CRC_ERROR: - case SD_ERROR_4BIT_CRC_ERROR: - case SD_ERROR_NO_CMD_ENDBIT: - case SD_ERROR_NO_1BIT_DATEND: - case SD_ERROR_NO_4BIT_DATEND: - case SD_ERROR_1BIT_DATA_TIMEOUT: - case SD_ERROR_4BIT_DATA_TIMEOUT: - case SD_ERROR_1BIT_UNEXPECTED_TIMEOUT: - case SD_ERROR_4BIT_UNEXPECTED_TIMEOUT: - return -EILSEQ; - case 33: - return -EILSEQ; - case SD_ERROR_ILLEGAL_COMMAND: - return -EINVAL; - case SD_ERROR_NO_DEVICE: - return -ENOMEDIUM; - default: - return -ENODEV; - } -} - -static void command_res_completed(struct urb *urb) -{ /* urb completion handler - hardirq */ - struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; - if (urb->status) { - /* we have to let the initiator handle the error */ - } else if (vub300->command_res_urb->actual_length == 0) { - /* - * we have seen this happen once or twice and - * we suspect a buggy USB host controller - */ - } else if (!vub300->data) { - /* this means that the command (typically CMD52) succeeded */ - } else if (vub300->resp.common.header_type != 0x02) { - /* - * this is an error response from the VUB300 chip - * and we let the initiator handle it - */ - } else if (vub300->urb) { - vub300->cmd->error = - vub300_response_error(vub300->resp.error.error_code); - usb_unlink_urb(vub300->urb); - } else { - vub300->cmd->error = - vub300_response_error(vub300->resp.error.error_code); - usb_sg_cancel(&vub300->sg_request); - } - complete(&vub300->command_complete); /* got_response_in */ -} - -static void command_out_completed(struct urb *urb) -{ /* urb completion handler - hardirq */ - struct vub300_mmc_host *vub300 = (struct vub300_mmc_host *)urb->context; - if (urb->status) { - complete(&vub300->command_complete); - } else { - int ret; - unsigned int pipe = - usb_rcvbulkpipe(vub300->udev, vub300->cmnd_res_ep); - usb_fill_bulk_urb(vub300->command_res_urb, vub300->udev, pipe, - &vub300->resp, sizeof(vub300->resp), - command_res_completed, vub300); - vub300->command_res_urb->actual_length = 0; - ret = usb_submit_urb(vub300->command_res_urb, GFP_ATOMIC); - if (ret == 0) { - /* - * the urb completion handler will call - * our completion handler - */ - } else { - /* - * and thus we only call it directly - * when it will not be called - */ - complete(&vub300->command_complete); - } - } -} - -/* - * the STUFF bits are masked out for the comparisons - */ -static void snoop_block_size_and_bus_width(struct vub300_mmc_host *vub300, - u32 cmd_arg) -{ - if ((0xFBFFFE00 & cmd_arg) == 0x80022200) - vub300->fbs[1] = (cmd_arg << 8) | (0x00FF & vub300->fbs[1]); - else if ((0xFBFFFE00 & cmd_arg) == 0x80022000) - vub300->fbs[1] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[1]); - else if ((0xFBFFFE00 & cmd_arg) == 0x80042200) - vub300->fbs[2] = (cmd_arg << 8) | (0x00FF & vub300->fbs[2]); - else if ((0xFBFFFE00 & cmd_arg) == 0x80042000) - vub300->fbs[2] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[2]); - else if ((0xFBFFFE00 & cmd_arg) == 0x80062200) - vub300->fbs[3] = (cmd_arg << 8) | (0x00FF & vub300->fbs[3]); - else if ((0xFBFFFE00 & cmd_arg) == 0x80062000) - vub300->fbs[3] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[3]); - else if ((0xFBFFFE00 & cmd_arg) == 0x80082200) - vub300->fbs[4] = (cmd_arg << 8) | (0x00FF & vub300->fbs[4]); - else if ((0xFBFFFE00 & cmd_arg) == 0x80082000) - vub300->fbs[4] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[4]); - else if ((0xFBFFFE00 & cmd_arg) == 0x800A2200) - vub300->fbs[5] = (cmd_arg << 8) | (0x00FF & vub300->fbs[5]); - else if ((0xFBFFFE00 & cmd_arg) == 0x800A2000) - vub300->fbs[5] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[5]); - else if ((0xFBFFFE00 & cmd_arg) == 0x800C2200) - vub300->fbs[6] = (cmd_arg << 8) | (0x00FF & vub300->fbs[6]); - else if ((0xFBFFFE00 & cmd_arg) == 0x800C2000) - vub300->fbs[6] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[6]); - else if ((0xFBFFFE00 & cmd_arg) == 0x800E2200) - vub300->fbs[7] = (cmd_arg << 8) | (0x00FF & vub300->fbs[7]); - else if ((0xFBFFFE00 & cmd_arg) == 0x800E2000) - vub300->fbs[7] = (0xFF & cmd_arg) | (0xFF00 & vub300->fbs[7]); - else if ((0xFBFFFE03 & cmd_arg) == 0x80000E00) - vub300->bus_width = 1; - else if ((0xFBFFFE03 & cmd_arg) == 0x80000E02) - vub300->bus_width = 4; -} - -static void send_command(struct vub300_mmc_host *vub300) -{ - /* cmd_mutex is held by vub300_cmndwork_thread */ - struct mmc_command *cmd = vub300->cmd; - struct mmc_data *data = vub300->data; - int retval; - int i; - u8 response_type; - if (vub300->app_spec) { - switch (cmd->opcode) { - case 6: - response_type = SDRT_1; - vub300->resp_len = 6; - if (0x00000000 == (0x00000003 & cmd->arg)) - vub300->bus_width = 1; - else if (0x00000002 == (0x00000003 & cmd->arg)) - vub300->bus_width = 4; - else - dev_err(&vub300->udev->dev, - "unexpected ACMD6 bus_width=%d\n", - 0x00000003 & cmd->arg); - break; - case 13: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 22: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 23: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 41: - response_type = SDRT_3; - vub300->resp_len = 6; - break; - case 42: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 51: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 55: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - default: - vub300->resp_len = 0; - cmd->error = -EINVAL; - complete(&vub300->command_complete); - return; - } - vub300->app_spec = 0; - } else { - switch (cmd->opcode) { - case 0: - response_type = SDRT_NONE; - vub300->resp_len = 0; - break; - case 1: - response_type = SDRT_3; - vub300->resp_len = 6; - break; - case 2: - response_type = SDRT_2; - vub300->resp_len = 17; - break; - case 3: - response_type = SDRT_6; - vub300->resp_len = 6; - break; - case 4: - response_type = SDRT_NONE; - vub300->resp_len = 0; - break; - case 5: - response_type = SDRT_4; - vub300->resp_len = 6; - break; - case 6: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 7: - response_type = SDRT_1B; - vub300->resp_len = 6; - break; - case 8: - response_type = SDRT_7; - vub300->resp_len = 6; - break; - case 9: - response_type = SDRT_2; - vub300->resp_len = 17; - break; - case 10: - response_type = SDRT_2; - vub300->resp_len = 17; - break; - case 12: - response_type = SDRT_1B; - vub300->resp_len = 6; - break; - case 13: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 15: - response_type = SDRT_NONE; - vub300->resp_len = 0; - break; - case 16: - for (i = 0; i < ARRAY_SIZE(vub300->fbs); i++) - vub300->fbs[i] = 0xFFFF & cmd->arg; - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 17: - case 18: - case 24: - case 25: - case 27: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 28: - case 29: - response_type = SDRT_1B; - vub300->resp_len = 6; - break; - case 30: - case 32: - case 33: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 38: - response_type = SDRT_1B; - vub300->resp_len = 6; - break; - case 42: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - case 52: - response_type = SDRT_5; - vub300->resp_len = 6; - snoop_block_size_and_bus_width(vub300, cmd->arg); - break; - case 53: - response_type = SDRT_5; - vub300->resp_len = 6; - break; - case 55: - response_type = SDRT_1; - vub300->resp_len = 6; - vub300->app_spec = 1; - break; - case 56: - response_type = SDRT_1; - vub300->resp_len = 6; - break; - default: - vub300->resp_len = 0; - cmd->error = -EINVAL; - complete(&vub300->command_complete); - return; - } - } - /* - * it is a shame that we can not use "sizeof(struct sd_command_header)" - * this is because the packet _must_ be padded to 64 bytes - */ - vub300->cmnd.head.header_size = 20; - vub300->cmnd.head.header_type = 0x00; - vub300->cmnd.head.port_number = 0; /* "0" means port 1 */ - vub300->cmnd.head.command_type = 0x00; /* standard read command */ - vub300->cmnd.head.response_type = response_type; - vub300->cmnd.head.command_index = cmd->opcode; - vub300->cmnd.head.arguments[0] = cmd->arg >> 24; - vub300->cmnd.head.arguments[1] = cmd->arg >> 16; - vub300->cmnd.head.arguments[2] = cmd->arg >> 8; - vub300->cmnd.head.arguments[3] = cmd->arg >> 0; - if (cmd->opcode == 52) { - int fn = 0x7 & (cmd->arg >> 28); - vub300->cmnd.head.block_count[0] = 0; - vub300->cmnd.head.block_count[1] = 0; - vub300->cmnd.head.block_size[0] = (vub300->fbs[fn] >> 8) & 0xFF; - vub300->cmnd.head.block_size[1] = (vub300->fbs[fn] >> 0) & 0xFF; - vub300->cmnd.head.command_type = 0x00; - vub300->cmnd.head.transfer_size[0] = 0; - vub300->cmnd.head.transfer_size[1] = 0; - vub300->cmnd.head.transfer_size[2] = 0; - vub300->cmnd.head.transfer_size[3] = 0; - } else if (!data) { - vub300->cmnd.head.block_count[0] = 0; - vub300->cmnd.head.block_count[1] = 0; - vub300->cmnd.head.block_size[0] = (vub300->fbs[0] >> 8) & 0xFF; - vub300->cmnd.head.block_size[1] = (vub300->fbs[0] >> 0) & 0xFF; - vub300->cmnd.head.command_type = 0x00; - vub300->cmnd.head.transfer_size[0] = 0; - vub300->cmnd.head.transfer_size[1] = 0; - vub300->cmnd.head.transfer_size[2] = 0; - vub300->cmnd.head.transfer_size[3] = 0; - } else if (cmd->opcode == 53) { - int fn = 0x7 & (cmd->arg >> 28); - if (0x08 & vub300->cmnd.head.arguments[0]) { /* BLOCK MODE */ - vub300->cmnd.head.block_count[0] = - (data->blocks >> 8) & 0xFF; - vub300->cmnd.head.block_count[1] = - (data->blocks >> 0) & 0xFF; - vub300->cmnd.head.block_size[0] = - (data->blksz >> 8) & 0xFF; - vub300->cmnd.head.block_size[1] = - (data->blksz >> 0) & 0xFF; - } else { /* BYTE MODE */ - vub300->cmnd.head.block_count[0] = 0; - vub300->cmnd.head.block_count[1] = 0; - vub300->cmnd.head.block_size[0] = - (vub300->datasize >> 8) & 0xFF; - vub300->cmnd.head.block_size[1] = - (vub300->datasize >> 0) & 0xFF; - } - vub300->cmnd.head.command_type = - (MMC_DATA_READ & data->flags) ? 0x00 : 0x80; - vub300->cmnd.head.transfer_size[0] = - (vub300->datasize >> 24) & 0xFF; - vub300->cmnd.head.transfer_size[1] = - (vub300->datasize >> 16) & 0xFF; - vub300->cmnd.head.transfer_size[2] = - (vub300->datasize >> 8) & 0xFF; - vub300->cmnd.head.transfer_size[3] = - (vub300->datasize >> 0) & 0xFF; - if (vub300->datasize < vub300->fbs[fn]) { - vub300->cmnd.head.block_count[0] = 0; - vub300->cmnd.head.block_count[1] = 0; - } - } else { - vub300->cmnd.head.block_count[0] = (data->blocks >> 8) & 0xFF; - vub300->cmnd.head.block_count[1] = (data->blocks >> 0) & 0xFF; - vub300->cmnd.head.block_size[0] = (data->blksz >> 8) & 0xFF; - vub300->cmnd.head.block_size[1] = (data->blksz >> 0) & 0xFF; - vub300->cmnd.head.command_type = - (MMC_DATA_READ & data->flags) ? 0x00 : 0x80; - vub300->cmnd.head.transfer_size[0] = - (vub300->datasize >> 24) & 0xFF; - vub300->cmnd.head.transfer_size[1] = - (vub300->datasize >> 16) & 0xFF; - vub300->cmnd.head.transfer_size[2] = - (vub300->datasize >> 8) & 0xFF; - vub300->cmnd.head.transfer_size[3] = - (vub300->datasize >> 0) & 0xFF; - if (vub300->datasize < vub300->fbs[0]) { - vub300->cmnd.head.block_count[0] = 0; - vub300->cmnd.head.block_count[1] = 0; - } - } - if (vub300->cmnd.head.block_size[0] || vub300->cmnd.head.block_size[1]) { - u16 block_size = vub300->cmnd.head.block_size[1] | - (vub300->cmnd.head.block_size[0] << 8); - u16 block_boundary = FIRMWARE_BLOCK_BOUNDARY - - (FIRMWARE_BLOCK_BOUNDARY % block_size); - vub300->cmnd.head.block_boundary[0] = - (block_boundary >> 8) & 0xFF; - vub300->cmnd.head.block_boundary[1] = - (block_boundary >> 0) & 0xFF; - } else { - vub300->cmnd.head.block_boundary[0] = 0; - vub300->cmnd.head.block_boundary[1] = 0; - } - usb_fill_bulk_urb(vub300->command_out_urb, vub300->udev, - usb_sndbulkpipe(vub300->udev, vub300->cmnd_out_ep), - &vub300->cmnd, sizeof(vub300->cmnd), - command_out_completed, vub300); - retval = usb_submit_urb(vub300->command_out_urb, GFP_KERNEL); - if (retval < 0) { - cmd->error = retval; - complete(&vub300->command_complete); - return; - } else { - return; - } -} - -/* - * timer callback runs in atomic mode - * so it cannot call usb_kill_urb() - */ -static void vub300_sg_timed_out(struct timer_list *t) -{ - struct vub300_mmc_host *vub300 = timer_container_of(vub300, t, - sg_transfer_timer); - vub300->usb_timed_out = 1; - usb_sg_cancel(&vub300->sg_request); - usb_unlink_urb(vub300->command_out_urb); - usb_unlink_urb(vub300->command_res_urb); -} - -static u16 roundup_to_multiple_of_64(u16 number) -{ - return 0xFFC0 & (0x3F + number); -} - -/* - * this is a separate function to solve the 80 column width restriction - */ -static void __download_offload_pseudocode(struct vub300_mmc_host *vub300, - const struct firmware *fw) -{ - u8 register_count = 0; - u16 ts = 0; - u16 interrupt_size = 0; - const u8 *data = fw->data; - int size = fw->size; - u8 c; - dev_info(&vub300->udev->dev, "using %s for SDIO offload processing\n", - vub300->vub_name); - do { - c = *data++; - } while (size-- && c); /* skip comment */ - dev_info(&vub300->udev->dev, "using offload firmware %s %s\n", fw->data, - vub300->vub_name); - if (size < 4) { - dev_err(&vub300->udev->dev, - "corrupt offload pseudocode in firmware %s\n", - vub300->vub_name); - strscpy(vub300->vub_name, "corrupt offload pseudocode", - sizeof(vub300->vub_name)); - return; - } - interrupt_size += *data++; - size -= 1; - interrupt_size <<= 8; - interrupt_size += *data++; - size -= 1; - if (interrupt_size < size) { - u16 xfer_length = roundup_to_multiple_of_64(interrupt_size); - u8 *xfer_buffer = kmalloc(xfer_length, GFP_KERNEL); - if (xfer_buffer) { - int retval; - memcpy(xfer_buffer, data, interrupt_size); - memset(xfer_buffer + interrupt_size, 0, - xfer_length - interrupt_size); - size -= interrupt_size; - data += interrupt_size; - retval = - usb_control_msg(vub300->udev, - usb_sndctrlpipe(vub300->udev, 0), - SET_INTERRUPT_PSEUDOCODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, 0x0000, 0x0000, - xfer_buffer, xfer_length, 1000); - kfree(xfer_buffer); - if (retval < 0) - goto copy_error_message; - } else { - dev_err(&vub300->udev->dev, - "not enough memory for xfer buffer to send" - " INTERRUPT_PSEUDOCODE for %s %s\n", fw->data, - vub300->vub_name); - strscpy(vub300->vub_name, - "SDIO interrupt pseudocode download failed", - sizeof(vub300->vub_name)); - return; - } - } else { - dev_err(&vub300->udev->dev, - "corrupt interrupt pseudocode in firmware %s %s\n", - fw->data, vub300->vub_name); - strscpy(vub300->vub_name, "corrupt interrupt pseudocode", - sizeof(vub300->vub_name)); - return; - } - ts += *data++; - size -= 1; - ts <<= 8; - ts += *data++; - size -= 1; - if (ts < size) { - u16 xfer_length = roundup_to_multiple_of_64(ts); - u8 *xfer_buffer = kmalloc(xfer_length, GFP_KERNEL); - if (xfer_buffer) { - int retval; - memcpy(xfer_buffer, data, ts); - memset(xfer_buffer + ts, 0, - xfer_length - ts); - size -= ts; - data += ts; - retval = - usb_control_msg(vub300->udev, - usb_sndctrlpipe(vub300->udev, 0), - SET_TRANSFER_PSEUDOCODE, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, 0x0000, 0x0000, - xfer_buffer, xfer_length, 1000); - kfree(xfer_buffer); - if (retval < 0) - goto copy_error_message; - } else { - dev_err(&vub300->udev->dev, - "not enough memory for xfer buffer to send" - " TRANSFER_PSEUDOCODE for %s %s\n", fw->data, - vub300->vub_name); - strscpy(vub300->vub_name, - "SDIO transfer pseudocode download failed", - sizeof(vub300->vub_name)); - return; - } - } else { - dev_err(&vub300->udev->dev, - "corrupt transfer pseudocode in firmware %s %s\n", - fw->data, vub300->vub_name); - strscpy(vub300->vub_name, "corrupt transfer pseudocode", - sizeof(vub300->vub_name)); - return; - } - register_count += *data++; - size -= 1; - if (register_count * 4 == size) { - int I = vub300->dynamic_register_count = register_count; - int i = 0; - while (I--) { - unsigned int func_num = 0; - vub300->sdio_register[i].func_num = *data++; - size -= 1; - func_num += *data++; - size -= 1; - func_num <<= 8; - func_num += *data++; - size -= 1; - func_num <<= 8; - func_num += *data++; - size -= 1; - vub300->sdio_register[i].sdio_reg = func_num; - vub300->sdio_register[i].activate = 1; - vub300->sdio_register[i].prepared = 0; - i += 1; - } - dev_info(&vub300->udev->dev, - "initialized %d dynamic pseudocode registers\n", - vub300->dynamic_register_count); - return; - } else { - dev_err(&vub300->udev->dev, - "corrupt dynamic registers in firmware %s\n", - vub300->vub_name); - strscpy(vub300->vub_name, "corrupt dynamic registers", - sizeof(vub300->vub_name)); - return; - } - -copy_error_message: - strscpy(vub300->vub_name, "SDIO pseudocode download failed", - sizeof(vub300->vub_name)); -} - -/* - * if the binary containing the EMPTY PseudoCode can not be found - * vub300->vub_name is set anyway in order to prevent an automatic retry - */ -static void download_offload_pseudocode(struct vub300_mmc_host *vub300) -{ - struct mmc_card *card = vub300->mmc->card; - int sdio_funcs = card->sdio_funcs; - const struct firmware *fw = NULL; - int l = snprintf(vub300->vub_name, sizeof(vub300->vub_name), - "vub_%04X%04X", card->cis.vendor, card->cis.device); - int n = 0; - int retval; - for (n = 0; n < sdio_funcs; n++) { - struct sdio_func *sf = card->sdio_func[n]; - l += scnprintf(vub300->vub_name + l, - sizeof(vub300->vub_name) - l, "_%04X%04X", - sf->vendor, sf->device); - } - snprintf(vub300->vub_name + l, sizeof(vub300->vub_name) - l, ".bin"); - dev_info(&vub300->udev->dev, "requesting offload firmware %s\n", - vub300->vub_name); - retval = request_firmware(&fw, vub300->vub_name, &card->dev); - if (retval < 0) { - strscpy(vub300->vub_name, "vub_default.bin", - sizeof(vub300->vub_name)); - retval = request_firmware(&fw, vub300->vub_name, &card->dev); - if (retval < 0) { - strscpy(vub300->vub_name, - "no SDIO offload firmware found", - sizeof(vub300->vub_name)); - } else { - __download_offload_pseudocode(vub300, fw); - release_firmware(fw); - } - } else { - __download_offload_pseudocode(vub300, fw); - release_firmware(fw); - } -} - -static void vub300_usb_bulk_msg_completion(struct urb *urb) -{ /* urb completion handler - hardirq */ - complete((struct completion *)urb->context); -} - -static int vub300_usb_bulk_msg(struct vub300_mmc_host *vub300, - unsigned int pipe, void *data, int len, - int *actual_length, int timeout_msecs) -{ - /* cmd_mutex is held by vub300_cmndwork_thread */ - struct usb_device *usb_dev = vub300->udev; - struct completion done; - int retval; - vub300->urb = usb_alloc_urb(0, GFP_KERNEL); - if (!vub300->urb) - return -ENOMEM; - usb_fill_bulk_urb(vub300->urb, usb_dev, pipe, data, len, - vub300_usb_bulk_msg_completion, NULL); - init_completion(&done); - vub300->urb->context = &done; - vub300->urb->actual_length = 0; - retval = usb_submit_urb(vub300->urb, GFP_KERNEL); - if (unlikely(retval)) - goto out; - if (!wait_for_completion_timeout - (&done, msecs_to_jiffies(timeout_msecs))) { - retval = -ETIMEDOUT; - usb_kill_urb(vub300->urb); - } else { - retval = vub300->urb->status; - } -out: - *actual_length = vub300->urb->actual_length; - usb_free_urb(vub300->urb); - vub300->urb = NULL; - return retval; -} - -static int __command_read_data(struct vub300_mmc_host *vub300, - struct mmc_command *cmd, struct mmc_data *data) -{ - /* cmd_mutex is held by vub300_cmndwork_thread */ - int linear_length = vub300->datasize; - int padded_length = vub300->large_usb_packets ? - ((511 + linear_length) >> 9) << 9 : - ((63 + linear_length) >> 6) << 6; - if ((padded_length == linear_length) || !pad_input_to_usb_pkt) { - int result; - unsigned pipe; - pipe = usb_rcvbulkpipe(vub300->udev, vub300->data_inp_ep); - result = usb_sg_init(&vub300->sg_request, vub300->udev, - pipe, 0, data->sg, - data->sg_len, 0, GFP_KERNEL); - if (result < 0) { - usb_unlink_urb(vub300->command_out_urb); - usb_unlink_urb(vub300->command_res_urb); - cmd->error = result; - data->bytes_xfered = 0; - return 0; - } else { - vub300->sg_transfer_timer.expires = - jiffies + msecs_to_jiffies(2000 + - (linear_length / 16384)); - add_timer(&vub300->sg_transfer_timer); - usb_sg_wait(&vub300->sg_request); - timer_delete(&vub300->sg_transfer_timer); - if (vub300->sg_request.status < 0) { - cmd->error = vub300->sg_request.status; - data->bytes_xfered = 0; - return 0; - } else { - data->bytes_xfered = vub300->datasize; - return linear_length; - } - } - } else { - u8 *buf = kmalloc(padded_length, GFP_KERNEL); - if (buf) { - int result; - unsigned pipe = usb_rcvbulkpipe(vub300->udev, - vub300->data_inp_ep); - int actual_length = 0; - result = vub300_usb_bulk_msg(vub300, pipe, buf, - padded_length, &actual_length, - 2000 + (padded_length / 16384)); - if (result < 0) { - cmd->error = result; - data->bytes_xfered = 0; - kfree(buf); - return 0; - } else if (actual_length < linear_length) { - cmd->error = -EREMOTEIO; - data->bytes_xfered = 0; - kfree(buf); - return 0; - } else { - sg_copy_from_buffer(data->sg, data->sg_len, buf, - linear_length); - kfree(buf); - data->bytes_xfered = vub300->datasize; - return linear_length; - } - } else { - cmd->error = -ENOMEM; - data->bytes_xfered = 0; - return 0; - } - } -} - -static int __command_write_data(struct vub300_mmc_host *vub300, - struct mmc_command *cmd, struct mmc_data *data) -{ - /* cmd_mutex is held by vub300_cmndwork_thread */ - unsigned pipe = usb_sndbulkpipe(vub300->udev, vub300->data_out_ep); - int linear_length = vub300->datasize; - int modulo_64_length = linear_length & 0x003F; - int modulo_512_length = linear_length & 0x01FF; - if (linear_length < 64) { - int result; - int actual_length; - sg_copy_to_buffer(data->sg, data->sg_len, - vub300->padded_buffer, - sizeof(vub300->padded_buffer)); - memset(vub300->padded_buffer + linear_length, 0, - sizeof(vub300->padded_buffer) - linear_length); - result = vub300_usb_bulk_msg(vub300, pipe, vub300->padded_buffer, - sizeof(vub300->padded_buffer), - &actual_length, 2000 + - (sizeof(vub300->padded_buffer) / - 16384)); - if (result < 0) { - cmd->error = result; - data->bytes_xfered = 0; - } else { - data->bytes_xfered = vub300->datasize; - } - } else if ((!vub300->large_usb_packets && (0 < modulo_64_length)) || - (vub300->large_usb_packets && (64 > modulo_512_length)) - ) { /* don't you just love these work-rounds */ - int padded_length = ((63 + linear_length) >> 6) << 6; - u8 *buf = kmalloc(padded_length, GFP_KERNEL); - if (buf) { - int result; - int actual_length; - sg_copy_to_buffer(data->sg, data->sg_len, buf, - padded_length); - memset(buf + linear_length, 0, - padded_length - linear_length); - result = - vub300_usb_bulk_msg(vub300, pipe, buf, - padded_length, &actual_length, - 2000 + padded_length / 16384); - kfree(buf); - if (result < 0) { - cmd->error = result; - data->bytes_xfered = 0; - } else { - data->bytes_xfered = vub300->datasize; - } - } else { - cmd->error = -ENOMEM; - data->bytes_xfered = 0; - } - } else { /* no data padding required */ - int result; - unsigned char buf[64 * 4]; - sg_copy_to_buffer(data->sg, data->sg_len, buf, sizeof(buf)); - result = usb_sg_init(&vub300->sg_request, vub300->udev, - pipe, 0, data->sg, - data->sg_len, 0, GFP_KERNEL); - if (result < 0) { - usb_unlink_urb(vub300->command_out_urb); - usb_unlink_urb(vub300->command_res_urb); - cmd->error = result; - data->bytes_xfered = 0; - } else { - vub300->sg_transfer_timer.expires = - jiffies + msecs_to_jiffies(2000 + - linear_length / 16384); - add_timer(&vub300->sg_transfer_timer); - usb_sg_wait(&vub300->sg_request); - if (cmd->error) { - data->bytes_xfered = 0; - } else { - timer_delete(&vub300->sg_transfer_timer); - if (vub300->sg_request.status < 0) { - cmd->error = vub300->sg_request.status; - data->bytes_xfered = 0; - } else { - data->bytes_xfered = vub300->datasize; - } - } - } - } - return linear_length; -} - -static bool __vub300_command_response(struct vub300_mmc_host *vub300, - struct mmc_command *cmd, - struct mmc_data *data, int data_length) -{ - /* cmd_mutex is held by vub300_cmndwork_thread */ - long respretval; - int msec_timeout = 1000 + data_length / 4; - respretval = - wait_for_completion_timeout(&vub300->command_complete, - msecs_to_jiffies(msec_timeout)); - if (respretval == 0) { /* TIMED OUT */ - /* we don't know which of "out" and "res" if any failed */ - vub300->usb_timed_out = 1; - usb_kill_urb(vub300->command_out_urb); - usb_kill_urb(vub300->command_res_urb); - cmd->error = -ETIMEDOUT; - return true; - } else if (respretval < 0) { - /* we don't know which of "out" and "res" if any failed */ - usb_kill_urb(vub300->command_out_urb); - usb_kill_urb(vub300->command_res_urb); - cmd->error = respretval; - } else if (cmd->error) { - /* - * the error occurred sending the command - * or receiving the response - */ - } else if (vub300->command_out_urb->status) { - vub300->usb_transport_fail = vub300->command_out_urb->status; - cmd->error = -EPROTO == vub300->command_out_urb->status ? - -ESHUTDOWN : vub300->command_out_urb->status; - } else if (vub300->command_res_urb->status) { - vub300->usb_transport_fail = vub300->command_res_urb->status; - cmd->error = -EPROTO == vub300->command_res_urb->status ? - -ESHUTDOWN : vub300->command_res_urb->status; - } else if (vub300->resp.common.header_type == 0x00) { - /* - * the command completed successfully - * and there was no piggybacked data - */ - } else if (vub300->resp.common.header_type == RESPONSE_ERROR) { - cmd->error = - vub300_response_error(vub300->resp.error.error_code); - if (vub300->data) - usb_sg_cancel(&vub300->sg_request); - } else if (vub300->resp.common.header_type == RESPONSE_PIGGYBACKED) { - int offloaded_data_length = - vub300->resp.common.header_size - - sizeof(struct sd_register_header); - int register_count = offloaded_data_length >> 3; - int ri = 0; - while (register_count--) { - add_offloaded_reg(vub300, &vub300->resp.pig.reg[ri]); - ri += 1; - } - vub300->resp.common.header_size = - sizeof(struct sd_register_header); - vub300->resp.common.header_type = 0x00; - cmd->error = 0; - } else if (vub300->resp.common.header_type == RESPONSE_PIG_DISABLED) { - int offloaded_data_length = - vub300->resp.common.header_size - - sizeof(struct sd_register_header); - int register_count = offloaded_data_length >> 3; - int ri = 0; - while (register_count--) { - add_offloaded_reg(vub300, &vub300->resp.pig.reg[ri]); - ri += 1; - } - mutex_lock(&vub300->irq_mutex); - if (vub300->irqs_queued) { - vub300->irqs_queued += 1; - } else if (vub300->irq_enabled) { - vub300->irqs_queued += 1; - vub300_queue_poll_work(vub300, 0); - } else { - vub300->irqs_queued += 1; - } - vub300->irq_disabled = 1; - mutex_unlock(&vub300->irq_mutex); - vub300->resp.common.header_size = - sizeof(struct sd_register_header); - vub300->resp.common.header_type = 0x00; - cmd->error = 0; - } else if (vub300->resp.common.header_type == RESPONSE_PIG_ENABLED) { - int offloaded_data_length = - vub300->resp.common.header_size - - sizeof(struct sd_register_header); - int register_count = offloaded_data_length >> 3; - int ri = 0; - while (register_count--) { - add_offloaded_reg(vub300, &vub300->resp.pig.reg[ri]); - ri += 1; - } - mutex_lock(&vub300->irq_mutex); - if (vub300->irqs_queued) { - vub300->irqs_queued += 1; - } else if (vub300->irq_enabled) { - vub300->irqs_queued += 1; - vub300_queue_poll_work(vub300, 0); - } else { - vub300->irqs_queued += 1; - } - vub300->irq_disabled = 0; - mutex_unlock(&vub300->irq_mutex); - vub300->resp.common.header_size = - sizeof(struct sd_register_header); - vub300->resp.common.header_type = 0x00; - cmd->error = 0; - } else { - cmd->error = -EINVAL; - } - - return false; -} - -static void construct_request_response(struct vub300_mmc_host *vub300, - struct mmc_command *cmd) -{ - int resp_len = vub300->resp_len; - int less_cmd = (17 == resp_len) ? resp_len : resp_len - 1; - int bytes = 3 & less_cmd; - int words = less_cmd >> 2; - u8 *r = vub300->resp.response.command_response; - - if (!resp_len) - return; - if (bytes == 3) { - cmd->resp[words] = (r[1 + (words << 2)] << 24) - | (r[2 + (words << 2)] << 16) - | (r[3 + (words << 2)] << 8); - } else if (bytes == 2) { - cmd->resp[words] = (r[1 + (words << 2)] << 24) - | (r[2 + (words << 2)] << 16); - } else if (bytes == 1) { - cmd->resp[words] = (r[1 + (words << 2)] << 24); - } - while (words-- > 0) { - cmd->resp[words] = (r[1 + (words << 2)] << 24) - | (r[2 + (words << 2)] << 16) - | (r[3 + (words << 2)] << 8) - | (r[4 + (words << 2)] << 0); - } - if ((cmd->opcode == 53) && (0x000000FF & cmd->resp[0])) - cmd->resp[0] &= 0xFFFFFF00; -} - -/* this thread runs only when there is an upper level command req outstanding */ -static void vub300_cmndwork_thread(struct work_struct *work) -{ - struct vub300_mmc_host *vub300 = - container_of(work, struct vub300_mmc_host, cmndwork); - if (!vub300->interface) { - kref_put(&vub300->kref, vub300_delete); - return; - } else { - struct mmc_request *req = vub300->req; - struct mmc_command *cmd = vub300->cmd; - struct mmc_data *data = vub300->data; - bool reset_device; - int data_length; - mutex_lock(&vub300->cmd_mutex); - init_completion(&vub300->command_complete); - if (likely(vub300->vub_name[0]) || !vub300->mmc->card) { - /* - * the name of the EMPTY Pseudo firmware file - * is used as a flag to indicate that the file - * has been already downloaded to the VUB300 chip - */ - } else if (0 == vub300->mmc->card->sdio_funcs) { - strscpy(vub300->vub_name, "SD memory device", - sizeof(vub300->vub_name)); - } else { - download_offload_pseudocode(vub300); - } - send_command(vub300); - if (!data) - data_length = 0; - else if (MMC_DATA_READ & data->flags) - data_length = __command_read_data(vub300, cmd, data); - else - data_length = __command_write_data(vub300, cmd, data); - reset_device = __vub300_command_response(vub300, cmd, - data, data_length); - vub300->req = NULL; - vub300->cmd = NULL; - vub300->data = NULL; - if (cmd->error) { - if (cmd->error == -ENOMEDIUM) - check_vub300_port_status(vub300); - mutex_unlock(&vub300->cmd_mutex); - if (reset_device) { - int result; - - result = usb_lock_device_for_reset(vub300->udev, - vub300->interface); - if (result == 0) { - result = usb_reset_device(vub300->udev); - usb_unlock_device(vub300->udev); - } - } - mmc_request_done(vub300->mmc, req); - kref_put(&vub300->kref, vub300_delete); - return; - } else { - construct_request_response(vub300, cmd); - vub300->resp_len = 0; - mutex_unlock(&vub300->cmd_mutex); - kref_put(&vub300->kref, vub300_delete); - mmc_request_done(vub300->mmc, req); - return; - } - } -} - -static int examine_cyclic_buffer(struct vub300_mmc_host *vub300, - struct mmc_command *cmd, u8 Function) -{ - /* cmd_mutex is held by vub300_mmc_request */ - u8 cmd0 = 0xFF & (cmd->arg >> 24); - u8 cmd1 = 0xFF & (cmd->arg >> 16); - u8 cmd2 = 0xFF & (cmd->arg >> 8); - u8 cmd3 = 0xFF & (cmd->arg >> 0); - int first = MAXREGMASK & vub300->fn[Function].offload_point; - struct offload_registers_access *rf = &vub300->fn[Function].reg[first]; - if (cmd0 == rf->command_byte[0] && - cmd1 == rf->command_byte[1] && - cmd2 == rf->command_byte[2] && - cmd3 == rf->command_byte[3]) { - u8 checksum = 0x00; - cmd->resp[1] = checksum << 24; - cmd->resp[0] = (rf->Respond_Byte[0] << 24) - | (rf->Respond_Byte[1] << 16) - | (rf->Respond_Byte[2] << 8) - | (rf->Respond_Byte[3] << 0); - vub300->fn[Function].offload_point += 1; - vub300->fn[Function].offload_count -= 1; - vub300->total_offload_count -= 1; - return 1; - } else { - int delta = 1; /* because it does not match the first one */ - u8 register_count = vub300->fn[Function].offload_count - 1; - u32 register_point = vub300->fn[Function].offload_point + 1; - while (0 < register_count) { - int point = MAXREGMASK & register_point; - struct offload_registers_access *r = - &vub300->fn[Function].reg[point]; - if (cmd0 == r->command_byte[0] && - cmd1 == r->command_byte[1] && - cmd2 == r->command_byte[2] && - cmd3 == r->command_byte[3]) { - u8 checksum = 0x00; - cmd->resp[1] = checksum << 24; - cmd->resp[0] = (r->Respond_Byte[0] << 24) - | (r->Respond_Byte[1] << 16) - | (r->Respond_Byte[2] << 8) - | (r->Respond_Byte[3] << 0); - vub300->fn[Function].offload_point += delta; - vub300->fn[Function].offload_count -= delta; - vub300->total_offload_count -= delta; - return 1; - } else { - register_point += 1; - register_count -= 1; - delta += 1; - continue; - } - } - return 0; - } -} - -static int satisfy_request_from_offloaded_data(struct vub300_mmc_host *vub300, - struct mmc_command *cmd) -{ - /* cmd_mutex is held by vub300_mmc_request */ - u8 regs = vub300->dynamic_register_count; - u8 i = 0; - u8 func = FUN(cmd); - u32 reg = REG(cmd); - while (0 < regs--) { - if ((vub300->sdio_register[i].func_num == func) && - (vub300->sdio_register[i].sdio_reg == reg)) { - if (!vub300->sdio_register[i].prepared) { - return 0; - } else if ((0x80000000 & cmd->arg) == 0x80000000) { - /* - * a write to a dynamic register - * nullifies our offloaded value - */ - vub300->sdio_register[i].prepared = 0; - return 0; - } else { - u8 checksum = 0x00; - u8 rsp0 = 0x00; - u8 rsp1 = 0x00; - u8 rsp2 = vub300->sdio_register[i].response; - u8 rsp3 = vub300->sdio_register[i].regvalue; - vub300->sdio_register[i].prepared = 0; - cmd->resp[1] = checksum << 24; - cmd->resp[0] = (rsp0 << 24) - | (rsp1 << 16) - | (rsp2 << 8) - | (rsp3 << 0); - return 1; - } - } else { - i += 1; - continue; - } - } - if (vub300->total_offload_count == 0) - return 0; - else if (vub300->fn[func].offload_count == 0) - return 0; - else - return examine_cyclic_buffer(vub300, cmd, func); -} - -static void vub300_mmc_request(struct mmc_host *mmc, struct mmc_request *req) -{ /* NOT irq */ - struct mmc_command *cmd = req->cmd; - struct vub300_mmc_host *vub300 = mmc_priv(mmc); - if (!vub300->interface) { - cmd->error = -ESHUTDOWN; - mmc_request_done(mmc, req); - return; - } else { - struct mmc_data *data = req->data; - if (!vub300->card_powered) { - cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, req); - return; - } - if (!vub300->card_present) { - cmd->error = -ENOMEDIUM; - mmc_request_done(mmc, req); - return; - } - if (vub300->usb_transport_fail) { - cmd->error = vub300->usb_transport_fail; - mmc_request_done(mmc, req); - return; - } - if (!vub300->interface) { - cmd->error = -ENODEV; - mmc_request_done(mmc, req); - return; - } - kref_get(&vub300->kref); - mutex_lock(&vub300->cmd_mutex); - mod_timer(&vub300->inactivity_timer, jiffies + HZ); - /* - * for performance we have to return immediately - * if the requested data has been offloaded - */ - if (cmd->opcode == 52 && - satisfy_request_from_offloaded_data(vub300, cmd)) { - cmd->error = 0; - mutex_unlock(&vub300->cmd_mutex); - kref_put(&vub300->kref, vub300_delete); - mmc_request_done(mmc, req); - return; - } else { - vub300->cmd = cmd; - vub300->req = req; - vub300->data = data; - if (data) - vub300->datasize = data->blksz * data->blocks; - else - vub300->datasize = 0; - vub300_queue_cmnd_work(vub300); - mutex_unlock(&vub300->cmd_mutex); - kref_put(&vub300->kref, vub300_delete); - /* - * the kernel lock diagnostics complain - * if the cmd_mutex * is "passed on" - * to the cmndwork thread, - * so we must release it now - * and re-acquire it in the cmndwork thread - */ - } - } -} - -static void __set_clock_speed(struct vub300_mmc_host *vub300, u8 buf[8], - struct mmc_ios *ios) -{ - int buf_array_size = 8; /* ARRAY_SIZE(buf) does not work !!! */ - int retval; - u32 kHzClock; - if (ios->clock >= 48000000) - kHzClock = 48000; - else if (ios->clock >= 24000000) - kHzClock = 24000; - else if (ios->clock >= 20000000) - kHzClock = 20000; - else if (ios->clock >= 15000000) - kHzClock = 15000; - else if (ios->clock >= 200000) - kHzClock = 200; - else - kHzClock = 0; - { - int i; - u64 c = kHzClock; - for (i = 0; i < buf_array_size; i++) { - buf[i] = c; - c >>= 8; - } - } - retval = - usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), - SET_CLOCK_SPEED, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, 0x00, buf, buf_array_size, 1000); - if (retval != 8) { - dev_err(&vub300->udev->dev, "SET_CLOCK_SPEED" - " %dkHz failed with retval=%d\n", kHzClock, retval); - } else { - dev_dbg(&vub300->udev->dev, "SET_CLOCK_SPEED" - " %dkHz\n", kHzClock); - } -} - -static void vub300_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = mmc_priv(mmc); - if (!vub300->interface) - return; - kref_get(&vub300->kref); - mutex_lock(&vub300->cmd_mutex); - if ((ios->power_mode == MMC_POWER_OFF) && vub300->card_powered) { - vub300->card_powered = 0; - usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), - SET_SD_POWER, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, 0x0000, NULL, 0, 1000); - /* must wait for the VUB300 u-proc to boot up */ - msleep(600); - } else if ((ios->power_mode == MMC_POWER_UP) && !vub300->card_powered) { - usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), - SET_SD_POWER, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0001, 0x0000, NULL, 0, 1000); - msleep(600); - vub300->card_powered = 1; - } else if (ios->power_mode == MMC_POWER_ON) { - u8 *buf = kmalloc(8, GFP_KERNEL); - if (buf) { - __set_clock_speed(vub300, buf, ios); - kfree(buf); - } - } else { - /* this should mean no change of state */ - } - mutex_unlock(&vub300->cmd_mutex); - kref_put(&vub300->kref, vub300_delete); -} - -static int vub300_mmc_get_ro(struct mmc_host *mmc) -{ - struct vub300_mmc_host *vub300 = mmc_priv(mmc); - return vub300->read_only; -} - -static void vub300_enable_sdio_irq(struct mmc_host *mmc, int enable) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = mmc_priv(mmc); - if (!vub300->interface) - return; - kref_get(&vub300->kref); - if (enable) { - set_current_state(TASK_RUNNING); - mutex_lock(&vub300->irq_mutex); - if (vub300->irqs_queued) { - vub300->irqs_queued -= 1; - mmc_signal_sdio_irq(vub300->mmc); - } else if (vub300->irq_disabled) { - vub300->irq_disabled = 0; - vub300->irq_enabled = 1; - vub300_queue_poll_work(vub300, 0); - } else if (vub300->irq_enabled) { - /* this should not happen, so we will just ignore it */ - } else { - vub300->irq_enabled = 1; - vub300_queue_poll_work(vub300, 0); - } - mutex_unlock(&vub300->irq_mutex); - set_current_state(TASK_INTERRUPTIBLE); - } else { - vub300->irq_enabled = 0; - } - kref_put(&vub300->kref, vub300_delete); -} - -static const struct mmc_host_ops vub300_mmc_ops = { - .request = vub300_mmc_request, - .set_ios = vub300_mmc_set_ios, - .get_ro = vub300_mmc_get_ro, - .enable_sdio_irq = vub300_enable_sdio_irq, -}; - -static int vub300_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ /* NOT irq */ - struct vub300_mmc_host *vub300; - struct usb_host_interface *iface_desc; - struct usb_device *udev = usb_get_dev(interface_to_usbdev(interface)); - int i; - int retval = -ENOMEM; - struct urb *command_out_urb; - struct urb *command_res_urb; - struct mmc_host *mmc; - char manufacturer[48]; - char product[32]; - char serial_number[32]; - usb_string(udev, udev->descriptor.iManufacturer, manufacturer, - sizeof(manufacturer)); - usb_string(udev, udev->descriptor.iProduct, product, sizeof(product)); - usb_string(udev, udev->descriptor.iSerialNumber, serial_number, - sizeof(serial_number)); - dev_info(&udev->dev, "probing VID:PID(%04X:%04X) %s %s %s\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct), - manufacturer, product, serial_number); - command_out_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!command_out_urb) { - retval = -ENOMEM; - goto err_put_udev; - } - command_res_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!command_res_urb) { - retval = -ENOMEM; - goto err_free_out_urb; - } - /* this also allocates memory for our VUB300 mmc host device */ - mmc = mmc_alloc_host(sizeof(*vub300), &udev->dev); - if (!mmc) { - retval = -ENOMEM; - dev_err(&udev->dev, "not enough memory for the mmc_host\n"); - goto err_free_res_urb; - } - /* MMC core transfer sizes tunable parameters */ - mmc->caps = 0; - if (!force_1_bit_data_xfers) - mmc->caps |= MMC_CAP_4_BIT_DATA; - if (!force_polling_for_irqs) - mmc->caps |= MMC_CAP_SDIO_IRQ; - mmc->caps &= ~MMC_CAP_NEEDS_POLL; - /* - * MMC_CAP_NEEDS_POLL causes core.c:mmc_rescan() to poll - * for devices which results in spurious CMD7's being - * issued which stops some SDIO cards from working - */ - if (limit_speed_to_24_MHz) { - mmc->caps |= MMC_CAP_MMC_HIGHSPEED; - mmc->caps |= MMC_CAP_SD_HIGHSPEED; - mmc->f_max = 24000000; - dev_info(&udev->dev, "limiting SDIO speed to 24_MHz\n"); - } else { - mmc->caps |= MMC_CAP_MMC_HIGHSPEED; - mmc->caps |= MMC_CAP_SD_HIGHSPEED; - mmc->f_max = 48000000; - } - mmc->f_min = 200000; - mmc->max_blk_count = 511; - mmc->max_blk_size = 512; - mmc->max_segs = 128; - if (force_max_req_size) - mmc->max_req_size = force_max_req_size * 1024; - else - mmc->max_req_size = 64 * 1024; - mmc->max_seg_size = mmc->max_req_size; - mmc->ocr_avail = 0; - mmc->ocr_avail |= MMC_VDD_165_195; - mmc->ocr_avail |= MMC_VDD_20_21; - mmc->ocr_avail |= MMC_VDD_21_22; - mmc->ocr_avail |= MMC_VDD_22_23; - mmc->ocr_avail |= MMC_VDD_23_24; - mmc->ocr_avail |= MMC_VDD_24_25; - mmc->ocr_avail |= MMC_VDD_25_26; - mmc->ocr_avail |= MMC_VDD_26_27; - mmc->ocr_avail |= MMC_VDD_27_28; - mmc->ocr_avail |= MMC_VDD_28_29; - mmc->ocr_avail |= MMC_VDD_29_30; - mmc->ocr_avail |= MMC_VDD_30_31; - mmc->ocr_avail |= MMC_VDD_31_32; - mmc->ocr_avail |= MMC_VDD_32_33; - mmc->ocr_avail |= MMC_VDD_33_34; - mmc->ocr_avail |= MMC_VDD_34_35; - mmc->ocr_avail |= MMC_VDD_35_36; - mmc->ops = &vub300_mmc_ops; - vub300 = mmc_priv(mmc); - vub300->mmc = mmc; - vub300->card_powered = 0; - vub300->bus_width = 0; - vub300->cmnd.head.block_size[0] = 0x00; - vub300->cmnd.head.block_size[1] = 0x00; - vub300->app_spec = 0; - mutex_init(&vub300->cmd_mutex); - mutex_init(&vub300->irq_mutex); - vub300->command_out_urb = command_out_urb; - vub300->command_res_urb = command_res_urb; - vub300->usb_timed_out = 0; - vub300->dynamic_register_count = 0; - - for (i = 0; i < ARRAY_SIZE(vub300->fn); i++) { - vub300->fn[i].offload_point = 0; - vub300->fn[i].offload_count = 0; - } - - vub300->total_offload_count = 0; - vub300->irq_enabled = 0; - vub300->irq_disabled = 0; - vub300->irqs_queued = 0; - - for (i = 0; i < ARRAY_SIZE(vub300->sdio_register); i++) - vub300->sdio_register[i++].activate = 0; - - vub300->udev = udev; - vub300->interface = interface; - vub300->cmnd_res_ep = 0; - vub300->cmnd_out_ep = 0; - vub300->data_inp_ep = 0; - vub300->data_out_ep = 0; - - for (i = 0; i < ARRAY_SIZE(vub300->fbs); i++) - vub300->fbs[i] = 512; - - /* - * set up the endpoint information - * - * use the first pair of bulk-in and bulk-out - * endpoints for Command/Response+Interrupt - * - * use the second pair of bulk-in and bulk-out - * endpoints for Data In/Out - */ - vub300->large_usb_packets = 0; - iface_desc = interface->cur_altsetting; - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - struct usb_endpoint_descriptor *endpoint = - &iface_desc->endpoint[i].desc; - dev_info(&vub300->udev->dev, - "vub300 testing %s EndPoint(%d) %02X\n", - usb_endpoint_is_bulk_in(endpoint) ? "BULK IN" : - usb_endpoint_is_bulk_out(endpoint) ? "BULK OUT" : - "UNKNOWN", i, endpoint->bEndpointAddress); - if (endpoint->wMaxPacketSize > 64) - vub300->large_usb_packets = 1; - if (usb_endpoint_is_bulk_in(endpoint)) { - if (!vub300->cmnd_res_ep) { - vub300->cmnd_res_ep = - endpoint->bEndpointAddress; - } else if (!vub300->data_inp_ep) { - vub300->data_inp_ep = - endpoint->bEndpointAddress; - } else { - dev_warn(&vub300->udev->dev, - "ignoring" - " unexpected bulk_in endpoint"); - } - } else if (usb_endpoint_is_bulk_out(endpoint)) { - if (!vub300->cmnd_out_ep) { - vub300->cmnd_out_ep = - endpoint->bEndpointAddress; - } else if (!vub300->data_out_ep) { - vub300->data_out_ep = - endpoint->bEndpointAddress; - } else { - dev_warn(&vub300->udev->dev, - "ignoring" - " unexpected bulk_out endpoint"); - } - } else { - dev_warn(&vub300->udev->dev, - "vub300 ignoring EndPoint(%d) %02X", i, - endpoint->bEndpointAddress); - } - } - if (vub300->cmnd_res_ep && vub300->cmnd_out_ep && - vub300->data_inp_ep && vub300->data_out_ep) { - dev_info(&vub300->udev->dev, - "vub300 %s packets" - " using EndPoints %02X %02X %02X %02X\n", - vub300->large_usb_packets ? "LARGE" : "SMALL", - vub300->cmnd_out_ep, vub300->cmnd_res_ep, - vub300->data_out_ep, vub300->data_inp_ep); - /* we have the expected EndPoints */ - } else { - dev_err(&vub300->udev->dev, - "Could not find two sets of bulk-in/out endpoint pairs\n"); - retval = -EINVAL; - goto err_free_host; - } - retval = - usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), - GET_HC_INF0, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, 0x0000, &vub300->hc_info, - sizeof(vub300->hc_info), 1000); - if (retval < 0) - goto err_free_host; - retval = - usb_control_msg(vub300->udev, usb_sndctrlpipe(vub300->udev, 0), - SET_ROM_WAIT_STATES, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - firmware_rom_wait_states, 0x0000, NULL, 0, 1000); - if (retval < 0) - goto err_free_host; - dev_info(&vub300->udev->dev, - "operating_mode = %s %s %d MHz %s %d byte USB packets\n", - (mmc->caps & MMC_CAP_SDIO_IRQ) ? "IRQs" : "POLL", - (mmc->caps & MMC_CAP_4_BIT_DATA) ? "4-bit" : "1-bit", - mmc->f_max / 1000000, - pad_input_to_usb_pkt ? "padding input data to" : "with", - vub300->large_usb_packets ? 512 : 64); - retval = - usb_control_msg(vub300->udev, usb_rcvctrlpipe(vub300->udev, 0), - GET_SYSTEM_PORT_STATUS, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x0000, 0x0000, &vub300->system_port_status, - sizeof(vub300->system_port_status), 1000); - if (retval < 0) { - goto err_free_host; - } else if (sizeof(vub300->system_port_status) == retval) { - vub300->card_present = - (0x0001 & vub300->system_port_status.port_flags) ? 1 : 0; - vub300->read_only = - (0x0010 & vub300->system_port_status.port_flags) ? 1 : 0; - } else { - retval = -EINVAL; - goto err_free_host; - } - usb_set_intfdata(interface, vub300); - INIT_DELAYED_WORK(&vub300->pollwork, vub300_pollwork_thread); - INIT_WORK(&vub300->cmndwork, vub300_cmndwork_thread); - INIT_WORK(&vub300->deadwork, vub300_deadwork_thread); - kref_init(&vub300->kref); - timer_setup(&vub300->sg_transfer_timer, vub300_sg_timed_out, 0); - kref_get(&vub300->kref); - timer_setup(&vub300->inactivity_timer, - vub300_inactivity_timer_expired, 0); - vub300->inactivity_timer.expires = jiffies + HZ; - add_timer(&vub300->inactivity_timer); - if (vub300->card_present) - dev_info(&vub300->udev->dev, - "USB vub300 remote SDIO host controller[%d]" - "connected with SD/SDIO card inserted\n", - interface_to_InterfaceNumber(interface)); - else - dev_info(&vub300->udev->dev, - "USB vub300 remote SDIO host controller[%d]" - "connected with no SD/SDIO card inserted\n", - interface_to_InterfaceNumber(interface)); - retval = mmc_add_host(mmc); - if (retval) - goto err_stop_io; - - return 0; - -err_stop_io: - vub300->interface = NULL; - kref_put(&vub300->kref, vub300_delete); - - return retval; - -err_free_host: - mmc_free_host(mmc); - /* - * and hence also frees vub300 - * which is contained at the end of struct mmc - */ -err_free_res_urb: - usb_free_urb(command_res_urb); -err_free_out_urb: - usb_free_urb(command_out_urb); -err_put_udev: - usb_put_dev(udev); - - return retval; -} - -static void vub300_disconnect(struct usb_interface *interface) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = usb_get_intfdata(interface); - if (!vub300 || !vub300->mmc) { - return; - } else { - struct mmc_host *mmc = vub300->mmc; - if (!vub300->mmc) { - return; - } else { - int ifnum = interface_to_InterfaceNumber(interface); - usb_set_intfdata(interface, NULL); - /* prevent more I/O from starting */ - vub300->interface = NULL; - mmc_remove_host(mmc); - kref_put(&vub300->kref, vub300_delete); - pr_info("USB vub300 remote SDIO host controller[%d]" - " now disconnected", ifnum); - return; - } - } -} - -#ifdef CONFIG_PM -static int vub300_suspend(struct usb_interface *intf, pm_message_t message) -{ - return 0; -} - -static int vub300_resume(struct usb_interface *intf) -{ - return 0; -} -#else -#define vub300_suspend NULL -#define vub300_resume NULL -#endif -static int vub300_pre_reset(struct usb_interface *intf) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); - mutex_lock(&vub300->cmd_mutex); - return 0; -} - -static int vub300_post_reset(struct usb_interface *intf) -{ /* NOT irq */ - struct vub300_mmc_host *vub300 = usb_get_intfdata(intf); - /* we are sure no URBs are active - no locking needed */ - vub300->errors = -EPIPE; - mutex_unlock(&vub300->cmd_mutex); - return 0; -} - -static struct usb_driver vub300_driver = { - .name = "vub300", - .probe = vub300_probe, - .disconnect = vub300_disconnect, - .suspend = vub300_suspend, - .resume = vub300_resume, - .pre_reset = vub300_pre_reset, - .post_reset = vub300_post_reset, - .id_table = vub300_table, - .supports_autosuspend = 1, -}; - -static int __init vub300_init(void) -{ /* NOT irq */ - int result; - - pr_info("VUB300 Driver rom wait states = %02X irqpoll timeout = %04X", - firmware_rom_wait_states, 0x0FFFF & firmware_irqpoll_timeout); - - cmndworkqueue = create_singlethread_workqueue("kvub300c"); - if (!cmndworkqueue) - return -ENOMEM; - - pollworkqueue = create_singlethread_workqueue("kvub300p"); - if (!pollworkqueue) { - result = -ENOMEM; - goto err_destroy_cmdwq; - } - - deadworkqueue = create_singlethread_workqueue("kvub300d"); - if (!deadworkqueue) { - result = -ENOMEM; - goto err_destroy_pollwq; - } - - result = usb_register(&vub300_driver); - if (result) - goto err_destroy_deadwq; - - return 0; - -err_destroy_deadwq: - destroy_workqueue(deadworkqueue); -err_destroy_pollwq: - destroy_workqueue(pollworkqueue); -err_destroy_cmdwq: - destroy_workqueue(cmndworkqueue); - - return result; -} - -static void __exit vub300_exit(void) -{ - usb_deregister(&vub300_driver); - flush_workqueue(cmndworkqueue); - flush_workqueue(pollworkqueue); - flush_workqueue(deadworkqueue); - destroy_workqueue(cmndworkqueue); - destroy_workqueue(pollworkqueue); - destroy_workqueue(deadworkqueue); -} - -module_init(vub300_init); -module_exit(vub300_exit); - -MODULE_AUTHOR("Tony Olech <tony.olech@elandigitalsystems.com>"); -MODULE_DESCRIPTION("VUB300 USB to SD/MMC/SDIO adapter driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/greybus/sdio.c b/drivers/staging/greybus/sdio.c index 3952f3d225db..642d785fe918 100644 --- a/drivers/staging/greybus/sdio.c +++ b/drivers/staging/greybus/sdio.c @@ -767,17 +767,15 @@ static int gb_sdio_probe(struct gbphy_device *gbphy_dev, struct gb_sdio_host *host; int ret = 0; - mmc = mmc_alloc_host(sizeof(*host), &gbphy_dev->dev); + mmc = devm_mmc_alloc_host(&gbphy_dev->dev, sizeof(*host)); if (!mmc) return -ENOMEM; connection = gb_connection_create(gbphy_dev->bundle, le16_to_cpu(gbphy_dev->cport_desc->id), gb_sdio_request_handler); - if (IS_ERR(connection)) { - ret = PTR_ERR(connection); - goto exit_mmc_free; - } + if (IS_ERR(connection)) + return PTR_ERR(connection); host = mmc_priv(mmc); host->mmc = mmc; @@ -835,8 +833,6 @@ exit_connection_disable: gb_connection_disable(connection); exit_connection_destroy: gb_connection_destroy(connection); -exit_mmc_free: - mmc_free_host(mmc); return ret; } @@ -863,7 +859,6 @@ static void gb_sdio_remove(struct gbphy_device *gbphy_dev) mmc_remove_host(mmc); gb_connection_disable(connection); gb_connection_destroy(connection); - mmc_free_host(mmc); } static const struct gbphy_device_id gb_sdio_id_table[] = { diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index ba84f02c2a10..032c018650e0 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -23,6 +23,7 @@ struct mmc_ios { unsigned int clock; /* clock rate */ unsigned short vdd; unsigned int power_delay_ms; /* waiting for stable power */ + unsigned int power_off_delay_us; /* waiting for power discharge */ /* vdd stores the bit number of the selected voltage range from below. */ @@ -463,6 +464,7 @@ struct mmc_host { #define MMC_CAP2_CRYPTO 0 #endif #define MMC_CAP2_ALT_GPT_TEGRA (1 << 28) /* Host with eMMC that has GPT entry at a non-standard location */ +#define MMC_CAP2_CRYPTO_NO_REPROG (1 << 29) /* Host handles inline crypto key reprogramming */ bool uhs2_sd_tran; /* UHS-II flag for SD_TRAN state */ bool uhs2_app_cmd; /* UHS-II flag for APP command */ @@ -583,11 +585,9 @@ struct mmc_host { struct device_node; -struct mmc_host *mmc_alloc_host(int extra, struct device *); struct mmc_host *devm_mmc_alloc_host(struct device *dev, int extra); int mmc_add_host(struct mmc_host *); void mmc_remove_host(struct mmc_host *); -void mmc_free_host(struct mmc_host *); void mmc_of_parse_clk_phase(struct device *dev, struct mmc_clk_phase_map *map); int mmc_of_parse(struct mmc_host *host); @@ -753,6 +753,8 @@ static inline int mmc_card_uhs2_hd_mode(struct mmc_host *host) int mmc_sd_switch(struct mmc_card *card, bool mode, int group, u8 value, u8 *resp); int mmc_send_status(struct mmc_card *card, u32 *status); +int mmc_send_tuning_timeout(struct mmc_host *host, u32 opcode, int *cmd_error, + u32 timeout_ms); int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode); int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); |
