1f707079dSWolfram Sang // SPDX-License-Identifier: GPL-2.0 275fa9ea6SGuennadi Liakhovetski /* 375fa9ea6SGuennadi Liakhovetski * Copyright (C) 2013-2014 Renesas Electronics Europe Ltd. 475fa9ea6SGuennadi Liakhovetski * Author: Guennadi Liakhovetski <g.liakhovetski@gmx.de> 575fa9ea6SGuennadi Liakhovetski */ 675fa9ea6SGuennadi Liakhovetski 775fa9ea6SGuennadi Liakhovetski #include <linux/clk.h> 875fa9ea6SGuennadi Liakhovetski #include <linux/delay.h> 975fa9ea6SGuennadi Liakhovetski #include <linux/device.h> 1075fa9ea6SGuennadi Liakhovetski #include <linux/dma-mapping.h> 1175fa9ea6SGuennadi Liakhovetski #include <linux/dmaengine.h> 1275fa9ea6SGuennadi Liakhovetski #include <linux/highmem.h> 1375fa9ea6SGuennadi Liakhovetski #include <linux/interrupt.h> 1475fa9ea6SGuennadi Liakhovetski #include <linux/io.h> 1575fa9ea6SGuennadi Liakhovetski #include <linux/log2.h> 1675fa9ea6SGuennadi Liakhovetski #include <linux/mmc/host.h> 1775fa9ea6SGuennadi Liakhovetski #include <linux/mmc/mmc.h> 1875fa9ea6SGuennadi Liakhovetski #include <linux/mmc/sd.h> 1975fa9ea6SGuennadi Liakhovetski #include <linux/mmc/sdio.h> 2075fa9ea6SGuennadi Liakhovetski #include <linux/module.h> 2175fa9ea6SGuennadi Liakhovetski #include <linux/pagemap.h> 22488aab3dSLars Persson #include <linux/pinctrl/consumer.h> 2375fa9ea6SGuennadi Liakhovetski #include <linux/platform_device.h> 2475fa9ea6SGuennadi Liakhovetski #include <linux/scatterlist.h> 2575fa9ea6SGuennadi Liakhovetski #include <linux/string.h> 2675fa9ea6SGuennadi Liakhovetski #include <linux/time.h> 2775fa9ea6SGuennadi Liakhovetski #include <linux/virtio.h> 2875fa9ea6SGuennadi Liakhovetski #include <linux/workqueue.h> 2975fa9ea6SGuennadi Liakhovetski 3075fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD 0x0000 3175fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_PORT_SEL 0x0004 3275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_ARG 0x0008 3375fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_STOP 0x0010 3475fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_SECCNT 0x0014 3575fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_RSP10 0x0018 3675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_RSP32 0x0020 3775fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_RSP54 0x0028 3875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_RSP76 0x0030 3975fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1 0x0038 4075fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2 0x003c 4175fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_MASK 0x0040 4275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_MASK 0x0044 4375fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CLK_CTRL 0x0048 4475fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_SIZE 0x004c 4575fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_OPTION 0x0050 4675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_ERR_STS1 0x0058 4775fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_ERR_STS2 0x005c 4875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_BUF0 0x0060 4975fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIO_MODE 0x0068 5075fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIO_INFO1 0x006c 5175fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIO_INFO1_MASK 0x0070 5275fa9ea6SGuennadi Liakhovetski #define USDHI6_CC_EXT_MODE 0x01b0 5375fa9ea6SGuennadi Liakhovetski #define USDHI6_SOFT_RST 0x01c0 5475fa9ea6SGuennadi Liakhovetski #define USDHI6_VERSION 0x01c4 5575fa9ea6SGuennadi Liakhovetski #define USDHI6_HOST_MODE 0x01c8 5675fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIF_MODE 0x01cc 5775fa9ea6SGuennadi Liakhovetski 5875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_APP 0x0040 5975fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_MODE_RSP_AUTO 0x0000 6075fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_MODE_RSP_NONE 0x0300 6175fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_MODE_RSP_R1 0x0400 /* Also R5, R6, R7 */ 6275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_MODE_RSP_R1B 0x0500 /* R1b */ 6375fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_MODE_RSP_R2 0x0600 6475fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_MODE_RSP_R3 0x0700 /* Also R4 */ 6575fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_DATA 0x0800 6675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_READ 0x1000 6775fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_MULTI 0x2000 6875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CMD_CMD12_AUTO_OFF 0x4000 6975fa9ea6SGuennadi Liakhovetski 7075fa9ea6SGuennadi Liakhovetski #define USDHI6_CC_EXT_MODE_SDRW BIT(1) 7175fa9ea6SGuennadi Liakhovetski 7275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_RSP_END BIT(0) 7375fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_ACCESS_END BIT(2) 7475fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_CARD_OUT BIT(3) 7575fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_CARD_IN BIT(4) 7675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_CD BIT(5) 7775fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_WP BIT(7) 7875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_D3_CARD_OUT BIT(8) 7975fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_D3_CARD_IN BIT(9) 8075fa9ea6SGuennadi Liakhovetski 8175fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_CMD_ERR BIT(0) 8275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_CRC_ERR BIT(1) 8375fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_END_ERR BIT(2) 8475fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_TOUT BIT(3) 8575fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_IWA_ERR BIT(4) 8675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_IRA_ERR BIT(5) 8775fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_RSP_TOUT BIT(6) 8875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_SDDAT0 BIT(7) 8975fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_BRE BIT(8) 9075fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_BWE BIT(9) 9175fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_SCLKDIVEN BIT(13) 9275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_CBSY BIT(14) 9375fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_ILA BIT(15) 9475fa9ea6SGuennadi Liakhovetski 9575fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_CARD_INSERT (USDHI6_SD_INFO1_CARD_IN | USDHI6_SD_INFO1_D3_CARD_IN) 9675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_CARD_EJECT (USDHI6_SD_INFO1_CARD_OUT | USDHI6_SD_INFO1_D3_CARD_OUT) 9775fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_CARD (USDHI6_SD_INFO1_CARD_INSERT | USDHI6_SD_INFO1_CARD_EJECT) 9875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_CARD_CD (USDHI6_SD_INFO1_CARD_IN | USDHI6_SD_INFO1_CARD_OUT) 9975fa9ea6SGuennadi Liakhovetski 10075fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_ERR (USDHI6_SD_INFO2_CMD_ERR | \ 10175fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO2_CRC_ERR | USDHI6_SD_INFO2_END_ERR | \ 10275fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO2_TOUT | USDHI6_SD_INFO2_IWA_ERR | \ 10375fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO2_IRA_ERR | USDHI6_SD_INFO2_RSP_TOUT | \ 10475fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO2_ILA) 10575fa9ea6SGuennadi Liakhovetski 10675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO1_IRQ (USDHI6_SD_INFO1_RSP_END | USDHI6_SD_INFO1_ACCESS_END | \ 10775fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO1_CARD) 10875fa9ea6SGuennadi Liakhovetski 10975fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_INFO2_IRQ (USDHI6_SD_INFO2_ERR | USDHI6_SD_INFO2_BRE | \ 11075fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO2_BWE | 0x0800 | USDHI6_SD_INFO2_ILA) 11175fa9ea6SGuennadi Liakhovetski 11275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CLK_CTRL_SCLKEN BIT(8) 11375fa9ea6SGuennadi Liakhovetski 11475fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_STOP_STP BIT(0) 11575fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_STOP_SEC BIT(8) 11675fa9ea6SGuennadi Liakhovetski 11775fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIO_INFO1_IOIRQ BIT(0) 11875fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIO_INFO1_EXPUB52 BIT(14) 11975fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIO_INFO1_EXWT BIT(15) 12075fa9ea6SGuennadi Liakhovetski 12175fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_ERR_STS1_CRC_NO_ERROR BIT(13) 12275fa9ea6SGuennadi Liakhovetski 12375fa9ea6SGuennadi Liakhovetski #define USDHI6_SOFT_RST_RESERVED (BIT(1) | BIT(2)) 12475fa9ea6SGuennadi Liakhovetski #define USDHI6_SOFT_RST_RESET BIT(0) 12575fa9ea6SGuennadi Liakhovetski 12675fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_OPTION_TIMEOUT_SHIFT 4 12775fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_OPTION_TIMEOUT_MASK (0xf << USDHI6_SD_OPTION_TIMEOUT_SHIFT) 12875fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_OPTION_WIDTH_1 BIT(15) 12975fa9ea6SGuennadi Liakhovetski 13075fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_PORT_SEL_PORTS_SHIFT 8 13175fa9ea6SGuennadi Liakhovetski 13275fa9ea6SGuennadi Liakhovetski #define USDHI6_SD_CLK_CTRL_DIV_MASK 0xff 13375fa9ea6SGuennadi Liakhovetski 13475fa9ea6SGuennadi Liakhovetski #define USDHI6_SDIO_INFO1_IRQ (USDHI6_SDIO_INFO1_IOIRQ | 3 | \ 13575fa9ea6SGuennadi Liakhovetski USDHI6_SDIO_INFO1_EXPUB52 | USDHI6_SDIO_INFO1_EXWT) 13675fa9ea6SGuennadi Liakhovetski 13775fa9ea6SGuennadi Liakhovetski #define USDHI6_MIN_DMA 64 13875fa9ea6SGuennadi Liakhovetski 1394c2e04d7SUlf Hansson #define USDHI6_REQ_TIMEOUT_MS 4000 1404c2e04d7SUlf Hansson 14175fa9ea6SGuennadi Liakhovetski enum usdhi6_wait_for { 14275fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_REQUEST, 14375fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_CMD, 14475fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_MREAD, 14575fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_MWRITE, 14675fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_READ, 14775fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_WRITE, 14875fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_DATA_END, 14975fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_STOP, 15075fa9ea6SGuennadi Liakhovetski USDHI6_WAIT_FOR_DMA, 15175fa9ea6SGuennadi Liakhovetski }; 15275fa9ea6SGuennadi Liakhovetski 15375fa9ea6SGuennadi Liakhovetski struct usdhi6_page { 15475fa9ea6SGuennadi Liakhovetski struct page *page; 15575fa9ea6SGuennadi Liakhovetski void *mapped; /* mapped page */ 15675fa9ea6SGuennadi Liakhovetski }; 15775fa9ea6SGuennadi Liakhovetski 15875fa9ea6SGuennadi Liakhovetski struct usdhi6_host { 15975fa9ea6SGuennadi Liakhovetski struct mmc_host *mmc; 16075fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq; 16175fa9ea6SGuennadi Liakhovetski void __iomem *base; 16275fa9ea6SGuennadi Liakhovetski struct clk *clk; 16375fa9ea6SGuennadi Liakhovetski 16475fa9ea6SGuennadi Liakhovetski /* SG memory handling */ 16575fa9ea6SGuennadi Liakhovetski 16675fa9ea6SGuennadi Liakhovetski /* Common for multiple and single block requests */ 16775fa9ea6SGuennadi Liakhovetski struct usdhi6_page pg; /* current page from an SG */ 16875fa9ea6SGuennadi Liakhovetski void *blk_page; /* either a mapped page, or the bounce buffer */ 16975fa9ea6SGuennadi Liakhovetski size_t offset; /* offset within a page, including sg->offset */ 17075fa9ea6SGuennadi Liakhovetski 17175fa9ea6SGuennadi Liakhovetski /* Blocks, crossing a page boundary */ 17275fa9ea6SGuennadi Liakhovetski size_t head_len; 17375fa9ea6SGuennadi Liakhovetski struct usdhi6_page head_pg; 17475fa9ea6SGuennadi Liakhovetski 17575fa9ea6SGuennadi Liakhovetski /* A bounce buffer for unaligned blocks or blocks, crossing a page boundary */ 17675fa9ea6SGuennadi Liakhovetski struct scatterlist bounce_sg; 17775fa9ea6SGuennadi Liakhovetski u8 bounce_buf[512]; 17875fa9ea6SGuennadi Liakhovetski 17975fa9ea6SGuennadi Liakhovetski /* Multiple block requests only */ 18075fa9ea6SGuennadi Liakhovetski struct scatterlist *sg; /* current SG segment */ 18175fa9ea6SGuennadi Liakhovetski int page_idx; /* page index within an SG segment */ 18275fa9ea6SGuennadi Liakhovetski 18375fa9ea6SGuennadi Liakhovetski enum usdhi6_wait_for wait; 18475fa9ea6SGuennadi Liakhovetski u32 status_mask; 18575fa9ea6SGuennadi Liakhovetski u32 status2_mask; 18675fa9ea6SGuennadi Liakhovetski u32 sdio_mask; 18775fa9ea6SGuennadi Liakhovetski u32 io_error; 18875fa9ea6SGuennadi Liakhovetski u32 irq_status; 18975fa9ea6SGuennadi Liakhovetski unsigned long imclk; 19075fa9ea6SGuennadi Liakhovetski unsigned long rate; 19175fa9ea6SGuennadi Liakhovetski bool app_cmd; 19275fa9ea6SGuennadi Liakhovetski 19375fa9ea6SGuennadi Liakhovetski /* Timeout handling */ 19475fa9ea6SGuennadi Liakhovetski struct delayed_work timeout_work; 19575fa9ea6SGuennadi Liakhovetski unsigned long timeout; 19675fa9ea6SGuennadi Liakhovetski 19775fa9ea6SGuennadi Liakhovetski /* DMA support */ 19875fa9ea6SGuennadi Liakhovetski struct dma_chan *chan_rx; 19975fa9ea6SGuennadi Liakhovetski struct dma_chan *chan_tx; 20075fa9ea6SGuennadi Liakhovetski bool dma_active; 201488aab3dSLars Persson 202488aab3dSLars Persson /* Pin control */ 203488aab3dSLars Persson struct pinctrl *pinctrl; 204488aab3dSLars Persson struct pinctrl_state *pins_uhs; 20575fa9ea6SGuennadi Liakhovetski }; 20675fa9ea6SGuennadi Liakhovetski 20775fa9ea6SGuennadi Liakhovetski /* I/O primitives */ 20875fa9ea6SGuennadi Liakhovetski 20975fa9ea6SGuennadi Liakhovetski static void usdhi6_write(struct usdhi6_host *host, u32 reg, u32 data) 21075fa9ea6SGuennadi Liakhovetski { 21175fa9ea6SGuennadi Liakhovetski iowrite32(data, host->base + reg); 21275fa9ea6SGuennadi Liakhovetski dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, 21375fa9ea6SGuennadi Liakhovetski host->base, reg, data); 21475fa9ea6SGuennadi Liakhovetski } 21575fa9ea6SGuennadi Liakhovetski 21675fa9ea6SGuennadi Liakhovetski static void usdhi6_write16(struct usdhi6_host *host, u32 reg, u16 data) 21775fa9ea6SGuennadi Liakhovetski { 21875fa9ea6SGuennadi Liakhovetski iowrite16(data, host->base + reg); 21975fa9ea6SGuennadi Liakhovetski dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, 22075fa9ea6SGuennadi Liakhovetski host->base, reg, data); 22175fa9ea6SGuennadi Liakhovetski } 22275fa9ea6SGuennadi Liakhovetski 22375fa9ea6SGuennadi Liakhovetski static u32 usdhi6_read(struct usdhi6_host *host, u32 reg) 22475fa9ea6SGuennadi Liakhovetski { 22575fa9ea6SGuennadi Liakhovetski u32 data = ioread32(host->base + reg); 22675fa9ea6SGuennadi Liakhovetski dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, 22775fa9ea6SGuennadi Liakhovetski host->base, reg, data); 22875fa9ea6SGuennadi Liakhovetski return data; 22975fa9ea6SGuennadi Liakhovetski } 23075fa9ea6SGuennadi Liakhovetski 23175fa9ea6SGuennadi Liakhovetski static u16 usdhi6_read16(struct usdhi6_host *host, u32 reg) 23275fa9ea6SGuennadi Liakhovetski { 23375fa9ea6SGuennadi Liakhovetski u16 data = ioread16(host->base + reg); 23475fa9ea6SGuennadi Liakhovetski dev_vdbg(mmc_dev(host->mmc), "%s(0x%p + 0x%x) = 0x%x\n", __func__, 23575fa9ea6SGuennadi Liakhovetski host->base, reg, data); 23675fa9ea6SGuennadi Liakhovetski return data; 23775fa9ea6SGuennadi Liakhovetski } 23875fa9ea6SGuennadi Liakhovetski 23975fa9ea6SGuennadi Liakhovetski static void usdhi6_irq_enable(struct usdhi6_host *host, u32 info1, u32 info2) 24075fa9ea6SGuennadi Liakhovetski { 24175fa9ea6SGuennadi Liakhovetski host->status_mask = USDHI6_SD_INFO1_IRQ & ~info1; 24275fa9ea6SGuennadi Liakhovetski host->status2_mask = USDHI6_SD_INFO2_IRQ & ~info2; 24375fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_INFO1_MASK, host->status_mask); 24475fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_INFO2_MASK, host->status2_mask); 24575fa9ea6SGuennadi Liakhovetski } 24675fa9ea6SGuennadi Liakhovetski 24775fa9ea6SGuennadi Liakhovetski static void usdhi6_wait_for_resp(struct usdhi6_host *host) 24875fa9ea6SGuennadi Liakhovetski { 24975fa9ea6SGuennadi Liakhovetski usdhi6_irq_enable(host, USDHI6_SD_INFO1_RSP_END | 25075fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO1_ACCESS_END | USDHI6_SD_INFO1_CARD_CD, 25175fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO2_ERR); 25275fa9ea6SGuennadi Liakhovetski } 25375fa9ea6SGuennadi Liakhovetski 25475fa9ea6SGuennadi Liakhovetski static void usdhi6_wait_for_brwe(struct usdhi6_host *host, bool read) 25575fa9ea6SGuennadi Liakhovetski { 25675fa9ea6SGuennadi Liakhovetski usdhi6_irq_enable(host, USDHI6_SD_INFO1_ACCESS_END | 25775fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO1_CARD_CD, USDHI6_SD_INFO2_ERR | 25875fa9ea6SGuennadi Liakhovetski (read ? USDHI6_SD_INFO2_BRE : USDHI6_SD_INFO2_BWE)); 25975fa9ea6SGuennadi Liakhovetski } 26075fa9ea6SGuennadi Liakhovetski 26175fa9ea6SGuennadi Liakhovetski static void usdhi6_only_cd(struct usdhi6_host *host) 26275fa9ea6SGuennadi Liakhovetski { 26375fa9ea6SGuennadi Liakhovetski /* Mask all except card hotplug */ 26475fa9ea6SGuennadi Liakhovetski usdhi6_irq_enable(host, USDHI6_SD_INFO1_CARD_CD, 0); 26575fa9ea6SGuennadi Liakhovetski } 26675fa9ea6SGuennadi Liakhovetski 26775fa9ea6SGuennadi Liakhovetski static void usdhi6_mask_all(struct usdhi6_host *host) 26875fa9ea6SGuennadi Liakhovetski { 26975fa9ea6SGuennadi Liakhovetski usdhi6_irq_enable(host, 0, 0); 27075fa9ea6SGuennadi Liakhovetski } 27175fa9ea6SGuennadi Liakhovetski 27275fa9ea6SGuennadi Liakhovetski static int usdhi6_error_code(struct usdhi6_host *host) 27375fa9ea6SGuennadi Liakhovetski { 27475fa9ea6SGuennadi Liakhovetski u32 err; 27575fa9ea6SGuennadi Liakhovetski 27675fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_STOP, USDHI6_SD_STOP_STP); 27775fa9ea6SGuennadi Liakhovetski 27875fa9ea6SGuennadi Liakhovetski if (host->io_error & 27975fa9ea6SGuennadi Liakhovetski (USDHI6_SD_INFO2_RSP_TOUT | USDHI6_SD_INFO2_TOUT)) { 28075fa9ea6SGuennadi Liakhovetski u32 rsp54 = usdhi6_read(host, USDHI6_SD_RSP54); 28175fa9ea6SGuennadi Liakhovetski int opc = host->mrq ? host->mrq->cmd->opcode : -1; 28275fa9ea6SGuennadi Liakhovetski 28375fa9ea6SGuennadi Liakhovetski err = usdhi6_read(host, USDHI6_SD_ERR_STS2); 28475fa9ea6SGuennadi Liakhovetski /* Response timeout is often normal, don't spam the log */ 28575fa9ea6SGuennadi Liakhovetski if (host->wait == USDHI6_WAIT_FOR_CMD) 28675fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), 28775fa9ea6SGuennadi Liakhovetski "T-out sts 0x%x, resp 0x%x, state %u, CMD%d\n", 28875fa9ea6SGuennadi Liakhovetski err, rsp54, host->wait, opc); 28975fa9ea6SGuennadi Liakhovetski else 29075fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), 29175fa9ea6SGuennadi Liakhovetski "T-out sts 0x%x, resp 0x%x, state %u, CMD%d\n", 29275fa9ea6SGuennadi Liakhovetski err, rsp54, host->wait, opc); 29375fa9ea6SGuennadi Liakhovetski return -ETIMEDOUT; 29475fa9ea6SGuennadi Liakhovetski } 29575fa9ea6SGuennadi Liakhovetski 29675fa9ea6SGuennadi Liakhovetski err = usdhi6_read(host, USDHI6_SD_ERR_STS1); 29775fa9ea6SGuennadi Liakhovetski if (err != USDHI6_SD_ERR_STS1_CRC_NO_ERROR) 29875fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), "Err sts 0x%x, state %u, CMD%d\n", 29975fa9ea6SGuennadi Liakhovetski err, host->wait, host->mrq ? host->mrq->cmd->opcode : -1); 30075fa9ea6SGuennadi Liakhovetski if (host->io_error & USDHI6_SD_INFO2_ILA) 30175fa9ea6SGuennadi Liakhovetski return -EILSEQ; 30275fa9ea6SGuennadi Liakhovetski 30375fa9ea6SGuennadi Liakhovetski return -EIO; 30475fa9ea6SGuennadi Liakhovetski } 30575fa9ea6SGuennadi Liakhovetski 30675fa9ea6SGuennadi Liakhovetski /* Scatter-Gather management */ 30775fa9ea6SGuennadi Liakhovetski 30875fa9ea6SGuennadi Liakhovetski /* 30975fa9ea6SGuennadi Liakhovetski * In PIO mode we have to map each page separately, using kmap(). That way 31075fa9ea6SGuennadi Liakhovetski * adjacent pages are mapped to non-adjacent virtual addresses. That's why we 31175fa9ea6SGuennadi Liakhovetski * have to use a bounce buffer for blocks, crossing page boundaries. Such blocks 31275fa9ea6SGuennadi Liakhovetski * have been observed with an SDIO WiFi card (b43 driver). 31375fa9ea6SGuennadi Liakhovetski */ 31475fa9ea6SGuennadi Liakhovetski static void usdhi6_blk_bounce(struct usdhi6_host *host, 31575fa9ea6SGuennadi Liakhovetski struct scatterlist *sg) 31675fa9ea6SGuennadi Liakhovetski { 31775fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 31875fa9ea6SGuennadi Liakhovetski size_t blk_head = host->head_len; 31975fa9ea6SGuennadi Liakhovetski 32075fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): CMD%u of %u SG: %ux%u @ 0x%x\n", 32175fa9ea6SGuennadi Liakhovetski __func__, host->mrq->cmd->opcode, data->sg_len, 32275fa9ea6SGuennadi Liakhovetski data->blksz, data->blocks, sg->offset); 32375fa9ea6SGuennadi Liakhovetski 32475fa9ea6SGuennadi Liakhovetski host->head_pg.page = host->pg.page; 32575fa9ea6SGuennadi Liakhovetski host->head_pg.mapped = host->pg.mapped; 32675fa9ea6SGuennadi Liakhovetski host->pg.page = nth_page(host->pg.page, 1); 32775fa9ea6SGuennadi Liakhovetski host->pg.mapped = kmap(host->pg.page); 32875fa9ea6SGuennadi Liakhovetski 32975fa9ea6SGuennadi Liakhovetski host->blk_page = host->bounce_buf; 33075fa9ea6SGuennadi Liakhovetski host->offset = 0; 33175fa9ea6SGuennadi Liakhovetski 33275fa9ea6SGuennadi Liakhovetski if (data->flags & MMC_DATA_READ) 33375fa9ea6SGuennadi Liakhovetski return; 33475fa9ea6SGuennadi Liakhovetski 33575fa9ea6SGuennadi Liakhovetski memcpy(host->bounce_buf, host->head_pg.mapped + PAGE_SIZE - blk_head, 33675fa9ea6SGuennadi Liakhovetski blk_head); 33775fa9ea6SGuennadi Liakhovetski memcpy(host->bounce_buf + blk_head, host->pg.mapped, 33875fa9ea6SGuennadi Liakhovetski data->blksz - blk_head); 33975fa9ea6SGuennadi Liakhovetski } 34075fa9ea6SGuennadi Liakhovetski 34175fa9ea6SGuennadi Liakhovetski /* Only called for multiple block IO */ 34275fa9ea6SGuennadi Liakhovetski static void usdhi6_sg_prep(struct usdhi6_host *host) 34375fa9ea6SGuennadi Liakhovetski { 34475fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 34575fa9ea6SGuennadi Liakhovetski struct mmc_data *data = mrq->data; 34675fa9ea6SGuennadi Liakhovetski 34775fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_SECCNT, data->blocks); 34875fa9ea6SGuennadi Liakhovetski 34975fa9ea6SGuennadi Liakhovetski host->sg = data->sg; 35075fa9ea6SGuennadi Liakhovetski /* TODO: if we always map, this is redundant */ 35175fa9ea6SGuennadi Liakhovetski host->offset = host->sg->offset; 35275fa9ea6SGuennadi Liakhovetski } 35375fa9ea6SGuennadi Liakhovetski 35475fa9ea6SGuennadi Liakhovetski /* Map the first page in an SG segment: common for multiple and single block IO */ 35575fa9ea6SGuennadi Liakhovetski static void *usdhi6_sg_map(struct usdhi6_host *host) 35675fa9ea6SGuennadi Liakhovetski { 35775fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 35875fa9ea6SGuennadi Liakhovetski struct scatterlist *sg = data->sg_len > 1 ? host->sg : data->sg; 35975fa9ea6SGuennadi Liakhovetski size_t head = PAGE_SIZE - sg->offset; 36075fa9ea6SGuennadi Liakhovetski size_t blk_head = head % data->blksz; 36175fa9ea6SGuennadi Liakhovetski 36275fa9ea6SGuennadi Liakhovetski WARN(host->pg.page, "%p not properly unmapped!\n", host->pg.page); 36375fa9ea6SGuennadi Liakhovetski if (WARN(sg_dma_len(sg) % data->blksz, 36413fe0ec3SGuennadi Liakhovetski "SG size %u isn't a multiple of block size %u\n", 36575fa9ea6SGuennadi Liakhovetski sg_dma_len(sg), data->blksz)) 36675fa9ea6SGuennadi Liakhovetski return NULL; 36775fa9ea6SGuennadi Liakhovetski 36875fa9ea6SGuennadi Liakhovetski host->pg.page = sg_page(sg); 36975fa9ea6SGuennadi Liakhovetski host->pg.mapped = kmap(host->pg.page); 37075fa9ea6SGuennadi Liakhovetski host->offset = sg->offset; 37175fa9ea6SGuennadi Liakhovetski 37275fa9ea6SGuennadi Liakhovetski /* 37375fa9ea6SGuennadi Liakhovetski * Block size must be a power of 2 for multi-block transfers, 37475fa9ea6SGuennadi Liakhovetski * therefore blk_head is equal for all pages in this SG 37575fa9ea6SGuennadi Liakhovetski */ 37675fa9ea6SGuennadi Liakhovetski host->head_len = blk_head; 37775fa9ea6SGuennadi Liakhovetski 37875fa9ea6SGuennadi Liakhovetski if (head < data->blksz) 37975fa9ea6SGuennadi Liakhovetski /* 38075fa9ea6SGuennadi Liakhovetski * The first block in the SG crosses a page boundary. 38175fa9ea6SGuennadi Liakhovetski * Max blksz = 512, so blocks can only span 2 pages 38275fa9ea6SGuennadi Liakhovetski */ 38375fa9ea6SGuennadi Liakhovetski usdhi6_blk_bounce(host, sg); 38475fa9ea6SGuennadi Liakhovetski else 38575fa9ea6SGuennadi Liakhovetski host->blk_page = host->pg.mapped; 38675fa9ea6SGuennadi Liakhovetski 38775fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "Mapped %p (%lx) at %p + %u for CMD%u @ 0x%p\n", 38875fa9ea6SGuennadi Liakhovetski host->pg.page, page_to_pfn(host->pg.page), host->pg.mapped, 38975fa9ea6SGuennadi Liakhovetski sg->offset, host->mrq->cmd->opcode, host->mrq); 39075fa9ea6SGuennadi Liakhovetski 39175fa9ea6SGuennadi Liakhovetski return host->blk_page + host->offset; 39275fa9ea6SGuennadi Liakhovetski } 39375fa9ea6SGuennadi Liakhovetski 39475fa9ea6SGuennadi Liakhovetski /* Unmap the current page: common for multiple and single block IO */ 39575fa9ea6SGuennadi Liakhovetski static void usdhi6_sg_unmap(struct usdhi6_host *host, bool force) 39675fa9ea6SGuennadi Liakhovetski { 39775fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 39875fa9ea6SGuennadi Liakhovetski struct page *page = host->head_pg.page; 39975fa9ea6SGuennadi Liakhovetski 40075fa9ea6SGuennadi Liakhovetski if (page) { 40175fa9ea6SGuennadi Liakhovetski /* Previous block was cross-page boundary */ 40275fa9ea6SGuennadi Liakhovetski struct scatterlist *sg = data->sg_len > 1 ? 40375fa9ea6SGuennadi Liakhovetski host->sg : data->sg; 40475fa9ea6SGuennadi Liakhovetski size_t blk_head = host->head_len; 40575fa9ea6SGuennadi Liakhovetski 40675fa9ea6SGuennadi Liakhovetski if (!data->error && data->flags & MMC_DATA_READ) { 40775fa9ea6SGuennadi Liakhovetski memcpy(host->head_pg.mapped + PAGE_SIZE - blk_head, 40875fa9ea6SGuennadi Liakhovetski host->bounce_buf, blk_head); 40975fa9ea6SGuennadi Liakhovetski memcpy(host->pg.mapped, host->bounce_buf + blk_head, 41075fa9ea6SGuennadi Liakhovetski data->blksz - blk_head); 41175fa9ea6SGuennadi Liakhovetski } 41275fa9ea6SGuennadi Liakhovetski 41375fa9ea6SGuennadi Liakhovetski flush_dcache_page(page); 41475fa9ea6SGuennadi Liakhovetski kunmap(page); 41575fa9ea6SGuennadi Liakhovetski 41675fa9ea6SGuennadi Liakhovetski host->head_pg.page = NULL; 41775fa9ea6SGuennadi Liakhovetski 41875fa9ea6SGuennadi Liakhovetski if (!force && sg_dma_len(sg) + sg->offset > 41975fa9ea6SGuennadi Liakhovetski (host->page_idx << PAGE_SHIFT) + data->blksz - blk_head) 42075fa9ea6SGuennadi Liakhovetski /* More blocks in this SG, don't unmap the next page */ 42175fa9ea6SGuennadi Liakhovetski return; 42275fa9ea6SGuennadi Liakhovetski } 42375fa9ea6SGuennadi Liakhovetski 42475fa9ea6SGuennadi Liakhovetski page = host->pg.page; 42575fa9ea6SGuennadi Liakhovetski if (!page) 42675fa9ea6SGuennadi Liakhovetski return; 42775fa9ea6SGuennadi Liakhovetski 42875fa9ea6SGuennadi Liakhovetski flush_dcache_page(page); 42975fa9ea6SGuennadi Liakhovetski kunmap(page); 43075fa9ea6SGuennadi Liakhovetski 43175fa9ea6SGuennadi Liakhovetski host->pg.page = NULL; 43275fa9ea6SGuennadi Liakhovetski } 43375fa9ea6SGuennadi Liakhovetski 43475fa9ea6SGuennadi Liakhovetski /* Called from MMC_WRITE_MULTIPLE_BLOCK or MMC_READ_MULTIPLE_BLOCK */ 43575fa9ea6SGuennadi Liakhovetski static void usdhi6_sg_advance(struct usdhi6_host *host) 43675fa9ea6SGuennadi Liakhovetski { 43775fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 43875fa9ea6SGuennadi Liakhovetski size_t done, total; 43975fa9ea6SGuennadi Liakhovetski 44075fa9ea6SGuennadi Liakhovetski /* New offset: set at the end of the previous block */ 44175fa9ea6SGuennadi Liakhovetski if (host->head_pg.page) { 44275fa9ea6SGuennadi Liakhovetski /* Finished a cross-page block, jump to the new page */ 44375fa9ea6SGuennadi Liakhovetski host->page_idx++; 44475fa9ea6SGuennadi Liakhovetski host->offset = data->blksz - host->head_len; 44575fa9ea6SGuennadi Liakhovetski host->blk_page = host->pg.mapped; 44675fa9ea6SGuennadi Liakhovetski usdhi6_sg_unmap(host, false); 44775fa9ea6SGuennadi Liakhovetski } else { 44875fa9ea6SGuennadi Liakhovetski host->offset += data->blksz; 44975fa9ea6SGuennadi Liakhovetski /* The completed block didn't cross a page boundary */ 45075fa9ea6SGuennadi Liakhovetski if (host->offset == PAGE_SIZE) { 45175fa9ea6SGuennadi Liakhovetski /* If required, we'll map the page below */ 45275fa9ea6SGuennadi Liakhovetski host->offset = 0; 45375fa9ea6SGuennadi Liakhovetski host->page_idx++; 45475fa9ea6SGuennadi Liakhovetski } 45575fa9ea6SGuennadi Liakhovetski } 45675fa9ea6SGuennadi Liakhovetski 45775fa9ea6SGuennadi Liakhovetski /* 45875fa9ea6SGuennadi Liakhovetski * Now host->blk_page + host->offset point at the end of our last block 45975fa9ea6SGuennadi Liakhovetski * and host->page_idx is the index of the page, in which our new block 46075fa9ea6SGuennadi Liakhovetski * is located, if any 46175fa9ea6SGuennadi Liakhovetski */ 46275fa9ea6SGuennadi Liakhovetski 46375fa9ea6SGuennadi Liakhovetski done = (host->page_idx << PAGE_SHIFT) + host->offset; 46475fa9ea6SGuennadi Liakhovetski total = host->sg->offset + sg_dma_len(host->sg); 46575fa9ea6SGuennadi Liakhovetski 46613fe0ec3SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): %zu of %zu @ %zu\n", __func__, 46775fa9ea6SGuennadi Liakhovetski done, total, host->offset); 46875fa9ea6SGuennadi Liakhovetski 46975fa9ea6SGuennadi Liakhovetski if (done < total && host->offset) { 47075fa9ea6SGuennadi Liakhovetski /* More blocks in this page */ 47175fa9ea6SGuennadi Liakhovetski if (host->offset + data->blksz > PAGE_SIZE) 47275fa9ea6SGuennadi Liakhovetski /* We approached at a block, that spans 2 pages */ 47375fa9ea6SGuennadi Liakhovetski usdhi6_blk_bounce(host, host->sg); 47475fa9ea6SGuennadi Liakhovetski 47575fa9ea6SGuennadi Liakhovetski return; 47675fa9ea6SGuennadi Liakhovetski } 47775fa9ea6SGuennadi Liakhovetski 47875fa9ea6SGuennadi Liakhovetski /* Finished current page or an SG segment */ 47975fa9ea6SGuennadi Liakhovetski usdhi6_sg_unmap(host, false); 48075fa9ea6SGuennadi Liakhovetski 48175fa9ea6SGuennadi Liakhovetski if (done == total) { 48275fa9ea6SGuennadi Liakhovetski /* 48375fa9ea6SGuennadi Liakhovetski * End of an SG segment or the complete SG: jump to the next 48475fa9ea6SGuennadi Liakhovetski * segment, we'll map it later in usdhi6_blk_read() or 48575fa9ea6SGuennadi Liakhovetski * usdhi6_blk_write() 48675fa9ea6SGuennadi Liakhovetski */ 48775fa9ea6SGuennadi Liakhovetski struct scatterlist *next = sg_next(host->sg); 48875fa9ea6SGuennadi Liakhovetski 48975fa9ea6SGuennadi Liakhovetski host->page_idx = 0; 49075fa9ea6SGuennadi Liakhovetski 49175fa9ea6SGuennadi Liakhovetski if (!next) 49275fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_DATA_END; 49375fa9ea6SGuennadi Liakhovetski host->sg = next; 49475fa9ea6SGuennadi Liakhovetski 49575fa9ea6SGuennadi Liakhovetski if (WARN(next && sg_dma_len(next) % data->blksz, 49613fe0ec3SGuennadi Liakhovetski "SG size %u isn't a multiple of block size %u\n", 49775fa9ea6SGuennadi Liakhovetski sg_dma_len(next), data->blksz)) 49875fa9ea6SGuennadi Liakhovetski data->error = -EINVAL; 49975fa9ea6SGuennadi Liakhovetski 50075fa9ea6SGuennadi Liakhovetski return; 50175fa9ea6SGuennadi Liakhovetski } 50275fa9ea6SGuennadi Liakhovetski 50375fa9ea6SGuennadi Liakhovetski /* We cannot get here after crossing a page border */ 50475fa9ea6SGuennadi Liakhovetski 50575fa9ea6SGuennadi Liakhovetski /* Next page in the same SG */ 50675fa9ea6SGuennadi Liakhovetski host->pg.page = nth_page(sg_page(host->sg), host->page_idx); 50775fa9ea6SGuennadi Liakhovetski host->pg.mapped = kmap(host->pg.page); 50875fa9ea6SGuennadi Liakhovetski host->blk_page = host->pg.mapped; 50975fa9ea6SGuennadi Liakhovetski 51075fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "Mapped %p (%lx) at %p for CMD%u @ 0x%p\n", 51175fa9ea6SGuennadi Liakhovetski host->pg.page, page_to_pfn(host->pg.page), host->pg.mapped, 51275fa9ea6SGuennadi Liakhovetski host->mrq->cmd->opcode, host->mrq); 51375fa9ea6SGuennadi Liakhovetski } 51475fa9ea6SGuennadi Liakhovetski 51575fa9ea6SGuennadi Liakhovetski /* DMA handling */ 51675fa9ea6SGuennadi Liakhovetski 51775fa9ea6SGuennadi Liakhovetski static void usdhi6_dma_release(struct usdhi6_host *host) 51875fa9ea6SGuennadi Liakhovetski { 51975fa9ea6SGuennadi Liakhovetski host->dma_active = false; 52075fa9ea6SGuennadi Liakhovetski if (host->chan_tx) { 52175fa9ea6SGuennadi Liakhovetski struct dma_chan *chan = host->chan_tx; 52275fa9ea6SGuennadi Liakhovetski host->chan_tx = NULL; 52375fa9ea6SGuennadi Liakhovetski dma_release_channel(chan); 52475fa9ea6SGuennadi Liakhovetski } 52575fa9ea6SGuennadi Liakhovetski if (host->chan_rx) { 52675fa9ea6SGuennadi Liakhovetski struct dma_chan *chan = host->chan_rx; 52775fa9ea6SGuennadi Liakhovetski host->chan_rx = NULL; 52875fa9ea6SGuennadi Liakhovetski dma_release_channel(chan); 52975fa9ea6SGuennadi Liakhovetski } 53075fa9ea6SGuennadi Liakhovetski } 53175fa9ea6SGuennadi Liakhovetski 53275fa9ea6SGuennadi Liakhovetski static void usdhi6_dma_stop_unmap(struct usdhi6_host *host) 53375fa9ea6SGuennadi Liakhovetski { 53475fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 53575fa9ea6SGuennadi Liakhovetski 53675fa9ea6SGuennadi Liakhovetski if (!host->dma_active) 53775fa9ea6SGuennadi Liakhovetski return; 53875fa9ea6SGuennadi Liakhovetski 53975fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_CC_EXT_MODE, 0); 54075fa9ea6SGuennadi Liakhovetski host->dma_active = false; 54175fa9ea6SGuennadi Liakhovetski 54275fa9ea6SGuennadi Liakhovetski if (data->flags & MMC_DATA_READ) 54375fa9ea6SGuennadi Liakhovetski dma_unmap_sg(host->chan_rx->device->dev, data->sg, 54475fa9ea6SGuennadi Liakhovetski data->sg_len, DMA_FROM_DEVICE); 54575fa9ea6SGuennadi Liakhovetski else 54675fa9ea6SGuennadi Liakhovetski dma_unmap_sg(host->chan_tx->device->dev, data->sg, 54775fa9ea6SGuennadi Liakhovetski data->sg_len, DMA_TO_DEVICE); 54875fa9ea6SGuennadi Liakhovetski } 54975fa9ea6SGuennadi Liakhovetski 55075fa9ea6SGuennadi Liakhovetski static void usdhi6_dma_complete(void *arg) 55175fa9ea6SGuennadi Liakhovetski { 55275fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = arg; 55375fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 55475fa9ea6SGuennadi Liakhovetski 55575fa9ea6SGuennadi Liakhovetski if (WARN(!mrq || !mrq->data, "%s: NULL data in DMA completion for %p!\n", 55675fa9ea6SGuennadi Liakhovetski dev_name(mmc_dev(host->mmc)), mrq)) 55775fa9ea6SGuennadi Liakhovetski return; 55875fa9ea6SGuennadi Liakhovetski 55975fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): CMD%u DMA completed\n", __func__, 56075fa9ea6SGuennadi Liakhovetski mrq->cmd->opcode); 56175fa9ea6SGuennadi Liakhovetski 56275fa9ea6SGuennadi Liakhovetski usdhi6_dma_stop_unmap(host); 56375fa9ea6SGuennadi Liakhovetski usdhi6_wait_for_brwe(host, mrq->data->flags & MMC_DATA_READ); 56475fa9ea6SGuennadi Liakhovetski } 56575fa9ea6SGuennadi Liakhovetski 56675fa9ea6SGuennadi Liakhovetski static int usdhi6_dma_setup(struct usdhi6_host *host, struct dma_chan *chan, 56775fa9ea6SGuennadi Liakhovetski enum dma_transfer_direction dir) 56875fa9ea6SGuennadi Liakhovetski { 56975fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 57075fa9ea6SGuennadi Liakhovetski struct scatterlist *sg = data->sg; 57175fa9ea6SGuennadi Liakhovetski struct dma_async_tx_descriptor *desc = NULL; 57275fa9ea6SGuennadi Liakhovetski dma_cookie_t cookie = -EINVAL; 57375fa9ea6SGuennadi Liakhovetski enum dma_data_direction data_dir; 57475fa9ea6SGuennadi Liakhovetski int ret; 57575fa9ea6SGuennadi Liakhovetski 57675fa9ea6SGuennadi Liakhovetski switch (dir) { 57775fa9ea6SGuennadi Liakhovetski case DMA_MEM_TO_DEV: 57875fa9ea6SGuennadi Liakhovetski data_dir = DMA_TO_DEVICE; 57975fa9ea6SGuennadi Liakhovetski break; 58075fa9ea6SGuennadi Liakhovetski case DMA_DEV_TO_MEM: 58175fa9ea6SGuennadi Liakhovetski data_dir = DMA_FROM_DEVICE; 58275fa9ea6SGuennadi Liakhovetski break; 58375fa9ea6SGuennadi Liakhovetski default: 58475fa9ea6SGuennadi Liakhovetski return -EINVAL; 58575fa9ea6SGuennadi Liakhovetski } 58675fa9ea6SGuennadi Liakhovetski 58775fa9ea6SGuennadi Liakhovetski ret = dma_map_sg(chan->device->dev, sg, data->sg_len, data_dir); 58875fa9ea6SGuennadi Liakhovetski if (ret > 0) { 58975fa9ea6SGuennadi Liakhovetski host->dma_active = true; 59075fa9ea6SGuennadi Liakhovetski desc = dmaengine_prep_slave_sg(chan, sg, ret, dir, 59175fa9ea6SGuennadi Liakhovetski DMA_PREP_INTERRUPT | DMA_CTRL_ACK); 59275fa9ea6SGuennadi Liakhovetski } 59375fa9ea6SGuennadi Liakhovetski 59475fa9ea6SGuennadi Liakhovetski if (desc) { 59575fa9ea6SGuennadi Liakhovetski desc->callback = usdhi6_dma_complete; 59675fa9ea6SGuennadi Liakhovetski desc->callback_param = host; 59775fa9ea6SGuennadi Liakhovetski cookie = dmaengine_submit(desc); 59875fa9ea6SGuennadi Liakhovetski } 59975fa9ea6SGuennadi Liakhovetski 60075fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): mapped %d -> %d, cookie %d @ %p\n", 60175fa9ea6SGuennadi Liakhovetski __func__, data->sg_len, ret, cookie, desc); 60275fa9ea6SGuennadi Liakhovetski 60375fa9ea6SGuennadi Liakhovetski if (cookie < 0) { 60475fa9ea6SGuennadi Liakhovetski /* DMA failed, fall back to PIO */ 60575fa9ea6SGuennadi Liakhovetski if (ret >= 0) 60675fa9ea6SGuennadi Liakhovetski ret = cookie; 60775fa9ea6SGuennadi Liakhovetski usdhi6_dma_release(host); 60875fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), 60975fa9ea6SGuennadi Liakhovetski "DMA failed: %d, falling back to PIO\n", ret); 61075fa9ea6SGuennadi Liakhovetski } 61175fa9ea6SGuennadi Liakhovetski 61275fa9ea6SGuennadi Liakhovetski return cookie; 61375fa9ea6SGuennadi Liakhovetski } 61475fa9ea6SGuennadi Liakhovetski 61575fa9ea6SGuennadi Liakhovetski static int usdhi6_dma_start(struct usdhi6_host *host) 61675fa9ea6SGuennadi Liakhovetski { 61775fa9ea6SGuennadi Liakhovetski if (!host->chan_rx || !host->chan_tx) 61875fa9ea6SGuennadi Liakhovetski return -ENODEV; 61975fa9ea6SGuennadi Liakhovetski 62075fa9ea6SGuennadi Liakhovetski if (host->mrq->data->flags & MMC_DATA_READ) 62175fa9ea6SGuennadi Liakhovetski return usdhi6_dma_setup(host, host->chan_rx, DMA_DEV_TO_MEM); 62275fa9ea6SGuennadi Liakhovetski 62375fa9ea6SGuennadi Liakhovetski return usdhi6_dma_setup(host, host->chan_tx, DMA_MEM_TO_DEV); 62475fa9ea6SGuennadi Liakhovetski } 62575fa9ea6SGuennadi Liakhovetski 62675fa9ea6SGuennadi Liakhovetski static void usdhi6_dma_kill(struct usdhi6_host *host) 62775fa9ea6SGuennadi Liakhovetski { 62875fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 62975fa9ea6SGuennadi Liakhovetski 63075fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): SG of %u: %ux%u\n", 63175fa9ea6SGuennadi Liakhovetski __func__, data->sg_len, data->blocks, data->blksz); 63275fa9ea6SGuennadi Liakhovetski /* Abort DMA */ 63375fa9ea6SGuennadi Liakhovetski if (data->flags & MMC_DATA_READ) 63429cef6d4SWolfram Sang dmaengine_terminate_sync(host->chan_rx); 63575fa9ea6SGuennadi Liakhovetski else 63629cef6d4SWolfram Sang dmaengine_terminate_sync(host->chan_tx); 63775fa9ea6SGuennadi Liakhovetski } 63875fa9ea6SGuennadi Liakhovetski 63975fa9ea6SGuennadi Liakhovetski static void usdhi6_dma_check_error(struct usdhi6_host *host) 64075fa9ea6SGuennadi Liakhovetski { 64175fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 64275fa9ea6SGuennadi Liakhovetski 64375fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): IO error %d, status 0x%x\n", 64475fa9ea6SGuennadi Liakhovetski __func__, host->io_error, usdhi6_read(host, USDHI6_SD_INFO1)); 64575fa9ea6SGuennadi Liakhovetski 64675fa9ea6SGuennadi Liakhovetski if (host->io_error) { 64775fa9ea6SGuennadi Liakhovetski data->error = usdhi6_error_code(host); 64875fa9ea6SGuennadi Liakhovetski data->bytes_xfered = 0; 64975fa9ea6SGuennadi Liakhovetski usdhi6_dma_kill(host); 65075fa9ea6SGuennadi Liakhovetski usdhi6_dma_release(host); 65175fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), 65275fa9ea6SGuennadi Liakhovetski "DMA failed: %d, falling back to PIO\n", data->error); 65375fa9ea6SGuennadi Liakhovetski return; 65475fa9ea6SGuennadi Liakhovetski } 65575fa9ea6SGuennadi Liakhovetski 65675fa9ea6SGuennadi Liakhovetski /* 65775fa9ea6SGuennadi Liakhovetski * The datasheet tells us to check a response from the card, whereas 65875fa9ea6SGuennadi Liakhovetski * responses only come after the command phase, not after the data 65975fa9ea6SGuennadi Liakhovetski * phase. Let's check anyway. 66075fa9ea6SGuennadi Liakhovetski */ 66175fa9ea6SGuennadi Liakhovetski if (host->irq_status & USDHI6_SD_INFO1_RSP_END) 66275fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), "Unexpected response received!\n"); 66375fa9ea6SGuennadi Liakhovetski } 66475fa9ea6SGuennadi Liakhovetski 66575fa9ea6SGuennadi Liakhovetski static void usdhi6_dma_kick(struct usdhi6_host *host) 66675fa9ea6SGuennadi Liakhovetski { 66775fa9ea6SGuennadi Liakhovetski if (host->mrq->data->flags & MMC_DATA_READ) 66875fa9ea6SGuennadi Liakhovetski dma_async_issue_pending(host->chan_rx); 66975fa9ea6SGuennadi Liakhovetski else 67075fa9ea6SGuennadi Liakhovetski dma_async_issue_pending(host->chan_tx); 67175fa9ea6SGuennadi Liakhovetski } 67275fa9ea6SGuennadi Liakhovetski 67375fa9ea6SGuennadi Liakhovetski static void usdhi6_dma_request(struct usdhi6_host *host, phys_addr_t start) 67475fa9ea6SGuennadi Liakhovetski { 67575fa9ea6SGuennadi Liakhovetski struct dma_slave_config cfg = { 67675fa9ea6SGuennadi Liakhovetski .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 67775fa9ea6SGuennadi Liakhovetski .dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, 67875fa9ea6SGuennadi Liakhovetski }; 67975fa9ea6SGuennadi Liakhovetski int ret; 68075fa9ea6SGuennadi Liakhovetski 681e6d82a7bSPeter Ujfalusi host->chan_tx = dma_request_chan(mmc_dev(host->mmc), "tx"); 68275fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s: TX: got channel %p\n", __func__, 68375fa9ea6SGuennadi Liakhovetski host->chan_tx); 68475fa9ea6SGuennadi Liakhovetski 685e6d82a7bSPeter Ujfalusi if (IS_ERR(host->chan_tx)) { 686e6d82a7bSPeter Ujfalusi host->chan_tx = NULL; 68775fa9ea6SGuennadi Liakhovetski return; 688e6d82a7bSPeter Ujfalusi } 68975fa9ea6SGuennadi Liakhovetski 69075fa9ea6SGuennadi Liakhovetski cfg.direction = DMA_MEM_TO_DEV; 69175fa9ea6SGuennadi Liakhovetski cfg.dst_addr = start + USDHI6_SD_BUF0; 69275fa9ea6SGuennadi Liakhovetski cfg.dst_maxburst = 128; /* 128 words * 4 bytes = 512 bytes */ 69375fa9ea6SGuennadi Liakhovetski cfg.src_addr = 0; 69475fa9ea6SGuennadi Liakhovetski ret = dmaengine_slave_config(host->chan_tx, &cfg); 69575fa9ea6SGuennadi Liakhovetski if (ret < 0) 69675fa9ea6SGuennadi Liakhovetski goto e_release_tx; 69775fa9ea6SGuennadi Liakhovetski 698e6d82a7bSPeter Ujfalusi host->chan_rx = dma_request_chan(mmc_dev(host->mmc), "rx"); 69975fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s: RX: got channel %p\n", __func__, 70075fa9ea6SGuennadi Liakhovetski host->chan_rx); 70175fa9ea6SGuennadi Liakhovetski 702e6d82a7bSPeter Ujfalusi if (IS_ERR(host->chan_rx)) { 703e6d82a7bSPeter Ujfalusi host->chan_rx = NULL; 70475fa9ea6SGuennadi Liakhovetski goto e_release_tx; 705e6d82a7bSPeter Ujfalusi } 70675fa9ea6SGuennadi Liakhovetski 70775fa9ea6SGuennadi Liakhovetski cfg.direction = DMA_DEV_TO_MEM; 70875fa9ea6SGuennadi Liakhovetski cfg.src_addr = cfg.dst_addr; 70975fa9ea6SGuennadi Liakhovetski cfg.src_maxburst = 128; /* 128 words * 4 bytes = 512 bytes */ 71075fa9ea6SGuennadi Liakhovetski cfg.dst_addr = 0; 71175fa9ea6SGuennadi Liakhovetski ret = dmaengine_slave_config(host->chan_rx, &cfg); 71275fa9ea6SGuennadi Liakhovetski if (ret < 0) 71375fa9ea6SGuennadi Liakhovetski goto e_release_rx; 71475fa9ea6SGuennadi Liakhovetski 71575fa9ea6SGuennadi Liakhovetski return; 71675fa9ea6SGuennadi Liakhovetski 71775fa9ea6SGuennadi Liakhovetski e_release_rx: 71875fa9ea6SGuennadi Liakhovetski dma_release_channel(host->chan_rx); 71975fa9ea6SGuennadi Liakhovetski host->chan_rx = NULL; 72075fa9ea6SGuennadi Liakhovetski e_release_tx: 72175fa9ea6SGuennadi Liakhovetski dma_release_channel(host->chan_tx); 72275fa9ea6SGuennadi Liakhovetski host->chan_tx = NULL; 72375fa9ea6SGuennadi Liakhovetski } 72475fa9ea6SGuennadi Liakhovetski 72575fa9ea6SGuennadi Liakhovetski /* API helpers */ 72675fa9ea6SGuennadi Liakhovetski 72775fa9ea6SGuennadi Liakhovetski static void usdhi6_clk_set(struct usdhi6_host *host, struct mmc_ios *ios) 72875fa9ea6SGuennadi Liakhovetski { 72975fa9ea6SGuennadi Liakhovetski unsigned long rate = ios->clock; 73075fa9ea6SGuennadi Liakhovetski u32 val; 73175fa9ea6SGuennadi Liakhovetski unsigned int i; 73275fa9ea6SGuennadi Liakhovetski 73375fa9ea6SGuennadi Liakhovetski for (i = 1000; i; i--) { 73475fa9ea6SGuennadi Liakhovetski if (usdhi6_read(host, USDHI6_SD_INFO2) & USDHI6_SD_INFO2_SCLKDIVEN) 73575fa9ea6SGuennadi Liakhovetski break; 73675fa9ea6SGuennadi Liakhovetski usleep_range(10, 100); 73775fa9ea6SGuennadi Liakhovetski } 73875fa9ea6SGuennadi Liakhovetski 73975fa9ea6SGuennadi Liakhovetski if (!i) { 74075fa9ea6SGuennadi Liakhovetski dev_err(mmc_dev(host->mmc), "SD bus busy, clock set aborted\n"); 74175fa9ea6SGuennadi Liakhovetski return; 74275fa9ea6SGuennadi Liakhovetski } 74375fa9ea6SGuennadi Liakhovetski 74475fa9ea6SGuennadi Liakhovetski val = usdhi6_read(host, USDHI6_SD_CLK_CTRL) & ~USDHI6_SD_CLK_CTRL_DIV_MASK; 74575fa9ea6SGuennadi Liakhovetski 74675fa9ea6SGuennadi Liakhovetski if (rate) { 74775fa9ea6SGuennadi Liakhovetski unsigned long new_rate; 74875fa9ea6SGuennadi Liakhovetski 74975fa9ea6SGuennadi Liakhovetski if (host->imclk <= rate) { 75075fa9ea6SGuennadi Liakhovetski if (ios->timing != MMC_TIMING_UHS_DDR50) { 75175fa9ea6SGuennadi Liakhovetski /* Cannot have 1-to-1 clock in DDR mode */ 75275fa9ea6SGuennadi Liakhovetski new_rate = host->imclk; 75375fa9ea6SGuennadi Liakhovetski val |= 0xff; 75475fa9ea6SGuennadi Liakhovetski } else { 75575fa9ea6SGuennadi Liakhovetski new_rate = host->imclk / 2; 75675fa9ea6SGuennadi Liakhovetski } 75775fa9ea6SGuennadi Liakhovetski } else { 75875fa9ea6SGuennadi Liakhovetski unsigned long div = 75975fa9ea6SGuennadi Liakhovetski roundup_pow_of_two(DIV_ROUND_UP(host->imclk, rate)); 76075fa9ea6SGuennadi Liakhovetski val |= div >> 2; 76175fa9ea6SGuennadi Liakhovetski new_rate = host->imclk / div; 76275fa9ea6SGuennadi Liakhovetski } 76375fa9ea6SGuennadi Liakhovetski 76475fa9ea6SGuennadi Liakhovetski if (host->rate == new_rate) 76575fa9ea6SGuennadi Liakhovetski return; 76675fa9ea6SGuennadi Liakhovetski 76775fa9ea6SGuennadi Liakhovetski host->rate = new_rate; 76875fa9ea6SGuennadi Liakhovetski 76975fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "target %lu, div %u, set %lu\n", 77075fa9ea6SGuennadi Liakhovetski rate, (val & 0xff) << 2, new_rate); 77175fa9ea6SGuennadi Liakhovetski } 77275fa9ea6SGuennadi Liakhovetski 77375fa9ea6SGuennadi Liakhovetski /* 77475fa9ea6SGuennadi Liakhovetski * if old or new rate is equal to input rate, have to switch the clock 77575fa9ea6SGuennadi Liakhovetski * off before changing and on after 77675fa9ea6SGuennadi Liakhovetski */ 77775fa9ea6SGuennadi Liakhovetski if (host->imclk == rate || host->imclk == host->rate || !rate) 77875fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_CLK_CTRL, 77975fa9ea6SGuennadi Liakhovetski val & ~USDHI6_SD_CLK_CTRL_SCLKEN); 78075fa9ea6SGuennadi Liakhovetski 78175fa9ea6SGuennadi Liakhovetski if (!rate) { 78275fa9ea6SGuennadi Liakhovetski host->rate = 0; 78375fa9ea6SGuennadi Liakhovetski return; 78475fa9ea6SGuennadi Liakhovetski } 78575fa9ea6SGuennadi Liakhovetski 78675fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_CLK_CTRL, val); 78775fa9ea6SGuennadi Liakhovetski 78875fa9ea6SGuennadi Liakhovetski if (host->imclk == rate || host->imclk == host->rate || 78975fa9ea6SGuennadi Liakhovetski !(val & USDHI6_SD_CLK_CTRL_SCLKEN)) 79075fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_CLK_CTRL, 79175fa9ea6SGuennadi Liakhovetski val | USDHI6_SD_CLK_CTRL_SCLKEN); 79275fa9ea6SGuennadi Liakhovetski } 79375fa9ea6SGuennadi Liakhovetski 79475fa9ea6SGuennadi Liakhovetski static void usdhi6_set_power(struct usdhi6_host *host, struct mmc_ios *ios) 79575fa9ea6SGuennadi Liakhovetski { 79675fa9ea6SGuennadi Liakhovetski struct mmc_host *mmc = host->mmc; 79775fa9ea6SGuennadi Liakhovetski 79875fa9ea6SGuennadi Liakhovetski if (!IS_ERR(mmc->supply.vmmc)) 79975fa9ea6SGuennadi Liakhovetski /* Errors ignored... */ 80075fa9ea6SGuennadi Liakhovetski mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 80175fa9ea6SGuennadi Liakhovetski ios->power_mode ? ios->vdd : 0); 80275fa9ea6SGuennadi Liakhovetski } 80375fa9ea6SGuennadi Liakhovetski 80475fa9ea6SGuennadi Liakhovetski static int usdhi6_reset(struct usdhi6_host *host) 80575fa9ea6SGuennadi Liakhovetski { 80675fa9ea6SGuennadi Liakhovetski int i; 80775fa9ea6SGuennadi Liakhovetski 80875fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SOFT_RST, USDHI6_SOFT_RST_RESERVED); 80975fa9ea6SGuennadi Liakhovetski cpu_relax(); 81075fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SOFT_RST, USDHI6_SOFT_RST_RESERVED | USDHI6_SOFT_RST_RESET); 81175fa9ea6SGuennadi Liakhovetski for (i = 1000; i; i--) 81275fa9ea6SGuennadi Liakhovetski if (usdhi6_read(host, USDHI6_SOFT_RST) & USDHI6_SOFT_RST_RESET) 81375fa9ea6SGuennadi Liakhovetski break; 81475fa9ea6SGuennadi Liakhovetski 81575fa9ea6SGuennadi Liakhovetski return i ? 0 : -ETIMEDOUT; 81675fa9ea6SGuennadi Liakhovetski } 81775fa9ea6SGuennadi Liakhovetski 81875fa9ea6SGuennadi Liakhovetski static void usdhi6_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 81975fa9ea6SGuennadi Liakhovetski { 82075fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = mmc_priv(mmc); 82175fa9ea6SGuennadi Liakhovetski u32 option, mode; 82275fa9ea6SGuennadi Liakhovetski int ret; 82375fa9ea6SGuennadi Liakhovetski 82475fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(mmc), "%uHz, OCR: %u, power %u, bus-width %u, timing %u\n", 82575fa9ea6SGuennadi Liakhovetski ios->clock, ios->vdd, ios->power_mode, ios->bus_width, ios->timing); 82675fa9ea6SGuennadi Liakhovetski 82775fa9ea6SGuennadi Liakhovetski switch (ios->power_mode) { 82875fa9ea6SGuennadi Liakhovetski case MMC_POWER_OFF: 82975fa9ea6SGuennadi Liakhovetski usdhi6_set_power(host, ios); 83075fa9ea6SGuennadi Liakhovetski usdhi6_only_cd(host); 83175fa9ea6SGuennadi Liakhovetski break; 83275fa9ea6SGuennadi Liakhovetski case MMC_POWER_UP: 83375fa9ea6SGuennadi Liakhovetski /* 83475fa9ea6SGuennadi Liakhovetski * We only also touch USDHI6_SD_OPTION from .request(), which 83575fa9ea6SGuennadi Liakhovetski * cannot race with MMC_POWER_UP 83675fa9ea6SGuennadi Liakhovetski */ 83775fa9ea6SGuennadi Liakhovetski ret = usdhi6_reset(host); 83875fa9ea6SGuennadi Liakhovetski if (ret < 0) { 83975fa9ea6SGuennadi Liakhovetski dev_err(mmc_dev(mmc), "Cannot reset the interface!\n"); 84075fa9ea6SGuennadi Liakhovetski } else { 84175fa9ea6SGuennadi Liakhovetski usdhi6_set_power(host, ios); 84275fa9ea6SGuennadi Liakhovetski usdhi6_only_cd(host); 84375fa9ea6SGuennadi Liakhovetski } 84475fa9ea6SGuennadi Liakhovetski break; 84575fa9ea6SGuennadi Liakhovetski case MMC_POWER_ON: 84675fa9ea6SGuennadi Liakhovetski option = usdhi6_read(host, USDHI6_SD_OPTION); 84775fa9ea6SGuennadi Liakhovetski /* 84875fa9ea6SGuennadi Liakhovetski * The eMMC standard only allows 4 or 8 bits in the DDR mode, 84975fa9ea6SGuennadi Liakhovetski * the same probably holds for SD cards. We check here anyway, 85075fa9ea6SGuennadi Liakhovetski * since the datasheet explicitly requires 4 bits for DDR. 85175fa9ea6SGuennadi Liakhovetski */ 85275fa9ea6SGuennadi Liakhovetski if (ios->bus_width == MMC_BUS_WIDTH_1) { 85375fa9ea6SGuennadi Liakhovetski if (ios->timing == MMC_TIMING_UHS_DDR50) 85475fa9ea6SGuennadi Liakhovetski dev_err(mmc_dev(mmc), 85575fa9ea6SGuennadi Liakhovetski "4 bits are required for DDR\n"); 85675fa9ea6SGuennadi Liakhovetski option |= USDHI6_SD_OPTION_WIDTH_1; 85775fa9ea6SGuennadi Liakhovetski mode = 0; 85875fa9ea6SGuennadi Liakhovetski } else { 85975fa9ea6SGuennadi Liakhovetski option &= ~USDHI6_SD_OPTION_WIDTH_1; 86075fa9ea6SGuennadi Liakhovetski mode = ios->timing == MMC_TIMING_UHS_DDR50; 86175fa9ea6SGuennadi Liakhovetski } 86275fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_OPTION, option); 86375fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SDIF_MODE, mode); 86475fa9ea6SGuennadi Liakhovetski break; 86575fa9ea6SGuennadi Liakhovetski } 86675fa9ea6SGuennadi Liakhovetski 86775fa9ea6SGuennadi Liakhovetski if (host->rate != ios->clock) 86875fa9ea6SGuennadi Liakhovetski usdhi6_clk_set(host, ios); 86975fa9ea6SGuennadi Liakhovetski } 87075fa9ea6SGuennadi Liakhovetski 87175fa9ea6SGuennadi Liakhovetski /* This is data timeout. Response timeout is fixed to 640 clock cycles */ 87275fa9ea6SGuennadi Liakhovetski static void usdhi6_timeout_set(struct usdhi6_host *host) 87375fa9ea6SGuennadi Liakhovetski { 87475fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 87575fa9ea6SGuennadi Liakhovetski u32 val; 87675fa9ea6SGuennadi Liakhovetski unsigned long ticks; 87775fa9ea6SGuennadi Liakhovetski 87875fa9ea6SGuennadi Liakhovetski if (!mrq->data) 87975fa9ea6SGuennadi Liakhovetski ticks = host->rate / 1000 * mrq->cmd->busy_timeout; 88075fa9ea6SGuennadi Liakhovetski else 88175fa9ea6SGuennadi Liakhovetski ticks = host->rate / 1000000 * (mrq->data->timeout_ns / 1000) + 88275fa9ea6SGuennadi Liakhovetski mrq->data->timeout_clks; 88375fa9ea6SGuennadi Liakhovetski 88475fa9ea6SGuennadi Liakhovetski if (!ticks || ticks > 1 << 27) 88575fa9ea6SGuennadi Liakhovetski /* Max timeout */ 88675fa9ea6SGuennadi Liakhovetski val = 14; 88775fa9ea6SGuennadi Liakhovetski else if (ticks < 1 << 13) 88875fa9ea6SGuennadi Liakhovetski /* Min timeout */ 88975fa9ea6SGuennadi Liakhovetski val = 0; 89075fa9ea6SGuennadi Liakhovetski else 89175fa9ea6SGuennadi Liakhovetski val = order_base_2(ticks) - 13; 89275fa9ea6SGuennadi Liakhovetski 89375fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "Set %s timeout %lu ticks @ %lu Hz\n", 89475fa9ea6SGuennadi Liakhovetski mrq->data ? "data" : "cmd", ticks, host->rate); 89575fa9ea6SGuennadi Liakhovetski 89675fa9ea6SGuennadi Liakhovetski /* Timeout Counter mask: 0xf0 */ 89775fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_OPTION, (val << USDHI6_SD_OPTION_TIMEOUT_SHIFT) | 89875fa9ea6SGuennadi Liakhovetski (usdhi6_read(host, USDHI6_SD_OPTION) & ~USDHI6_SD_OPTION_TIMEOUT_MASK)); 89975fa9ea6SGuennadi Liakhovetski } 90075fa9ea6SGuennadi Liakhovetski 90175fa9ea6SGuennadi Liakhovetski static void usdhi6_request_done(struct usdhi6_host *host) 90275fa9ea6SGuennadi Liakhovetski { 90375fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 90475fa9ea6SGuennadi Liakhovetski struct mmc_data *data = mrq->data; 90575fa9ea6SGuennadi Liakhovetski 90675fa9ea6SGuennadi Liakhovetski if (WARN(host->pg.page || host->head_pg.page, 90713fe0ec3SGuennadi Liakhovetski "Page %p or %p not unmapped: wait %u, CMD%d(%c) @ +0x%zx %ux%u in SG%u!\n", 90875fa9ea6SGuennadi Liakhovetski host->pg.page, host->head_pg.page, host->wait, mrq->cmd->opcode, 90975fa9ea6SGuennadi Liakhovetski data ? (data->flags & MMC_DATA_READ ? 'R' : 'W') : '-', 91075fa9ea6SGuennadi Liakhovetski data ? host->offset : 0, data ? data->blocks : 0, 91175fa9ea6SGuennadi Liakhovetski data ? data->blksz : 0, data ? data->sg_len : 0)) 91275fa9ea6SGuennadi Liakhovetski usdhi6_sg_unmap(host, true); 91375fa9ea6SGuennadi Liakhovetski 91475fa9ea6SGuennadi Liakhovetski if (mrq->cmd->error || 91575fa9ea6SGuennadi Liakhovetski (data && data->error) || 91675fa9ea6SGuennadi Liakhovetski (mrq->stop && mrq->stop->error)) 91775fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(CMD%d: %ux%u): err %d %d %d\n", 91875fa9ea6SGuennadi Liakhovetski __func__, mrq->cmd->opcode, data ? data->blocks : 0, 91975fa9ea6SGuennadi Liakhovetski data ? data->blksz : 0, 92075fa9ea6SGuennadi Liakhovetski mrq->cmd->error, 92175fa9ea6SGuennadi Liakhovetski data ? data->error : 1, 92275fa9ea6SGuennadi Liakhovetski mrq->stop ? mrq->stop->error : 1); 92375fa9ea6SGuennadi Liakhovetski 92475fa9ea6SGuennadi Liakhovetski /* Disable DMA */ 92575fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_CC_EXT_MODE, 0); 92675fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_REQUEST; 92775fa9ea6SGuennadi Liakhovetski host->mrq = NULL; 92875fa9ea6SGuennadi Liakhovetski 92975fa9ea6SGuennadi Liakhovetski mmc_request_done(host->mmc, mrq); 93075fa9ea6SGuennadi Liakhovetski } 93175fa9ea6SGuennadi Liakhovetski 93275fa9ea6SGuennadi Liakhovetski static int usdhi6_cmd_flags(struct usdhi6_host *host) 93375fa9ea6SGuennadi Liakhovetski { 93475fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 93575fa9ea6SGuennadi Liakhovetski struct mmc_command *cmd = mrq->cmd; 93675fa9ea6SGuennadi Liakhovetski u16 opc = cmd->opcode; 93775fa9ea6SGuennadi Liakhovetski 93875fa9ea6SGuennadi Liakhovetski if (host->app_cmd) { 93975fa9ea6SGuennadi Liakhovetski host->app_cmd = false; 94075fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_APP; 94175fa9ea6SGuennadi Liakhovetski } 94275fa9ea6SGuennadi Liakhovetski 94375fa9ea6SGuennadi Liakhovetski if (mrq->data) { 94475fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_DATA; 94575fa9ea6SGuennadi Liakhovetski 94675fa9ea6SGuennadi Liakhovetski if (mrq->data->flags & MMC_DATA_READ) 94775fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_READ; 94875fa9ea6SGuennadi Liakhovetski 94975fa9ea6SGuennadi Liakhovetski if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || 95075fa9ea6SGuennadi Liakhovetski cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || 95175fa9ea6SGuennadi Liakhovetski (cmd->opcode == SD_IO_RW_EXTENDED && 95275fa9ea6SGuennadi Liakhovetski mrq->data->blocks > 1)) { 95375fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_MULTI; 95475fa9ea6SGuennadi Liakhovetski if (!mrq->stop) 95575fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_CMD12_AUTO_OFF; 95675fa9ea6SGuennadi Liakhovetski } 95775fa9ea6SGuennadi Liakhovetski 95875fa9ea6SGuennadi Liakhovetski switch (mmc_resp_type(cmd)) { 95975fa9ea6SGuennadi Liakhovetski case MMC_RSP_NONE: 96075fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_MODE_RSP_NONE; 96175fa9ea6SGuennadi Liakhovetski break; 96275fa9ea6SGuennadi Liakhovetski case MMC_RSP_R1: 96375fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_MODE_RSP_R1; 96475fa9ea6SGuennadi Liakhovetski break; 96575fa9ea6SGuennadi Liakhovetski case MMC_RSP_R1B: 96675fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_MODE_RSP_R1B; 96775fa9ea6SGuennadi Liakhovetski break; 96875fa9ea6SGuennadi Liakhovetski case MMC_RSP_R2: 96975fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_MODE_RSP_R2; 97075fa9ea6SGuennadi Liakhovetski break; 97175fa9ea6SGuennadi Liakhovetski case MMC_RSP_R3: 97275fa9ea6SGuennadi Liakhovetski opc |= USDHI6_SD_CMD_MODE_RSP_R3; 97375fa9ea6SGuennadi Liakhovetski break; 97475fa9ea6SGuennadi Liakhovetski default: 97575fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), 97675fa9ea6SGuennadi Liakhovetski "Unknown response type %d\n", 97775fa9ea6SGuennadi Liakhovetski mmc_resp_type(cmd)); 97875fa9ea6SGuennadi Liakhovetski return -EINVAL; 97975fa9ea6SGuennadi Liakhovetski } 98075fa9ea6SGuennadi Liakhovetski } 98175fa9ea6SGuennadi Liakhovetski 98275fa9ea6SGuennadi Liakhovetski return opc; 98375fa9ea6SGuennadi Liakhovetski } 98475fa9ea6SGuennadi Liakhovetski 98575fa9ea6SGuennadi Liakhovetski static int usdhi6_rq_start(struct usdhi6_host *host) 98675fa9ea6SGuennadi Liakhovetski { 98775fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 98875fa9ea6SGuennadi Liakhovetski struct mmc_command *cmd = mrq->cmd; 98975fa9ea6SGuennadi Liakhovetski struct mmc_data *data = mrq->data; 99075fa9ea6SGuennadi Liakhovetski int opc = usdhi6_cmd_flags(host); 99175fa9ea6SGuennadi Liakhovetski int i; 99275fa9ea6SGuennadi Liakhovetski 99375fa9ea6SGuennadi Liakhovetski if (opc < 0) 99475fa9ea6SGuennadi Liakhovetski return opc; 99575fa9ea6SGuennadi Liakhovetski 99675fa9ea6SGuennadi Liakhovetski for (i = 1000; i; i--) { 99775fa9ea6SGuennadi Liakhovetski if (!(usdhi6_read(host, USDHI6_SD_INFO2) & USDHI6_SD_INFO2_CBSY)) 99875fa9ea6SGuennadi Liakhovetski break; 99975fa9ea6SGuennadi Liakhovetski usleep_range(10, 100); 100075fa9ea6SGuennadi Liakhovetski } 100175fa9ea6SGuennadi Liakhovetski 100275fa9ea6SGuennadi Liakhovetski if (!i) { 100375fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "Command active, request aborted\n"); 100475fa9ea6SGuennadi Liakhovetski return -EAGAIN; 100575fa9ea6SGuennadi Liakhovetski } 100675fa9ea6SGuennadi Liakhovetski 100775fa9ea6SGuennadi Liakhovetski if (data) { 100875fa9ea6SGuennadi Liakhovetski bool use_dma; 100975fa9ea6SGuennadi Liakhovetski int ret = 0; 101075fa9ea6SGuennadi Liakhovetski 101175fa9ea6SGuennadi Liakhovetski host->page_idx = 0; 101275fa9ea6SGuennadi Liakhovetski 101375fa9ea6SGuennadi Liakhovetski if (cmd->opcode == SD_IO_RW_EXTENDED && data->blocks > 1) { 101475fa9ea6SGuennadi Liakhovetski switch (data->blksz) { 101575fa9ea6SGuennadi Liakhovetski case 512: 101675fa9ea6SGuennadi Liakhovetski break; 101775fa9ea6SGuennadi Liakhovetski case 32: 101875fa9ea6SGuennadi Liakhovetski case 64: 101975fa9ea6SGuennadi Liakhovetski case 128: 102075fa9ea6SGuennadi Liakhovetski case 256: 102175fa9ea6SGuennadi Liakhovetski if (mrq->stop) 102275fa9ea6SGuennadi Liakhovetski ret = -EINVAL; 102375fa9ea6SGuennadi Liakhovetski break; 102475fa9ea6SGuennadi Liakhovetski default: 102575fa9ea6SGuennadi Liakhovetski ret = -EINVAL; 102675fa9ea6SGuennadi Liakhovetski } 102775fa9ea6SGuennadi Liakhovetski } else if ((cmd->opcode == MMC_READ_MULTIPLE_BLOCK || 102875fa9ea6SGuennadi Liakhovetski cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) && 102975fa9ea6SGuennadi Liakhovetski data->blksz != 512) { 103075fa9ea6SGuennadi Liakhovetski ret = -EINVAL; 103175fa9ea6SGuennadi Liakhovetski } 103275fa9ea6SGuennadi Liakhovetski 103375fa9ea6SGuennadi Liakhovetski if (ret < 0) { 103475fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), "%s(): %u blocks of %u bytes\n", 103575fa9ea6SGuennadi Liakhovetski __func__, data->blocks, data->blksz); 103675fa9ea6SGuennadi Liakhovetski return -EINVAL; 103775fa9ea6SGuennadi Liakhovetski } 103875fa9ea6SGuennadi Liakhovetski 103975fa9ea6SGuennadi Liakhovetski if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || 104075fa9ea6SGuennadi Liakhovetski cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || 104175fa9ea6SGuennadi Liakhovetski (cmd->opcode == SD_IO_RW_EXTENDED && 104275fa9ea6SGuennadi Liakhovetski data->blocks > 1)) 104375fa9ea6SGuennadi Liakhovetski usdhi6_sg_prep(host); 104475fa9ea6SGuennadi Liakhovetski 104575fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_SIZE, data->blksz); 104675fa9ea6SGuennadi Liakhovetski 104775fa9ea6SGuennadi Liakhovetski if ((data->blksz >= USDHI6_MIN_DMA || 104875fa9ea6SGuennadi Liakhovetski data->blocks > 1) && 104975fa9ea6SGuennadi Liakhovetski (data->blksz % 4 || 105075fa9ea6SGuennadi Liakhovetski data->sg->offset % 4)) 105175fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), 105275fa9ea6SGuennadi Liakhovetski "Bad SG of %u: %ux%u @ %u\n", data->sg_len, 105375fa9ea6SGuennadi Liakhovetski data->blksz, data->blocks, data->sg->offset); 105475fa9ea6SGuennadi Liakhovetski 105575fa9ea6SGuennadi Liakhovetski /* Enable DMA for USDHI6_MIN_DMA bytes or more */ 105675fa9ea6SGuennadi Liakhovetski use_dma = data->blksz >= USDHI6_MIN_DMA && 105775fa9ea6SGuennadi Liakhovetski !(data->blksz % 4) && 105875fa9ea6SGuennadi Liakhovetski usdhi6_dma_start(host) >= DMA_MIN_COOKIE; 105975fa9ea6SGuennadi Liakhovetski 106075fa9ea6SGuennadi Liakhovetski if (use_dma) 106175fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_CC_EXT_MODE, USDHI6_CC_EXT_MODE_SDRW); 106275fa9ea6SGuennadi Liakhovetski 106375fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), 106475fa9ea6SGuennadi Liakhovetski "%s(): request opcode %u, %u blocks of %u bytes in %u segments, %s %s @+0x%x%s\n", 106575fa9ea6SGuennadi Liakhovetski __func__, cmd->opcode, data->blocks, data->blksz, 106675fa9ea6SGuennadi Liakhovetski data->sg_len, use_dma ? "DMA" : "PIO", 106775fa9ea6SGuennadi Liakhovetski data->flags & MMC_DATA_READ ? "read" : "write", 106875fa9ea6SGuennadi Liakhovetski data->sg->offset, mrq->stop ? " + stop" : ""); 106975fa9ea6SGuennadi Liakhovetski } else { 107075fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): request opcode %u\n", 107175fa9ea6SGuennadi Liakhovetski __func__, cmd->opcode); 107275fa9ea6SGuennadi Liakhovetski } 107375fa9ea6SGuennadi Liakhovetski 107475fa9ea6SGuennadi Liakhovetski /* We have to get a command completion interrupt with DMA too */ 107575fa9ea6SGuennadi Liakhovetski usdhi6_wait_for_resp(host); 107675fa9ea6SGuennadi Liakhovetski 107775fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_CMD; 107875fa9ea6SGuennadi Liakhovetski schedule_delayed_work(&host->timeout_work, host->timeout); 107975fa9ea6SGuennadi Liakhovetski 108075fa9ea6SGuennadi Liakhovetski /* SEC bit is required to enable block counting by the core */ 108175fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_STOP, 108275fa9ea6SGuennadi Liakhovetski data && data->blocks > 1 ? USDHI6_SD_STOP_SEC : 0); 108375fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_ARG, cmd->arg); 108475fa9ea6SGuennadi Liakhovetski 108575fa9ea6SGuennadi Liakhovetski /* Kick command execution */ 108675fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_CMD, opc); 108775fa9ea6SGuennadi Liakhovetski 108875fa9ea6SGuennadi Liakhovetski return 0; 108975fa9ea6SGuennadi Liakhovetski } 109075fa9ea6SGuennadi Liakhovetski 109175fa9ea6SGuennadi Liakhovetski static void usdhi6_request(struct mmc_host *mmc, struct mmc_request *mrq) 109275fa9ea6SGuennadi Liakhovetski { 109375fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = mmc_priv(mmc); 109475fa9ea6SGuennadi Liakhovetski int ret; 109575fa9ea6SGuennadi Liakhovetski 109675fa9ea6SGuennadi Liakhovetski cancel_delayed_work_sync(&host->timeout_work); 109775fa9ea6SGuennadi Liakhovetski 109875fa9ea6SGuennadi Liakhovetski host->mrq = mrq; 109975fa9ea6SGuennadi Liakhovetski host->sg = NULL; 110075fa9ea6SGuennadi Liakhovetski 110175fa9ea6SGuennadi Liakhovetski usdhi6_timeout_set(host); 110275fa9ea6SGuennadi Liakhovetski ret = usdhi6_rq_start(host); 110375fa9ea6SGuennadi Liakhovetski if (ret < 0) { 110475fa9ea6SGuennadi Liakhovetski mrq->cmd->error = ret; 110575fa9ea6SGuennadi Liakhovetski usdhi6_request_done(host); 110675fa9ea6SGuennadi Liakhovetski } 110775fa9ea6SGuennadi Liakhovetski } 110875fa9ea6SGuennadi Liakhovetski 110975fa9ea6SGuennadi Liakhovetski static int usdhi6_get_cd(struct mmc_host *mmc) 111075fa9ea6SGuennadi Liakhovetski { 111175fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = mmc_priv(mmc); 111275fa9ea6SGuennadi Liakhovetski /* Read is atomic, no need to lock */ 111375fa9ea6SGuennadi Liakhovetski u32 status = usdhi6_read(host, USDHI6_SD_INFO1) & USDHI6_SD_INFO1_CD; 111475fa9ea6SGuennadi Liakhovetski 111575fa9ea6SGuennadi Liakhovetski /* 111675fa9ea6SGuennadi Liakhovetski * level status.CD CD_ACTIVE_HIGH card present 111775fa9ea6SGuennadi Liakhovetski * 1 0 0 0 111875fa9ea6SGuennadi Liakhovetski * 1 0 1 1 111975fa9ea6SGuennadi Liakhovetski * 0 1 0 1 112075fa9ea6SGuennadi Liakhovetski * 0 1 1 0 112175fa9ea6SGuennadi Liakhovetski */ 112275fa9ea6SGuennadi Liakhovetski return !status ^ !(mmc->caps2 & MMC_CAP2_CD_ACTIVE_HIGH); 112375fa9ea6SGuennadi Liakhovetski } 112475fa9ea6SGuennadi Liakhovetski 112575fa9ea6SGuennadi Liakhovetski static int usdhi6_get_ro(struct mmc_host *mmc) 112675fa9ea6SGuennadi Liakhovetski { 112775fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = mmc_priv(mmc); 112875fa9ea6SGuennadi Liakhovetski /* No locking as above */ 112975fa9ea6SGuennadi Liakhovetski u32 status = usdhi6_read(host, USDHI6_SD_INFO1) & USDHI6_SD_INFO1_WP; 113075fa9ea6SGuennadi Liakhovetski 113175fa9ea6SGuennadi Liakhovetski /* 113275fa9ea6SGuennadi Liakhovetski * level status.WP RO_ACTIVE_HIGH card read-only 113375fa9ea6SGuennadi Liakhovetski * 1 0 0 0 113475fa9ea6SGuennadi Liakhovetski * 1 0 1 1 113575fa9ea6SGuennadi Liakhovetski * 0 1 0 1 113675fa9ea6SGuennadi Liakhovetski * 0 1 1 0 113775fa9ea6SGuennadi Liakhovetski */ 113875fa9ea6SGuennadi Liakhovetski return !status ^ !(mmc->caps2 & MMC_CAP2_RO_ACTIVE_HIGH); 113975fa9ea6SGuennadi Liakhovetski } 114075fa9ea6SGuennadi Liakhovetski 114175fa9ea6SGuennadi Liakhovetski static void usdhi6_enable_sdio_irq(struct mmc_host *mmc, int enable) 114275fa9ea6SGuennadi Liakhovetski { 114375fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = mmc_priv(mmc); 114475fa9ea6SGuennadi Liakhovetski 114575fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(mmc), "%s(): %sable\n", __func__, enable ? "en" : "dis"); 114675fa9ea6SGuennadi Liakhovetski 114775fa9ea6SGuennadi Liakhovetski if (enable) { 114875fa9ea6SGuennadi Liakhovetski host->sdio_mask = USDHI6_SDIO_INFO1_IRQ & ~USDHI6_SDIO_INFO1_IOIRQ; 114975fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SDIO_INFO1_MASK, host->sdio_mask); 115075fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SDIO_MODE, 1); 115175fa9ea6SGuennadi Liakhovetski } else { 115275fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SDIO_MODE, 0); 115375fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SDIO_INFO1_MASK, USDHI6_SDIO_INFO1_IRQ); 115475fa9ea6SGuennadi Liakhovetski host->sdio_mask = USDHI6_SDIO_INFO1_IRQ; 115575fa9ea6SGuennadi Liakhovetski } 115675fa9ea6SGuennadi Liakhovetski } 115775fa9ea6SGuennadi Liakhovetski 1158488aab3dSLars Persson static int usdhi6_set_pinstates(struct usdhi6_host *host, int voltage) 1159488aab3dSLars Persson { 1160488aab3dSLars Persson if (IS_ERR(host->pins_uhs)) 1161488aab3dSLars Persson return 0; 1162488aab3dSLars Persson 1163488aab3dSLars Persson switch (voltage) { 1164488aab3dSLars Persson case MMC_SIGNAL_VOLTAGE_180: 1165488aab3dSLars Persson case MMC_SIGNAL_VOLTAGE_120: 1166488aab3dSLars Persson return pinctrl_select_state(host->pinctrl, 1167488aab3dSLars Persson host->pins_uhs); 1168488aab3dSLars Persson 1169488aab3dSLars Persson default: 1170c17e73c3SUlf Hansson return pinctrl_select_default_state(mmc_dev(host->mmc)); 1171488aab3dSLars Persson } 1172488aab3dSLars Persson } 1173488aab3dSLars Persson 11740cd59df9SLars Persson static int usdhi6_sig_volt_switch(struct mmc_host *mmc, struct mmc_ios *ios) 11750cd59df9SLars Persson { 11760cd59df9SLars Persson int ret; 11770cd59df9SLars Persson 11780cd59df9SLars Persson ret = mmc_regulator_set_vqmmc(mmc, ios); 1179488aab3dSLars Persson if (ret < 0) 1180488aab3dSLars Persson return ret; 11810cd59df9SLars Persson 1182488aab3dSLars Persson ret = usdhi6_set_pinstates(mmc_priv(mmc), ios->signal_voltage); 1183488aab3dSLars Persson if (ret) 1184488aab3dSLars Persson dev_warn_once(mmc_dev(mmc), 1185488aab3dSLars Persson "Failed to set pinstate err=%d\n", ret); 11860cd59df9SLars Persson return ret; 11870cd59df9SLars Persson } 11880cd59df9SLars Persson 11894850c225SMårten Lindahl static int usdhi6_card_busy(struct mmc_host *mmc) 11904850c225SMårten Lindahl { 11914850c225SMårten Lindahl struct usdhi6_host *host = mmc_priv(mmc); 11924850c225SMårten Lindahl u32 tmp = usdhi6_read(host, USDHI6_SD_INFO2); 11934850c225SMårten Lindahl 11944850c225SMårten Lindahl /* Card is busy if it is pulling dat[0] low */ 11954850c225SMårten Lindahl return !(tmp & USDHI6_SD_INFO2_SDDAT0); 11964850c225SMårten Lindahl } 11974850c225SMårten Lindahl 119857e30c0cSJulia Lawall static const struct mmc_host_ops usdhi6_ops = { 119975fa9ea6SGuennadi Liakhovetski .request = usdhi6_request, 120075fa9ea6SGuennadi Liakhovetski .set_ios = usdhi6_set_ios, 120175fa9ea6SGuennadi Liakhovetski .get_cd = usdhi6_get_cd, 120275fa9ea6SGuennadi Liakhovetski .get_ro = usdhi6_get_ro, 120375fa9ea6SGuennadi Liakhovetski .enable_sdio_irq = usdhi6_enable_sdio_irq, 12040cd59df9SLars Persson .start_signal_voltage_switch = usdhi6_sig_volt_switch, 12054850c225SMårten Lindahl .card_busy = usdhi6_card_busy, 120675fa9ea6SGuennadi Liakhovetski }; 120775fa9ea6SGuennadi Liakhovetski 120875fa9ea6SGuennadi Liakhovetski /* State machine handlers */ 120975fa9ea6SGuennadi Liakhovetski 121075fa9ea6SGuennadi Liakhovetski static void usdhi6_resp_cmd12(struct usdhi6_host *host) 121175fa9ea6SGuennadi Liakhovetski { 121275fa9ea6SGuennadi Liakhovetski struct mmc_command *cmd = host->mrq->stop; 121375fa9ea6SGuennadi Liakhovetski cmd->resp[0] = usdhi6_read(host, USDHI6_SD_RSP10); 121475fa9ea6SGuennadi Liakhovetski } 121575fa9ea6SGuennadi Liakhovetski 121675fa9ea6SGuennadi Liakhovetski static void usdhi6_resp_read(struct usdhi6_host *host) 121775fa9ea6SGuennadi Liakhovetski { 121875fa9ea6SGuennadi Liakhovetski struct mmc_command *cmd = host->mrq->cmd; 121975fa9ea6SGuennadi Liakhovetski u32 *rsp = cmd->resp, tmp = 0; 122075fa9ea6SGuennadi Liakhovetski int i; 122175fa9ea6SGuennadi Liakhovetski 122275fa9ea6SGuennadi Liakhovetski /* 122375fa9ea6SGuennadi Liakhovetski * RSP10 39-8 122475fa9ea6SGuennadi Liakhovetski * RSP32 71-40 122575fa9ea6SGuennadi Liakhovetski * RSP54 103-72 122675fa9ea6SGuennadi Liakhovetski * RSP76 127-104 122775fa9ea6SGuennadi Liakhovetski * R2-type response: 122875fa9ea6SGuennadi Liakhovetski * resp[0] = r[127..96] 122975fa9ea6SGuennadi Liakhovetski * resp[1] = r[95..64] 123075fa9ea6SGuennadi Liakhovetski * resp[2] = r[63..32] 123175fa9ea6SGuennadi Liakhovetski * resp[3] = r[31..0] 123275fa9ea6SGuennadi Liakhovetski * Other responses: 123375fa9ea6SGuennadi Liakhovetski * resp[0] = r[39..8] 123475fa9ea6SGuennadi Liakhovetski */ 123575fa9ea6SGuennadi Liakhovetski 123675fa9ea6SGuennadi Liakhovetski if (mmc_resp_type(cmd) == MMC_RSP_NONE) 123775fa9ea6SGuennadi Liakhovetski return; 123875fa9ea6SGuennadi Liakhovetski 123975fa9ea6SGuennadi Liakhovetski if (!(host->irq_status & USDHI6_SD_INFO1_RSP_END)) { 124075fa9ea6SGuennadi Liakhovetski dev_err(mmc_dev(host->mmc), 124175fa9ea6SGuennadi Liakhovetski "CMD%d: response expected but is missing!\n", cmd->opcode); 124275fa9ea6SGuennadi Liakhovetski return; 124375fa9ea6SGuennadi Liakhovetski } 124475fa9ea6SGuennadi Liakhovetski 124575fa9ea6SGuennadi Liakhovetski if (mmc_resp_type(cmd) & MMC_RSP_136) 124675fa9ea6SGuennadi Liakhovetski for (i = 0; i < 4; i++) { 124775fa9ea6SGuennadi Liakhovetski if (i) 124875fa9ea6SGuennadi Liakhovetski rsp[3 - i] = tmp >> 24; 124975fa9ea6SGuennadi Liakhovetski tmp = usdhi6_read(host, USDHI6_SD_RSP10 + i * 8); 125075fa9ea6SGuennadi Liakhovetski rsp[3 - i] |= tmp << 8; 125175fa9ea6SGuennadi Liakhovetski } 125275fa9ea6SGuennadi Liakhovetski else if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || 125375fa9ea6SGuennadi Liakhovetski cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK) 125475fa9ea6SGuennadi Liakhovetski /* Read RSP54 to avoid conflict with auto CMD12 */ 125575fa9ea6SGuennadi Liakhovetski rsp[0] = usdhi6_read(host, USDHI6_SD_RSP54); 125675fa9ea6SGuennadi Liakhovetski else 125775fa9ea6SGuennadi Liakhovetski rsp[0] = usdhi6_read(host, USDHI6_SD_RSP10); 125875fa9ea6SGuennadi Liakhovetski 125975fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "Response 0x%x\n", rsp[0]); 126075fa9ea6SGuennadi Liakhovetski } 126175fa9ea6SGuennadi Liakhovetski 126275fa9ea6SGuennadi Liakhovetski static int usdhi6_blk_read(struct usdhi6_host *host) 126375fa9ea6SGuennadi Liakhovetski { 126475fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 126575fa9ea6SGuennadi Liakhovetski u32 *p; 126675fa9ea6SGuennadi Liakhovetski int i, rest; 126775fa9ea6SGuennadi Liakhovetski 126875fa9ea6SGuennadi Liakhovetski if (host->io_error) { 126975fa9ea6SGuennadi Liakhovetski data->error = usdhi6_error_code(host); 127075fa9ea6SGuennadi Liakhovetski goto error; 127175fa9ea6SGuennadi Liakhovetski } 127275fa9ea6SGuennadi Liakhovetski 127375fa9ea6SGuennadi Liakhovetski if (host->pg.page) { 127475fa9ea6SGuennadi Liakhovetski p = host->blk_page + host->offset; 127575fa9ea6SGuennadi Liakhovetski } else { 127675fa9ea6SGuennadi Liakhovetski p = usdhi6_sg_map(host); 127775fa9ea6SGuennadi Liakhovetski if (!p) { 127875fa9ea6SGuennadi Liakhovetski data->error = -ENOMEM; 127975fa9ea6SGuennadi Liakhovetski goto error; 128075fa9ea6SGuennadi Liakhovetski } 128175fa9ea6SGuennadi Liakhovetski } 128275fa9ea6SGuennadi Liakhovetski 128375fa9ea6SGuennadi Liakhovetski for (i = 0; i < data->blksz / 4; i++, p++) 128475fa9ea6SGuennadi Liakhovetski *p = usdhi6_read(host, USDHI6_SD_BUF0); 128575fa9ea6SGuennadi Liakhovetski 128675fa9ea6SGuennadi Liakhovetski rest = data->blksz % 4; 128775fa9ea6SGuennadi Liakhovetski for (i = 0; i < (rest + 1) / 2; i++) { 128875fa9ea6SGuennadi Liakhovetski u16 d = usdhi6_read16(host, USDHI6_SD_BUF0); 128975fa9ea6SGuennadi Liakhovetski ((u8 *)p)[2 * i] = ((u8 *)&d)[0]; 129075fa9ea6SGuennadi Liakhovetski if (rest > 1 && !i) 129175fa9ea6SGuennadi Liakhovetski ((u8 *)p)[2 * i + 1] = ((u8 *)&d)[1]; 129275fa9ea6SGuennadi Liakhovetski } 129375fa9ea6SGuennadi Liakhovetski 129475fa9ea6SGuennadi Liakhovetski return 0; 129575fa9ea6SGuennadi Liakhovetski 129675fa9ea6SGuennadi Liakhovetski error: 129775fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): %d\n", __func__, data->error); 129875fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_REQUEST; 129975fa9ea6SGuennadi Liakhovetski return data->error; 130075fa9ea6SGuennadi Liakhovetski } 130175fa9ea6SGuennadi Liakhovetski 130275fa9ea6SGuennadi Liakhovetski static int usdhi6_blk_write(struct usdhi6_host *host) 130375fa9ea6SGuennadi Liakhovetski { 130475fa9ea6SGuennadi Liakhovetski struct mmc_data *data = host->mrq->data; 130575fa9ea6SGuennadi Liakhovetski u32 *p; 130675fa9ea6SGuennadi Liakhovetski int i, rest; 130775fa9ea6SGuennadi Liakhovetski 130875fa9ea6SGuennadi Liakhovetski if (host->io_error) { 130975fa9ea6SGuennadi Liakhovetski data->error = usdhi6_error_code(host); 131075fa9ea6SGuennadi Liakhovetski goto error; 131175fa9ea6SGuennadi Liakhovetski } 131275fa9ea6SGuennadi Liakhovetski 131375fa9ea6SGuennadi Liakhovetski if (host->pg.page) { 131475fa9ea6SGuennadi Liakhovetski p = host->blk_page + host->offset; 131575fa9ea6SGuennadi Liakhovetski } else { 131675fa9ea6SGuennadi Liakhovetski p = usdhi6_sg_map(host); 131775fa9ea6SGuennadi Liakhovetski if (!p) { 131875fa9ea6SGuennadi Liakhovetski data->error = -ENOMEM; 131975fa9ea6SGuennadi Liakhovetski goto error; 132075fa9ea6SGuennadi Liakhovetski } 132175fa9ea6SGuennadi Liakhovetski } 132275fa9ea6SGuennadi Liakhovetski 132375fa9ea6SGuennadi Liakhovetski for (i = 0; i < data->blksz / 4; i++, p++) 132475fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_BUF0, *p); 132575fa9ea6SGuennadi Liakhovetski 132675fa9ea6SGuennadi Liakhovetski rest = data->blksz % 4; 132775fa9ea6SGuennadi Liakhovetski for (i = 0; i < (rest + 1) / 2; i++) { 132875fa9ea6SGuennadi Liakhovetski u16 d; 132975fa9ea6SGuennadi Liakhovetski ((u8 *)&d)[0] = ((u8 *)p)[2 * i]; 133075fa9ea6SGuennadi Liakhovetski if (rest > 1 && !i) 133175fa9ea6SGuennadi Liakhovetski ((u8 *)&d)[1] = ((u8 *)p)[2 * i + 1]; 133275fa9ea6SGuennadi Liakhovetski else 133375fa9ea6SGuennadi Liakhovetski ((u8 *)&d)[1] = 0; 133475fa9ea6SGuennadi Liakhovetski usdhi6_write16(host, USDHI6_SD_BUF0, d); 133575fa9ea6SGuennadi Liakhovetski } 133675fa9ea6SGuennadi Liakhovetski 133775fa9ea6SGuennadi Liakhovetski return 0; 133875fa9ea6SGuennadi Liakhovetski 133975fa9ea6SGuennadi Liakhovetski error: 134075fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): %d\n", __func__, data->error); 134175fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_REQUEST; 134275fa9ea6SGuennadi Liakhovetski return data->error; 134375fa9ea6SGuennadi Liakhovetski } 134475fa9ea6SGuennadi Liakhovetski 134575fa9ea6SGuennadi Liakhovetski static int usdhi6_stop_cmd(struct usdhi6_host *host) 134675fa9ea6SGuennadi Liakhovetski { 134775fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 134875fa9ea6SGuennadi Liakhovetski 134975fa9ea6SGuennadi Liakhovetski switch (mrq->cmd->opcode) { 135075fa9ea6SGuennadi Liakhovetski case MMC_READ_MULTIPLE_BLOCK: 135175fa9ea6SGuennadi Liakhovetski case MMC_WRITE_MULTIPLE_BLOCK: 135275fa9ea6SGuennadi Liakhovetski if (mrq->stop->opcode == MMC_STOP_TRANSMISSION) { 135375fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_STOP; 135475fa9ea6SGuennadi Liakhovetski return 0; 135575fa9ea6SGuennadi Liakhovetski } 1356df561f66SGustavo A. R. Silva fallthrough; /* Unsupported STOP command */ 135775fa9ea6SGuennadi Liakhovetski default: 135875fa9ea6SGuennadi Liakhovetski dev_err(mmc_dev(host->mmc), 135975fa9ea6SGuennadi Liakhovetski "unsupported stop CMD%d for CMD%d\n", 136075fa9ea6SGuennadi Liakhovetski mrq->stop->opcode, mrq->cmd->opcode); 136175fa9ea6SGuennadi Liakhovetski mrq->stop->error = -EOPNOTSUPP; 136275fa9ea6SGuennadi Liakhovetski } 136375fa9ea6SGuennadi Liakhovetski 136475fa9ea6SGuennadi Liakhovetski return -EOPNOTSUPP; 136575fa9ea6SGuennadi Liakhovetski } 136675fa9ea6SGuennadi Liakhovetski 136775fa9ea6SGuennadi Liakhovetski static bool usdhi6_end_cmd(struct usdhi6_host *host) 136875fa9ea6SGuennadi Liakhovetski { 136975fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 137075fa9ea6SGuennadi Liakhovetski struct mmc_command *cmd = mrq->cmd; 137175fa9ea6SGuennadi Liakhovetski 137275fa9ea6SGuennadi Liakhovetski if (host->io_error) { 137375fa9ea6SGuennadi Liakhovetski cmd->error = usdhi6_error_code(host); 137475fa9ea6SGuennadi Liakhovetski return false; 137575fa9ea6SGuennadi Liakhovetski } 137675fa9ea6SGuennadi Liakhovetski 137775fa9ea6SGuennadi Liakhovetski usdhi6_resp_read(host); 137875fa9ea6SGuennadi Liakhovetski 137975fa9ea6SGuennadi Liakhovetski if (!mrq->data) 138075fa9ea6SGuennadi Liakhovetski return false; 138175fa9ea6SGuennadi Liakhovetski 138275fa9ea6SGuennadi Liakhovetski if (host->dma_active) { 138375fa9ea6SGuennadi Liakhovetski usdhi6_dma_kick(host); 138475fa9ea6SGuennadi Liakhovetski if (!mrq->stop) 138575fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_DMA; 138675fa9ea6SGuennadi Liakhovetski else if (usdhi6_stop_cmd(host) < 0) 138775fa9ea6SGuennadi Liakhovetski return false; 138875fa9ea6SGuennadi Liakhovetski } else if (mrq->data->flags & MMC_DATA_READ) { 138975fa9ea6SGuennadi Liakhovetski if (cmd->opcode == MMC_READ_MULTIPLE_BLOCK || 139075fa9ea6SGuennadi Liakhovetski (cmd->opcode == SD_IO_RW_EXTENDED && 139175fa9ea6SGuennadi Liakhovetski mrq->data->blocks > 1)) 139275fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_MREAD; 139375fa9ea6SGuennadi Liakhovetski else 139475fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_READ; 139575fa9ea6SGuennadi Liakhovetski } else { 139675fa9ea6SGuennadi Liakhovetski if (cmd->opcode == MMC_WRITE_MULTIPLE_BLOCK || 139775fa9ea6SGuennadi Liakhovetski (cmd->opcode == SD_IO_RW_EXTENDED && 139875fa9ea6SGuennadi Liakhovetski mrq->data->blocks > 1)) 139975fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_MWRITE; 140075fa9ea6SGuennadi Liakhovetski else 140175fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_WRITE; 140275fa9ea6SGuennadi Liakhovetski } 140375fa9ea6SGuennadi Liakhovetski 140475fa9ea6SGuennadi Liakhovetski return true; 140575fa9ea6SGuennadi Liakhovetski } 140675fa9ea6SGuennadi Liakhovetski 140775fa9ea6SGuennadi Liakhovetski static bool usdhi6_read_block(struct usdhi6_host *host) 140875fa9ea6SGuennadi Liakhovetski { 140975fa9ea6SGuennadi Liakhovetski /* ACCESS_END IRQ is already unmasked */ 141075fa9ea6SGuennadi Liakhovetski int ret = usdhi6_blk_read(host); 141175fa9ea6SGuennadi Liakhovetski 141275fa9ea6SGuennadi Liakhovetski /* 141375fa9ea6SGuennadi Liakhovetski * Have to force unmapping both pages: the single block could have been 141475fa9ea6SGuennadi Liakhovetski * cross-page, in which case for single-block IO host->page_idx == 0. 141575fa9ea6SGuennadi Liakhovetski * So, if we don't force, the second page won't be unmapped. 141675fa9ea6SGuennadi Liakhovetski */ 141775fa9ea6SGuennadi Liakhovetski usdhi6_sg_unmap(host, true); 141875fa9ea6SGuennadi Liakhovetski 141975fa9ea6SGuennadi Liakhovetski if (ret < 0) 142075fa9ea6SGuennadi Liakhovetski return false; 142175fa9ea6SGuennadi Liakhovetski 142275fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_DATA_END; 142375fa9ea6SGuennadi Liakhovetski return true; 142475fa9ea6SGuennadi Liakhovetski } 142575fa9ea6SGuennadi Liakhovetski 142675fa9ea6SGuennadi Liakhovetski static bool usdhi6_mread_block(struct usdhi6_host *host) 142775fa9ea6SGuennadi Liakhovetski { 142875fa9ea6SGuennadi Liakhovetski int ret = usdhi6_blk_read(host); 142975fa9ea6SGuennadi Liakhovetski 143075fa9ea6SGuennadi Liakhovetski if (ret < 0) 143175fa9ea6SGuennadi Liakhovetski return false; 143275fa9ea6SGuennadi Liakhovetski 143375fa9ea6SGuennadi Liakhovetski usdhi6_sg_advance(host); 143475fa9ea6SGuennadi Liakhovetski 143575fa9ea6SGuennadi Liakhovetski return !host->mrq->data->error && 143675fa9ea6SGuennadi Liakhovetski (host->wait != USDHI6_WAIT_FOR_DATA_END || !host->mrq->stop); 143775fa9ea6SGuennadi Liakhovetski } 143875fa9ea6SGuennadi Liakhovetski 143975fa9ea6SGuennadi Liakhovetski static bool usdhi6_write_block(struct usdhi6_host *host) 144075fa9ea6SGuennadi Liakhovetski { 144175fa9ea6SGuennadi Liakhovetski int ret = usdhi6_blk_write(host); 144275fa9ea6SGuennadi Liakhovetski 144375fa9ea6SGuennadi Liakhovetski /* See comment in usdhi6_read_block() */ 144475fa9ea6SGuennadi Liakhovetski usdhi6_sg_unmap(host, true); 144575fa9ea6SGuennadi Liakhovetski 144675fa9ea6SGuennadi Liakhovetski if (ret < 0) 144775fa9ea6SGuennadi Liakhovetski return false; 144875fa9ea6SGuennadi Liakhovetski 144975fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_DATA_END; 145075fa9ea6SGuennadi Liakhovetski return true; 145175fa9ea6SGuennadi Liakhovetski } 145275fa9ea6SGuennadi Liakhovetski 145375fa9ea6SGuennadi Liakhovetski static bool usdhi6_mwrite_block(struct usdhi6_host *host) 145475fa9ea6SGuennadi Liakhovetski { 145575fa9ea6SGuennadi Liakhovetski int ret = usdhi6_blk_write(host); 145675fa9ea6SGuennadi Liakhovetski 145775fa9ea6SGuennadi Liakhovetski if (ret < 0) 145875fa9ea6SGuennadi Liakhovetski return false; 145975fa9ea6SGuennadi Liakhovetski 146075fa9ea6SGuennadi Liakhovetski usdhi6_sg_advance(host); 146175fa9ea6SGuennadi Liakhovetski 146275fa9ea6SGuennadi Liakhovetski return !host->mrq->data->error && 146375fa9ea6SGuennadi Liakhovetski (host->wait != USDHI6_WAIT_FOR_DATA_END || !host->mrq->stop); 146475fa9ea6SGuennadi Liakhovetski } 146575fa9ea6SGuennadi Liakhovetski 146675fa9ea6SGuennadi Liakhovetski /* Interrupt & timeout handlers */ 146775fa9ea6SGuennadi Liakhovetski 146875fa9ea6SGuennadi Liakhovetski static irqreturn_t usdhi6_sd_bh(int irq, void *dev_id) 146975fa9ea6SGuennadi Liakhovetski { 147075fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = dev_id; 147175fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq; 147275fa9ea6SGuennadi Liakhovetski struct mmc_command *cmd; 147375fa9ea6SGuennadi Liakhovetski struct mmc_data *data; 147475fa9ea6SGuennadi Liakhovetski bool io_wait = false; 147575fa9ea6SGuennadi Liakhovetski 147675fa9ea6SGuennadi Liakhovetski cancel_delayed_work_sync(&host->timeout_work); 147775fa9ea6SGuennadi Liakhovetski 147875fa9ea6SGuennadi Liakhovetski mrq = host->mrq; 147975fa9ea6SGuennadi Liakhovetski if (!mrq) 148075fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 148175fa9ea6SGuennadi Liakhovetski 148275fa9ea6SGuennadi Liakhovetski cmd = mrq->cmd; 148375fa9ea6SGuennadi Liakhovetski data = mrq->data; 148475fa9ea6SGuennadi Liakhovetski 148575fa9ea6SGuennadi Liakhovetski switch (host->wait) { 148675fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_REQUEST: 148775fa9ea6SGuennadi Liakhovetski /* We're too late, the timeout has already kicked in */ 148875fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 148975fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_CMD: 149075fa9ea6SGuennadi Liakhovetski /* Wait for data? */ 149175fa9ea6SGuennadi Liakhovetski io_wait = usdhi6_end_cmd(host); 149275fa9ea6SGuennadi Liakhovetski break; 149375fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_MREAD: 149475fa9ea6SGuennadi Liakhovetski /* Wait for more data? */ 149575fa9ea6SGuennadi Liakhovetski io_wait = usdhi6_mread_block(host); 149675fa9ea6SGuennadi Liakhovetski break; 149775fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_READ: 149875fa9ea6SGuennadi Liakhovetski /* Wait for data end? */ 149975fa9ea6SGuennadi Liakhovetski io_wait = usdhi6_read_block(host); 150075fa9ea6SGuennadi Liakhovetski break; 150175fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_MWRITE: 150275fa9ea6SGuennadi Liakhovetski /* Wait data to write? */ 150375fa9ea6SGuennadi Liakhovetski io_wait = usdhi6_mwrite_block(host); 150475fa9ea6SGuennadi Liakhovetski break; 150575fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_WRITE: 150675fa9ea6SGuennadi Liakhovetski /* Wait for data end? */ 150775fa9ea6SGuennadi Liakhovetski io_wait = usdhi6_write_block(host); 150875fa9ea6SGuennadi Liakhovetski break; 150975fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_DMA: 151075fa9ea6SGuennadi Liakhovetski usdhi6_dma_check_error(host); 151175fa9ea6SGuennadi Liakhovetski break; 151275fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_STOP: 151375fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_STOP, 0); 151475fa9ea6SGuennadi Liakhovetski if (host->io_error) { 151575fa9ea6SGuennadi Liakhovetski int ret = usdhi6_error_code(host); 151675fa9ea6SGuennadi Liakhovetski if (mrq->stop) 151775fa9ea6SGuennadi Liakhovetski mrq->stop->error = ret; 151875fa9ea6SGuennadi Liakhovetski else 151975fa9ea6SGuennadi Liakhovetski mrq->data->error = ret; 152075fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), "%s(): %d\n", __func__, ret); 152175fa9ea6SGuennadi Liakhovetski break; 152275fa9ea6SGuennadi Liakhovetski } 152375fa9ea6SGuennadi Liakhovetski usdhi6_resp_cmd12(host); 152475fa9ea6SGuennadi Liakhovetski mrq->stop->error = 0; 152575fa9ea6SGuennadi Liakhovetski break; 152675fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_DATA_END: 152775fa9ea6SGuennadi Liakhovetski if (host->io_error) { 152875fa9ea6SGuennadi Liakhovetski mrq->data->error = usdhi6_error_code(host); 152975fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), "%s(): %d\n", __func__, 153075fa9ea6SGuennadi Liakhovetski mrq->data->error); 153175fa9ea6SGuennadi Liakhovetski } 153275fa9ea6SGuennadi Liakhovetski break; 153375fa9ea6SGuennadi Liakhovetski default: 153475fa9ea6SGuennadi Liakhovetski cmd->error = -EFAULT; 153575fa9ea6SGuennadi Liakhovetski dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait); 153675fa9ea6SGuennadi Liakhovetski usdhi6_request_done(host); 153775fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 153875fa9ea6SGuennadi Liakhovetski } 153975fa9ea6SGuennadi Liakhovetski 154075fa9ea6SGuennadi Liakhovetski if (io_wait) { 154175fa9ea6SGuennadi Liakhovetski schedule_delayed_work(&host->timeout_work, host->timeout); 154275fa9ea6SGuennadi Liakhovetski /* Wait for more data or ACCESS_END */ 154375fa9ea6SGuennadi Liakhovetski if (!host->dma_active) 154475fa9ea6SGuennadi Liakhovetski usdhi6_wait_for_brwe(host, mrq->data->flags & MMC_DATA_READ); 154575fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 154675fa9ea6SGuennadi Liakhovetski } 154775fa9ea6SGuennadi Liakhovetski 154875fa9ea6SGuennadi Liakhovetski if (!cmd->error) { 154975fa9ea6SGuennadi Liakhovetski if (data) { 155075fa9ea6SGuennadi Liakhovetski if (!data->error) { 155175fa9ea6SGuennadi Liakhovetski if (host->wait != USDHI6_WAIT_FOR_STOP && 155275fa9ea6SGuennadi Liakhovetski host->mrq->stop && 155375fa9ea6SGuennadi Liakhovetski !host->mrq->stop->error && 155475fa9ea6SGuennadi Liakhovetski !usdhi6_stop_cmd(host)) { 155575fa9ea6SGuennadi Liakhovetski /* Sending STOP */ 155675fa9ea6SGuennadi Liakhovetski usdhi6_wait_for_resp(host); 155775fa9ea6SGuennadi Liakhovetski 155875fa9ea6SGuennadi Liakhovetski schedule_delayed_work(&host->timeout_work, 155975fa9ea6SGuennadi Liakhovetski host->timeout); 156075fa9ea6SGuennadi Liakhovetski 156175fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 156275fa9ea6SGuennadi Liakhovetski } 156375fa9ea6SGuennadi Liakhovetski 156475fa9ea6SGuennadi Liakhovetski data->bytes_xfered = data->blocks * data->blksz; 156575fa9ea6SGuennadi Liakhovetski } else { 156675fa9ea6SGuennadi Liakhovetski /* Data error: might need to unmap the last page */ 156775fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), "%s(): data error %d\n", 156875fa9ea6SGuennadi Liakhovetski __func__, data->error); 156975fa9ea6SGuennadi Liakhovetski usdhi6_sg_unmap(host, true); 157075fa9ea6SGuennadi Liakhovetski } 157175fa9ea6SGuennadi Liakhovetski } else if (cmd->opcode == MMC_APP_CMD) { 157275fa9ea6SGuennadi Liakhovetski host->app_cmd = true; 157375fa9ea6SGuennadi Liakhovetski } 157475fa9ea6SGuennadi Liakhovetski } 157575fa9ea6SGuennadi Liakhovetski 157675fa9ea6SGuennadi Liakhovetski usdhi6_request_done(host); 157775fa9ea6SGuennadi Liakhovetski 157875fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 157975fa9ea6SGuennadi Liakhovetski } 158075fa9ea6SGuennadi Liakhovetski 158175fa9ea6SGuennadi Liakhovetski static irqreturn_t usdhi6_sd(int irq, void *dev_id) 158275fa9ea6SGuennadi Liakhovetski { 158375fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = dev_id; 158475fa9ea6SGuennadi Liakhovetski u16 status, status2, error; 158575fa9ea6SGuennadi Liakhovetski 158675fa9ea6SGuennadi Liakhovetski status = usdhi6_read(host, USDHI6_SD_INFO1) & ~host->status_mask & 158775fa9ea6SGuennadi Liakhovetski ~USDHI6_SD_INFO1_CARD; 158875fa9ea6SGuennadi Liakhovetski status2 = usdhi6_read(host, USDHI6_SD_INFO2) & ~host->status2_mask; 158975fa9ea6SGuennadi Liakhovetski 159075fa9ea6SGuennadi Liakhovetski usdhi6_only_cd(host); 159175fa9ea6SGuennadi Liakhovetski 159275fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), 159375fa9ea6SGuennadi Liakhovetski "IRQ status = 0x%08x, status2 = 0x%08x\n", status, status2); 159475fa9ea6SGuennadi Liakhovetski 159575fa9ea6SGuennadi Liakhovetski if (!status && !status2) 159675fa9ea6SGuennadi Liakhovetski return IRQ_NONE; 159775fa9ea6SGuennadi Liakhovetski 159875fa9ea6SGuennadi Liakhovetski error = status2 & USDHI6_SD_INFO2_ERR; 159975fa9ea6SGuennadi Liakhovetski 160075fa9ea6SGuennadi Liakhovetski /* Ack / clear interrupts */ 160175fa9ea6SGuennadi Liakhovetski if (USDHI6_SD_INFO1_IRQ & status) 160275fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_INFO1, 160375fa9ea6SGuennadi Liakhovetski 0xffff & ~(USDHI6_SD_INFO1_IRQ & status)); 160475fa9ea6SGuennadi Liakhovetski 160575fa9ea6SGuennadi Liakhovetski if (USDHI6_SD_INFO2_IRQ & status2) { 160675fa9ea6SGuennadi Liakhovetski if (error) 160775fa9ea6SGuennadi Liakhovetski /* In error cases BWE and BRE aren't cleared automatically */ 160875fa9ea6SGuennadi Liakhovetski status2 |= USDHI6_SD_INFO2_BWE | USDHI6_SD_INFO2_BRE; 160975fa9ea6SGuennadi Liakhovetski 161075fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SD_INFO2, 161175fa9ea6SGuennadi Liakhovetski 0xffff & ~(USDHI6_SD_INFO2_IRQ & status2)); 161275fa9ea6SGuennadi Liakhovetski } 161375fa9ea6SGuennadi Liakhovetski 161475fa9ea6SGuennadi Liakhovetski host->io_error = error; 161575fa9ea6SGuennadi Liakhovetski host->irq_status = status; 161675fa9ea6SGuennadi Liakhovetski 161775fa9ea6SGuennadi Liakhovetski if (error) { 161875fa9ea6SGuennadi Liakhovetski /* Don't pollute the log with unsupported command timeouts */ 161975fa9ea6SGuennadi Liakhovetski if (host->wait != USDHI6_WAIT_FOR_CMD || 162075fa9ea6SGuennadi Liakhovetski error != USDHI6_SD_INFO2_RSP_TOUT) 162175fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), 162275fa9ea6SGuennadi Liakhovetski "%s(): INFO2 error bits 0x%08x\n", 162375fa9ea6SGuennadi Liakhovetski __func__, error); 162475fa9ea6SGuennadi Liakhovetski else 162575fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), 162675fa9ea6SGuennadi Liakhovetski "%s(): INFO2 error bits 0x%08x\n", 162775fa9ea6SGuennadi Liakhovetski __func__, error); 162875fa9ea6SGuennadi Liakhovetski } 162975fa9ea6SGuennadi Liakhovetski 163075fa9ea6SGuennadi Liakhovetski return IRQ_WAKE_THREAD; 163175fa9ea6SGuennadi Liakhovetski } 163275fa9ea6SGuennadi Liakhovetski 163375fa9ea6SGuennadi Liakhovetski static irqreturn_t usdhi6_sdio(int irq, void *dev_id) 163475fa9ea6SGuennadi Liakhovetski { 163575fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = dev_id; 163675fa9ea6SGuennadi Liakhovetski u32 status = usdhi6_read(host, USDHI6_SDIO_INFO1) & ~host->sdio_mask; 163775fa9ea6SGuennadi Liakhovetski 163875fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), "%s(): status 0x%x\n", __func__, status); 163975fa9ea6SGuennadi Liakhovetski 164075fa9ea6SGuennadi Liakhovetski if (!status) 164175fa9ea6SGuennadi Liakhovetski return IRQ_NONE; 164275fa9ea6SGuennadi Liakhovetski 164375fa9ea6SGuennadi Liakhovetski usdhi6_write(host, USDHI6_SDIO_INFO1, ~status); 164475fa9ea6SGuennadi Liakhovetski 164575fa9ea6SGuennadi Liakhovetski mmc_signal_sdio_irq(host->mmc); 164675fa9ea6SGuennadi Liakhovetski 164775fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 164875fa9ea6SGuennadi Liakhovetski } 164975fa9ea6SGuennadi Liakhovetski 165075fa9ea6SGuennadi Liakhovetski static irqreturn_t usdhi6_cd(int irq, void *dev_id) 165175fa9ea6SGuennadi Liakhovetski { 165275fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = dev_id; 165375fa9ea6SGuennadi Liakhovetski struct mmc_host *mmc = host->mmc; 165475fa9ea6SGuennadi Liakhovetski u16 status; 165575fa9ea6SGuennadi Liakhovetski 165675fa9ea6SGuennadi Liakhovetski /* We're only interested in hotplug events here */ 165775fa9ea6SGuennadi Liakhovetski status = usdhi6_read(host, USDHI6_SD_INFO1) & ~host->status_mask & 165875fa9ea6SGuennadi Liakhovetski USDHI6_SD_INFO1_CARD; 165975fa9ea6SGuennadi Liakhovetski 166075fa9ea6SGuennadi Liakhovetski if (!status) 166175fa9ea6SGuennadi Liakhovetski return IRQ_NONE; 166275fa9ea6SGuennadi Liakhovetski 166375fa9ea6SGuennadi Liakhovetski /* Ack */ 16643fe95db1SRabin Vincent usdhi6_write(host, USDHI6_SD_INFO1, ~status); 166575fa9ea6SGuennadi Liakhovetski 166675fa9ea6SGuennadi Liakhovetski if (!work_pending(&mmc->detect.work) && 166775fa9ea6SGuennadi Liakhovetski (((status & USDHI6_SD_INFO1_CARD_INSERT) && 166875fa9ea6SGuennadi Liakhovetski !mmc->card) || 166975fa9ea6SGuennadi Liakhovetski ((status & USDHI6_SD_INFO1_CARD_EJECT) && 167075fa9ea6SGuennadi Liakhovetski mmc->card))) 167175fa9ea6SGuennadi Liakhovetski mmc_detect_change(mmc, msecs_to_jiffies(100)); 167275fa9ea6SGuennadi Liakhovetski 167375fa9ea6SGuennadi Liakhovetski return IRQ_HANDLED; 167475fa9ea6SGuennadi Liakhovetski } 167575fa9ea6SGuennadi Liakhovetski 167675fa9ea6SGuennadi Liakhovetski /* 167775fa9ea6SGuennadi Liakhovetski * Actually this should not be needed, if the built-in timeout works reliably in 167875fa9ea6SGuennadi Liakhovetski * the both PIO cases and DMA never fails. But if DMA does fail, a timeout 167975fa9ea6SGuennadi Liakhovetski * handler might be the only way to catch the error. 168075fa9ea6SGuennadi Liakhovetski */ 168175fa9ea6SGuennadi Liakhovetski static void usdhi6_timeout_work(struct work_struct *work) 168275fa9ea6SGuennadi Liakhovetski { 1683238fc95eSGeliang Tang struct delayed_work *d = to_delayed_work(work); 168475fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = container_of(d, struct usdhi6_host, timeout_work); 168575fa9ea6SGuennadi Liakhovetski struct mmc_request *mrq = host->mrq; 168675fa9ea6SGuennadi Liakhovetski struct mmc_data *data = mrq ? mrq->data : NULL; 168705caee93SRabin Vincent struct scatterlist *sg; 168875fa9ea6SGuennadi Liakhovetski 168975fa9ea6SGuennadi Liakhovetski dev_warn(mmc_dev(host->mmc), 169075fa9ea6SGuennadi Liakhovetski "%s timeout wait %u CMD%d: IRQ 0x%08x:0x%08x, last IRQ 0x%08x\n", 169175fa9ea6SGuennadi Liakhovetski host->dma_active ? "DMA" : "PIO", 169275fa9ea6SGuennadi Liakhovetski host->wait, mrq ? mrq->cmd->opcode : -1, 169375fa9ea6SGuennadi Liakhovetski usdhi6_read(host, USDHI6_SD_INFO1), 169475fa9ea6SGuennadi Liakhovetski usdhi6_read(host, USDHI6_SD_INFO2), host->irq_status); 169575fa9ea6SGuennadi Liakhovetski 169675fa9ea6SGuennadi Liakhovetski if (host->dma_active) { 169775fa9ea6SGuennadi Liakhovetski usdhi6_dma_kill(host); 169875fa9ea6SGuennadi Liakhovetski usdhi6_dma_stop_unmap(host); 169975fa9ea6SGuennadi Liakhovetski } 170075fa9ea6SGuennadi Liakhovetski 170175fa9ea6SGuennadi Liakhovetski switch (host->wait) { 170275fa9ea6SGuennadi Liakhovetski default: 170375fa9ea6SGuennadi Liakhovetski dev_err(mmc_dev(host->mmc), "Invalid state %u\n", host->wait); 1704df561f66SGustavo A. R. Silva fallthrough; /* mrq can be NULL, but is impossible */ 170575fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_CMD: 170675fa9ea6SGuennadi Liakhovetski usdhi6_error_code(host); 170775fa9ea6SGuennadi Liakhovetski if (mrq) 170875fa9ea6SGuennadi Liakhovetski mrq->cmd->error = -ETIMEDOUT; 170975fa9ea6SGuennadi Liakhovetski break; 171075fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_STOP: 171175fa9ea6SGuennadi Liakhovetski usdhi6_error_code(host); 171275fa9ea6SGuennadi Liakhovetski mrq->stop->error = -ETIMEDOUT; 171375fa9ea6SGuennadi Liakhovetski break; 171475fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_DMA: 171575fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_MREAD: 171675fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_MWRITE: 171775fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_READ: 171875fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_WRITE: 171905caee93SRabin Vincent sg = host->sg ?: data->sg; 172075fa9ea6SGuennadi Liakhovetski dev_dbg(mmc_dev(host->mmc), 172113fe0ec3SGuennadi Liakhovetski "%c: page #%u @ +0x%zx %ux%u in SG%u. Current SG %u bytes @ %u\n", 172275fa9ea6SGuennadi Liakhovetski data->flags & MMC_DATA_READ ? 'R' : 'W', host->page_idx, 172375fa9ea6SGuennadi Liakhovetski host->offset, data->blocks, data->blksz, data->sg_len, 1724bb08a7d4SRabin Vincent sg_dma_len(sg), sg->offset); 172575fa9ea6SGuennadi Liakhovetski usdhi6_sg_unmap(host, true); 1726df561f66SGustavo A. R. Silva fallthrough; /* page unmapped in USDHI6_WAIT_FOR_DATA_END */ 172775fa9ea6SGuennadi Liakhovetski case USDHI6_WAIT_FOR_DATA_END: 172875fa9ea6SGuennadi Liakhovetski usdhi6_error_code(host); 172975fa9ea6SGuennadi Liakhovetski data->error = -ETIMEDOUT; 173075fa9ea6SGuennadi Liakhovetski } 173175fa9ea6SGuennadi Liakhovetski 173275fa9ea6SGuennadi Liakhovetski if (mrq) 173375fa9ea6SGuennadi Liakhovetski usdhi6_request_done(host); 173475fa9ea6SGuennadi Liakhovetski } 173575fa9ea6SGuennadi Liakhovetski 173675fa9ea6SGuennadi Liakhovetski /* Probe / release */ 173775fa9ea6SGuennadi Liakhovetski 173875fa9ea6SGuennadi Liakhovetski static const struct of_device_id usdhi6_of_match[] = { 173975fa9ea6SGuennadi Liakhovetski {.compatible = "renesas,usdhi6rol0"}, 174075fa9ea6SGuennadi Liakhovetski {} 174175fa9ea6SGuennadi Liakhovetski }; 174275fa9ea6SGuennadi Liakhovetski MODULE_DEVICE_TABLE(of, usdhi6_of_match); 174375fa9ea6SGuennadi Liakhovetski 174475fa9ea6SGuennadi Liakhovetski static int usdhi6_probe(struct platform_device *pdev) 174575fa9ea6SGuennadi Liakhovetski { 174675fa9ea6SGuennadi Liakhovetski struct device *dev = &pdev->dev; 174775fa9ea6SGuennadi Liakhovetski struct mmc_host *mmc; 174875fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host; 174975fa9ea6SGuennadi Liakhovetski struct resource *res; 175075fa9ea6SGuennadi Liakhovetski int irq_cd, irq_sd, irq_sdio; 175175fa9ea6SGuennadi Liakhovetski u32 version; 175275fa9ea6SGuennadi Liakhovetski int ret; 175375fa9ea6SGuennadi Liakhovetski 175475fa9ea6SGuennadi Liakhovetski if (!dev->of_node) 175575fa9ea6SGuennadi Liakhovetski return -ENODEV; 175675fa9ea6SGuennadi Liakhovetski 175775fa9ea6SGuennadi Liakhovetski irq_cd = platform_get_irq_byname(pdev, "card detect"); 175875fa9ea6SGuennadi Liakhovetski irq_sd = platform_get_irq_byname(pdev, "data"); 175975fa9ea6SGuennadi Liakhovetski irq_sdio = platform_get_irq_byname(pdev, "SDIO"); 176075fa9ea6SGuennadi Liakhovetski if (irq_sd < 0 || irq_sdio < 0) 176175fa9ea6SGuennadi Liakhovetski return -ENODEV; 176275fa9ea6SGuennadi Liakhovetski 176375fa9ea6SGuennadi Liakhovetski mmc = mmc_alloc_host(sizeof(struct usdhi6_host), dev); 176475fa9ea6SGuennadi Liakhovetski if (!mmc) 176575fa9ea6SGuennadi Liakhovetski return -ENOMEM; 176675fa9ea6SGuennadi Liakhovetski 176776726472SRabin Vincent ret = mmc_regulator_get_supply(mmc); 17682d87ddd7SWolfram Sang if (ret) 176976726472SRabin Vincent goto e_free_mmc; 177076726472SRabin Vincent 177175fa9ea6SGuennadi Liakhovetski ret = mmc_of_parse(mmc); 177275fa9ea6SGuennadi Liakhovetski if (ret < 0) 177375fa9ea6SGuennadi Liakhovetski goto e_free_mmc; 177475fa9ea6SGuennadi Liakhovetski 177575fa9ea6SGuennadi Liakhovetski host = mmc_priv(mmc); 177675fa9ea6SGuennadi Liakhovetski host->mmc = mmc; 177775fa9ea6SGuennadi Liakhovetski host->wait = USDHI6_WAIT_FOR_REQUEST; 17784c2e04d7SUlf Hansson host->timeout = msecs_to_jiffies(USDHI6_REQ_TIMEOUT_MS); 17794c2e04d7SUlf Hansson /* 17804c2e04d7SUlf Hansson * We use a fixed timeout of 4s, hence inform the core about it. A 17814c2e04d7SUlf Hansson * future improvement should instead respect the cmd->busy_timeout. 17824c2e04d7SUlf Hansson */ 17834c2e04d7SUlf Hansson mmc->max_busy_timeout = USDHI6_REQ_TIMEOUT_MS; 178475fa9ea6SGuennadi Liakhovetski 1785488aab3dSLars Persson host->pinctrl = devm_pinctrl_get(&pdev->dev); 1786488aab3dSLars Persson if (IS_ERR(host->pinctrl)) { 1787488aab3dSLars Persson ret = PTR_ERR(host->pinctrl); 1788488aab3dSLars Persson goto e_free_mmc; 1789488aab3dSLars Persson } 1790488aab3dSLars Persson 1791488aab3dSLars Persson host->pins_uhs = pinctrl_lookup_state(host->pinctrl, "state_uhs"); 1792488aab3dSLars Persson 1793*cf267901SYang Li host->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); 179475fa9ea6SGuennadi Liakhovetski if (IS_ERR(host->base)) { 179575fa9ea6SGuennadi Liakhovetski ret = PTR_ERR(host->base); 179675fa9ea6SGuennadi Liakhovetski goto e_free_mmc; 179775fa9ea6SGuennadi Liakhovetski } 179875fa9ea6SGuennadi Liakhovetski 179975fa9ea6SGuennadi Liakhovetski host->clk = devm_clk_get(dev, NULL); 18003b1cac4dSJulia Lawall if (IS_ERR(host->clk)) { 18013b1cac4dSJulia Lawall ret = PTR_ERR(host->clk); 180275fa9ea6SGuennadi Liakhovetski goto e_free_mmc; 18033b1cac4dSJulia Lawall } 180475fa9ea6SGuennadi Liakhovetski 180575fa9ea6SGuennadi Liakhovetski host->imclk = clk_get_rate(host->clk); 180675fa9ea6SGuennadi Liakhovetski 180775fa9ea6SGuennadi Liakhovetski ret = clk_prepare_enable(host->clk); 180875fa9ea6SGuennadi Liakhovetski if (ret < 0) 180975fa9ea6SGuennadi Liakhovetski goto e_free_mmc; 181075fa9ea6SGuennadi Liakhovetski 181175fa9ea6SGuennadi Liakhovetski version = usdhi6_read(host, USDHI6_VERSION); 181275fa9ea6SGuennadi Liakhovetski if ((version & 0xfff) != 0xa0d) { 18132f9ae69eSZhen Lei ret = -EPERM; 181475fa9ea6SGuennadi Liakhovetski dev_err(dev, "Version not recognized %x\n", version); 181575fa9ea6SGuennadi Liakhovetski goto e_clk_off; 181675fa9ea6SGuennadi Liakhovetski } 181775fa9ea6SGuennadi Liakhovetski 181875fa9ea6SGuennadi Liakhovetski dev_info(dev, "A USDHI6ROL0 SD host detected with %d ports\n", 181975fa9ea6SGuennadi Liakhovetski usdhi6_read(host, USDHI6_SD_PORT_SEL) >> USDHI6_SD_PORT_SEL_PORTS_SHIFT); 182075fa9ea6SGuennadi Liakhovetski 182175fa9ea6SGuennadi Liakhovetski usdhi6_mask_all(host); 182275fa9ea6SGuennadi Liakhovetski 182375fa9ea6SGuennadi Liakhovetski if (irq_cd >= 0) { 182475fa9ea6SGuennadi Liakhovetski ret = devm_request_irq(dev, irq_cd, usdhi6_cd, 0, 182575fa9ea6SGuennadi Liakhovetski dev_name(dev), host); 182675fa9ea6SGuennadi Liakhovetski if (ret < 0) 182775fa9ea6SGuennadi Liakhovetski goto e_clk_off; 182875fa9ea6SGuennadi Liakhovetski } else { 182975fa9ea6SGuennadi Liakhovetski mmc->caps |= MMC_CAP_NEEDS_POLL; 183075fa9ea6SGuennadi Liakhovetski } 183175fa9ea6SGuennadi Liakhovetski 183275fa9ea6SGuennadi Liakhovetski ret = devm_request_threaded_irq(dev, irq_sd, usdhi6_sd, usdhi6_sd_bh, 0, 183375fa9ea6SGuennadi Liakhovetski dev_name(dev), host); 183475fa9ea6SGuennadi Liakhovetski if (ret < 0) 183575fa9ea6SGuennadi Liakhovetski goto e_clk_off; 183675fa9ea6SGuennadi Liakhovetski 183775fa9ea6SGuennadi Liakhovetski ret = devm_request_irq(dev, irq_sdio, usdhi6_sdio, 0, 183875fa9ea6SGuennadi Liakhovetski dev_name(dev), host); 183975fa9ea6SGuennadi Liakhovetski if (ret < 0) 184075fa9ea6SGuennadi Liakhovetski goto e_clk_off; 184175fa9ea6SGuennadi Liakhovetski 184275fa9ea6SGuennadi Liakhovetski INIT_DELAYED_WORK(&host->timeout_work, usdhi6_timeout_work); 184375fa9ea6SGuennadi Liakhovetski 184475fa9ea6SGuennadi Liakhovetski usdhi6_dma_request(host, res->start); 184575fa9ea6SGuennadi Liakhovetski 184675fa9ea6SGuennadi Liakhovetski mmc->ops = &usdhi6_ops; 184775fa9ea6SGuennadi Liakhovetski mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | 1848701dcef7SLars Persson MMC_CAP_SDIO_IRQ; 184975fa9ea6SGuennadi Liakhovetski /* Set .max_segs to some random number. Feel free to adjust. */ 185075fa9ea6SGuennadi Liakhovetski mmc->max_segs = 32; 185175fa9ea6SGuennadi Liakhovetski mmc->max_blk_size = 512; 185209cbfeafSKirill A. Shutemov mmc->max_req_size = PAGE_SIZE * mmc->max_segs; 185375fa9ea6SGuennadi Liakhovetski mmc->max_blk_count = mmc->max_req_size / mmc->max_blk_size; 185475fa9ea6SGuennadi Liakhovetski /* 185575fa9ea6SGuennadi Liakhovetski * Setting .max_seg_size to 1 page would simplify our page-mapping code, 185675fa9ea6SGuennadi Liakhovetski * But OTOH, having large segments makes DMA more efficient. We could 185775fa9ea6SGuennadi Liakhovetski * check, whether we managed to get DMA and fall back to 1 page 185875fa9ea6SGuennadi Liakhovetski * segments, but if we do manage to obtain DMA and then it fails at 185975fa9ea6SGuennadi Liakhovetski * run-time and we fall back to PIO, we will continue getting large 186075fa9ea6SGuennadi Liakhovetski * segments. So, we wouldn't be able to get rid of the code anyway. 186175fa9ea6SGuennadi Liakhovetski */ 186275fa9ea6SGuennadi Liakhovetski mmc->max_seg_size = mmc->max_req_size; 186375fa9ea6SGuennadi Liakhovetski if (!mmc->f_max) 186475fa9ea6SGuennadi Liakhovetski mmc->f_max = host->imclk; 186575fa9ea6SGuennadi Liakhovetski mmc->f_min = host->imclk / 512; 186675fa9ea6SGuennadi Liakhovetski 186775fa9ea6SGuennadi Liakhovetski platform_set_drvdata(pdev, host); 186875fa9ea6SGuennadi Liakhovetski 186975fa9ea6SGuennadi Liakhovetski ret = mmc_add_host(mmc); 187075fa9ea6SGuennadi Liakhovetski if (ret < 0) 18716052b3c3SChristophe JAILLET goto e_release_dma; 187275fa9ea6SGuennadi Liakhovetski 187375fa9ea6SGuennadi Liakhovetski return 0; 187475fa9ea6SGuennadi Liakhovetski 18756052b3c3SChristophe JAILLET e_release_dma: 18766052b3c3SChristophe JAILLET usdhi6_dma_release(host); 187775fa9ea6SGuennadi Liakhovetski e_clk_off: 187875fa9ea6SGuennadi Liakhovetski clk_disable_unprepare(host->clk); 187975fa9ea6SGuennadi Liakhovetski e_free_mmc: 188075fa9ea6SGuennadi Liakhovetski mmc_free_host(mmc); 188175fa9ea6SGuennadi Liakhovetski 188275fa9ea6SGuennadi Liakhovetski return ret; 188375fa9ea6SGuennadi Liakhovetski } 188475fa9ea6SGuennadi Liakhovetski 188575fa9ea6SGuennadi Liakhovetski static int usdhi6_remove(struct platform_device *pdev) 188675fa9ea6SGuennadi Liakhovetski { 188775fa9ea6SGuennadi Liakhovetski struct usdhi6_host *host = platform_get_drvdata(pdev); 188875fa9ea6SGuennadi Liakhovetski 188975fa9ea6SGuennadi Liakhovetski mmc_remove_host(host->mmc); 189075fa9ea6SGuennadi Liakhovetski 189175fa9ea6SGuennadi Liakhovetski usdhi6_mask_all(host); 189275fa9ea6SGuennadi Liakhovetski cancel_delayed_work_sync(&host->timeout_work); 189375fa9ea6SGuennadi Liakhovetski usdhi6_dma_release(host); 189475fa9ea6SGuennadi Liakhovetski clk_disable_unprepare(host->clk); 189575fa9ea6SGuennadi Liakhovetski mmc_free_host(host->mmc); 189675fa9ea6SGuennadi Liakhovetski 189775fa9ea6SGuennadi Liakhovetski return 0; 189875fa9ea6SGuennadi Liakhovetski } 189975fa9ea6SGuennadi Liakhovetski 190075fa9ea6SGuennadi Liakhovetski static struct platform_driver usdhi6_driver = { 190175fa9ea6SGuennadi Liakhovetski .probe = usdhi6_probe, 190275fa9ea6SGuennadi Liakhovetski .remove = usdhi6_remove, 190375fa9ea6SGuennadi Liakhovetski .driver = { 190475fa9ea6SGuennadi Liakhovetski .name = "usdhi6rol0", 190521b2cec6SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 190675fa9ea6SGuennadi Liakhovetski .of_match_table = usdhi6_of_match, 190775fa9ea6SGuennadi Liakhovetski }, 190875fa9ea6SGuennadi Liakhovetski }; 190975fa9ea6SGuennadi Liakhovetski 191075fa9ea6SGuennadi Liakhovetski module_platform_driver(usdhi6_driver); 191175fa9ea6SGuennadi Liakhovetski 191275fa9ea6SGuennadi Liakhovetski MODULE_DESCRIPTION("Renesas usdhi6rol0 SD/SDIO host driver"); 191375fa9ea6SGuennadi Liakhovetski MODULE_LICENSE("GPL v2"); 191475fa9ea6SGuennadi Liakhovetski MODULE_ALIAS("platform:usdhi6rol0"); 191575fa9ea6SGuennadi Liakhovetski MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>"); 1916