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