161bfbdb8SLars-Peter Clausen /* 261bfbdb8SLars-Peter Clausen * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 361bfbdb8SLars-Peter Clausen * JZ4740 SD/MMC controller driver 461bfbdb8SLars-Peter Clausen * 561bfbdb8SLars-Peter Clausen * This program is free software; you can redistribute it and/or modify it 661bfbdb8SLars-Peter Clausen * under the terms of the GNU General Public License as published by the 761bfbdb8SLars-Peter Clausen * Free Software Foundation; either version 2 of the License, or (at your 861bfbdb8SLars-Peter Clausen * option) any later version. 961bfbdb8SLars-Peter Clausen * 1061bfbdb8SLars-Peter Clausen * You should have received a copy of the GNU General Public License along 1161bfbdb8SLars-Peter Clausen * with this program; if not, write to the Free Software Foundation, Inc., 1261bfbdb8SLars-Peter Clausen * 675 Mass Ave, Cambridge, MA 02139, USA. 1361bfbdb8SLars-Peter Clausen * 1461bfbdb8SLars-Peter Clausen */ 1561bfbdb8SLars-Peter Clausen 1661bfbdb8SLars-Peter Clausen #include <linux/mmc/host.h> 173119cbdaSJamie Iles #include <linux/err.h> 1861bfbdb8SLars-Peter Clausen #include <linux/io.h> 1961bfbdb8SLars-Peter Clausen #include <linux/irq.h> 2061bfbdb8SLars-Peter Clausen #include <linux/interrupt.h> 2161bfbdb8SLars-Peter Clausen #include <linux/module.h> 2261bfbdb8SLars-Peter Clausen #include <linux/platform_device.h> 2361bfbdb8SLars-Peter Clausen #include <linux/delay.h> 2461bfbdb8SLars-Peter Clausen #include <linux/scatterlist.h> 2561bfbdb8SLars-Peter Clausen #include <linux/clk.h> 2661bfbdb8SLars-Peter Clausen 2761bfbdb8SLars-Peter Clausen #include <linux/bitops.h> 2861bfbdb8SLars-Peter Clausen #include <linux/gpio.h> 2961bfbdb8SLars-Peter Clausen #include <asm/mach-jz4740/gpio.h> 3061bfbdb8SLars-Peter Clausen #include <asm/cacheflush.h> 3161bfbdb8SLars-Peter Clausen #include <linux/dma-mapping.h> 3261bfbdb8SLars-Peter Clausen 3361bfbdb8SLars-Peter Clausen #include <asm/mach-jz4740/jz4740_mmc.h> 3461bfbdb8SLars-Peter Clausen 3561bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STRPCL 0x00 3661bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_STATUS 0x04 3761bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CLKRT 0x08 3861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMDAT 0x0C 3961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESTO 0x10 4061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RDTO 0x14 4161bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_BLKLEN 0x18 4261bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_NOB 0x1C 4361bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_SNOB 0x20 4461bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IMASK 0x24 4561bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_IREG 0x28 4661bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_CMD 0x2C 4761bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_ARG 0x30 4861bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RESP_FIFO 0x34 4961bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_RXFIFO 0x38 5061bfbdb8SLars-Peter Clausen #define JZ_REG_MMC_TXFIFO 0x3C 5161bfbdb8SLars-Peter Clausen 5261bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) 5361bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) 5461bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_READWAIT BIT(5) 5561bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) 5661bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_RESET BIT(3) 5761bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_START_OP BIT(2) 5861bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_CONTROL (BIT(1) | BIT(0)) 5961bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) 6061bfbdb8SLars-Peter Clausen #define JZ_MMC_STRPCL_CLOCK_START BIT(1) 6161bfbdb8SLars-Peter Clausen 6261bfbdb8SLars-Peter Clausen 6361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_RESETTING BIT(15) 6461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) 6561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_PRG_DONE BIT(13) 6661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) 6761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_END_CMD_RES BIT(11) 6861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) 6961bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_IS_READWAIT BIT(9) 7061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CLK_EN BIT(8) 7161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) 7261bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) 7361bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) 7461bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) 7561bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) 7661bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) 7761bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) 7861bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) 7961bfbdb8SLars-Peter Clausen 8061bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) 8161bfbdb8SLars-Peter Clausen #define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) 8261bfbdb8SLars-Peter Clausen 8361bfbdb8SLars-Peter Clausen 8461bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_IO_ABORT BIT(11) 8561bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) 8661bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DMA_EN BIT(8) 8761bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_INIT BIT(7) 8861bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_BUSY BIT(6) 8961bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_STREAM BIT(5) 9061bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_WRITE BIT(4) 9161bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_DATA_EN BIT(3) 9261bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RESPONSE_FORMAT (BIT(2) | BIT(1) | BIT(0)) 9361bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R1 1 9461bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R2 2 9561bfbdb8SLars-Peter Clausen #define JZ_MMC_CMDAT_RSP_R3 3 9661bfbdb8SLars-Peter Clausen 9761bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_SDIO BIT(7) 9861bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) 9961bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) 10061bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_END_CMD_RES BIT(2) 10161bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_PRG_DONE BIT(1) 10261bfbdb8SLars-Peter Clausen #define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) 10361bfbdb8SLars-Peter Clausen 10461bfbdb8SLars-Peter Clausen 10561bfbdb8SLars-Peter Clausen #define JZ_MMC_CLK_RATE 24000000 10661bfbdb8SLars-Peter Clausen 10761bfbdb8SLars-Peter Clausen enum jz4740_mmc_state { 10861bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_READ_RESPONSE, 10961bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_TRANSFER_DATA, 11061bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_SEND_STOP, 11161bfbdb8SLars-Peter Clausen JZ4740_MMC_STATE_DONE, 11261bfbdb8SLars-Peter Clausen }; 11361bfbdb8SLars-Peter Clausen 11461bfbdb8SLars-Peter Clausen struct jz4740_mmc_host { 11561bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 11661bfbdb8SLars-Peter Clausen struct platform_device *pdev; 11761bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata; 11861bfbdb8SLars-Peter Clausen struct clk *clk; 11961bfbdb8SLars-Peter Clausen 12061bfbdb8SLars-Peter Clausen int irq; 12161bfbdb8SLars-Peter Clausen int card_detect_irq; 12261bfbdb8SLars-Peter Clausen 12361bfbdb8SLars-Peter Clausen struct resource *mem; 12461bfbdb8SLars-Peter Clausen void __iomem *base; 12561bfbdb8SLars-Peter Clausen struct mmc_request *req; 12661bfbdb8SLars-Peter Clausen struct mmc_command *cmd; 12761bfbdb8SLars-Peter Clausen 12861bfbdb8SLars-Peter Clausen unsigned long waiting; 12961bfbdb8SLars-Peter Clausen 13061bfbdb8SLars-Peter Clausen uint32_t cmdat; 13161bfbdb8SLars-Peter Clausen 13261bfbdb8SLars-Peter Clausen uint16_t irq_mask; 13361bfbdb8SLars-Peter Clausen 13461bfbdb8SLars-Peter Clausen spinlock_t lock; 13561bfbdb8SLars-Peter Clausen 13661bfbdb8SLars-Peter Clausen struct timer_list timeout_timer; 13761bfbdb8SLars-Peter Clausen struct sg_mapping_iter miter; 13861bfbdb8SLars-Peter Clausen enum jz4740_mmc_state state; 13961bfbdb8SLars-Peter Clausen }; 14061bfbdb8SLars-Peter Clausen 14161bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_irq_enabled(struct jz4740_mmc_host *host, 14261bfbdb8SLars-Peter Clausen unsigned int irq, bool enabled) 14361bfbdb8SLars-Peter Clausen { 14461bfbdb8SLars-Peter Clausen unsigned long flags; 14561bfbdb8SLars-Peter Clausen 14661bfbdb8SLars-Peter Clausen spin_lock_irqsave(&host->lock, flags); 14761bfbdb8SLars-Peter Clausen if (enabled) 14861bfbdb8SLars-Peter Clausen host->irq_mask &= ~irq; 14961bfbdb8SLars-Peter Clausen else 15061bfbdb8SLars-Peter Clausen host->irq_mask |= irq; 15161bfbdb8SLars-Peter Clausen spin_unlock_irqrestore(&host->lock, flags); 15261bfbdb8SLars-Peter Clausen 15361bfbdb8SLars-Peter Clausen writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); 15461bfbdb8SLars-Peter Clausen } 15561bfbdb8SLars-Peter Clausen 15661bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, 15761bfbdb8SLars-Peter Clausen bool start_transfer) 15861bfbdb8SLars-Peter Clausen { 15961bfbdb8SLars-Peter Clausen uint16_t val = JZ_MMC_STRPCL_CLOCK_START; 16061bfbdb8SLars-Peter Clausen 16161bfbdb8SLars-Peter Clausen if (start_transfer) 16261bfbdb8SLars-Peter Clausen val |= JZ_MMC_STRPCL_START_OP; 16361bfbdb8SLars-Peter Clausen 16461bfbdb8SLars-Peter Clausen writew(val, host->base + JZ_REG_MMC_STRPCL); 16561bfbdb8SLars-Peter Clausen } 16661bfbdb8SLars-Peter Clausen 16761bfbdb8SLars-Peter Clausen static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) 16861bfbdb8SLars-Peter Clausen { 16961bfbdb8SLars-Peter Clausen uint32_t status; 17061bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 17161bfbdb8SLars-Peter Clausen 17261bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); 17361bfbdb8SLars-Peter Clausen do { 17461bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 17561bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_CLK_EN && --timeout); 17661bfbdb8SLars-Peter Clausen } 17761bfbdb8SLars-Peter Clausen 17861bfbdb8SLars-Peter Clausen static void jz4740_mmc_reset(struct jz4740_mmc_host *host) 17961bfbdb8SLars-Peter Clausen { 18061bfbdb8SLars-Peter Clausen uint32_t status; 18161bfbdb8SLars-Peter Clausen unsigned int timeout = 1000; 18261bfbdb8SLars-Peter Clausen 18361bfbdb8SLars-Peter Clausen writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); 18461bfbdb8SLars-Peter Clausen udelay(10); 18561bfbdb8SLars-Peter Clausen do { 18661bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 18761bfbdb8SLars-Peter Clausen } while (status & JZ_MMC_STATUS_IS_RESETTING && --timeout); 18861bfbdb8SLars-Peter Clausen } 18961bfbdb8SLars-Peter Clausen 19061bfbdb8SLars-Peter Clausen static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) 19161bfbdb8SLars-Peter Clausen { 19261bfbdb8SLars-Peter Clausen struct mmc_request *req; 19361bfbdb8SLars-Peter Clausen 19461bfbdb8SLars-Peter Clausen req = host->req; 19561bfbdb8SLars-Peter Clausen host->req = NULL; 19661bfbdb8SLars-Peter Clausen 19761bfbdb8SLars-Peter Clausen mmc_request_done(host->mmc, req); 19861bfbdb8SLars-Peter Clausen } 19961bfbdb8SLars-Peter Clausen 20061bfbdb8SLars-Peter Clausen static unsigned int jz4740_mmc_poll_irq(struct jz4740_mmc_host *host, 20161bfbdb8SLars-Peter Clausen unsigned int irq) 20261bfbdb8SLars-Peter Clausen { 20361bfbdb8SLars-Peter Clausen unsigned int timeout = 0x800; 20461bfbdb8SLars-Peter Clausen uint16_t status; 20561bfbdb8SLars-Peter Clausen 20661bfbdb8SLars-Peter Clausen do { 20761bfbdb8SLars-Peter Clausen status = readw(host->base + JZ_REG_MMC_IREG); 20861bfbdb8SLars-Peter Clausen } while (!(status & irq) && --timeout); 20961bfbdb8SLars-Peter Clausen 21061bfbdb8SLars-Peter Clausen if (timeout == 0) { 21161bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 21261bfbdb8SLars-Peter Clausen mod_timer(&host->timeout_timer, jiffies + 5*HZ); 21361bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq, true); 21461bfbdb8SLars-Peter Clausen return true; 21561bfbdb8SLars-Peter Clausen } 21661bfbdb8SLars-Peter Clausen 21761bfbdb8SLars-Peter Clausen return false; 21861bfbdb8SLars-Peter Clausen } 21961bfbdb8SLars-Peter Clausen 22061bfbdb8SLars-Peter Clausen static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, 22161bfbdb8SLars-Peter Clausen struct mmc_data *data) 22261bfbdb8SLars-Peter Clausen { 22361bfbdb8SLars-Peter Clausen int status; 22461bfbdb8SLars-Peter Clausen 22561bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 22661bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) { 22761bfbdb8SLars-Peter Clausen if (status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { 22861bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 22961bfbdb8SLars-Peter Clausen data->error = -ETIMEDOUT; 23061bfbdb8SLars-Peter Clausen } else { 23161bfbdb8SLars-Peter Clausen host->req->cmd->error = -EIO; 23261bfbdb8SLars-Peter Clausen data->error = -EIO; 23361bfbdb8SLars-Peter Clausen } 23461bfbdb8SLars-Peter Clausen } 23561bfbdb8SLars-Peter Clausen } 23661bfbdb8SLars-Peter Clausen 23761bfbdb8SLars-Peter Clausen static bool jz4740_mmc_write_data(struct jz4740_mmc_host *host, 23861bfbdb8SLars-Peter Clausen struct mmc_data *data) 23961bfbdb8SLars-Peter Clausen { 24061bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 24161bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_TXFIFO; 24261bfbdb8SLars-Peter Clausen uint32_t *buf; 24361bfbdb8SLars-Peter Clausen bool timeout; 24461bfbdb8SLars-Peter Clausen size_t i, j; 24561bfbdb8SLars-Peter Clausen 24661bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 24761bfbdb8SLars-Peter Clausen buf = miter->addr; 24861bfbdb8SLars-Peter Clausen i = miter->length / 4; 24961bfbdb8SLars-Peter Clausen j = i / 8; 25061bfbdb8SLars-Peter Clausen i = i & 0x7; 25161bfbdb8SLars-Peter Clausen while (j) { 25261bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 25361bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 25461bfbdb8SLars-Peter Clausen goto poll_timeout; 25561bfbdb8SLars-Peter Clausen 25661bfbdb8SLars-Peter Clausen writel(buf[0], fifo_addr); 25761bfbdb8SLars-Peter Clausen writel(buf[1], fifo_addr); 25861bfbdb8SLars-Peter Clausen writel(buf[2], fifo_addr); 25961bfbdb8SLars-Peter Clausen writel(buf[3], fifo_addr); 26061bfbdb8SLars-Peter Clausen writel(buf[4], fifo_addr); 26161bfbdb8SLars-Peter Clausen writel(buf[5], fifo_addr); 26261bfbdb8SLars-Peter Clausen writel(buf[6], fifo_addr); 26361bfbdb8SLars-Peter Clausen writel(buf[7], fifo_addr); 26461bfbdb8SLars-Peter Clausen buf += 8; 26561bfbdb8SLars-Peter Clausen --j; 26661bfbdb8SLars-Peter Clausen } 26761bfbdb8SLars-Peter Clausen if (unlikely(i)) { 26861bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_TXFIFO_WR_REQ); 26961bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 27061bfbdb8SLars-Peter Clausen goto poll_timeout; 27161bfbdb8SLars-Peter Clausen 27261bfbdb8SLars-Peter Clausen while (i) { 27361bfbdb8SLars-Peter Clausen writel(*buf, fifo_addr); 27461bfbdb8SLars-Peter Clausen ++buf; 27561bfbdb8SLars-Peter Clausen --i; 27661bfbdb8SLars-Peter Clausen } 27761bfbdb8SLars-Peter Clausen } 27861bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 27961bfbdb8SLars-Peter Clausen } 28061bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 28161bfbdb8SLars-Peter Clausen 28261bfbdb8SLars-Peter Clausen return false; 28361bfbdb8SLars-Peter Clausen 28461bfbdb8SLars-Peter Clausen poll_timeout: 28561bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 28661bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 28761bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 28861bfbdb8SLars-Peter Clausen 28961bfbdb8SLars-Peter Clausen return true; 29061bfbdb8SLars-Peter Clausen } 29161bfbdb8SLars-Peter Clausen 29261bfbdb8SLars-Peter Clausen static bool jz4740_mmc_read_data(struct jz4740_mmc_host *host, 29361bfbdb8SLars-Peter Clausen struct mmc_data *data) 29461bfbdb8SLars-Peter Clausen { 29561bfbdb8SLars-Peter Clausen struct sg_mapping_iter *miter = &host->miter; 29661bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RXFIFO; 29761bfbdb8SLars-Peter Clausen uint32_t *buf; 29861bfbdb8SLars-Peter Clausen uint32_t d; 29961bfbdb8SLars-Peter Clausen uint16_t status; 30061bfbdb8SLars-Peter Clausen size_t i, j; 30161bfbdb8SLars-Peter Clausen unsigned int timeout; 30261bfbdb8SLars-Peter Clausen 30361bfbdb8SLars-Peter Clausen while (sg_miter_next(miter)) { 30461bfbdb8SLars-Peter Clausen buf = miter->addr; 30561bfbdb8SLars-Peter Clausen i = miter->length; 30661bfbdb8SLars-Peter Clausen j = i / 32; 30761bfbdb8SLars-Peter Clausen i = i & 0x1f; 30861bfbdb8SLars-Peter Clausen while (j) { 30961bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 31061bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 31161bfbdb8SLars-Peter Clausen goto poll_timeout; 31261bfbdb8SLars-Peter Clausen 31361bfbdb8SLars-Peter Clausen buf[0] = readl(fifo_addr); 31461bfbdb8SLars-Peter Clausen buf[1] = readl(fifo_addr); 31561bfbdb8SLars-Peter Clausen buf[2] = readl(fifo_addr); 31661bfbdb8SLars-Peter Clausen buf[3] = readl(fifo_addr); 31761bfbdb8SLars-Peter Clausen buf[4] = readl(fifo_addr); 31861bfbdb8SLars-Peter Clausen buf[5] = readl(fifo_addr); 31961bfbdb8SLars-Peter Clausen buf[6] = readl(fifo_addr); 32061bfbdb8SLars-Peter Clausen buf[7] = readl(fifo_addr); 32161bfbdb8SLars-Peter Clausen 32261bfbdb8SLars-Peter Clausen buf += 8; 32361bfbdb8SLars-Peter Clausen --j; 32461bfbdb8SLars-Peter Clausen } 32561bfbdb8SLars-Peter Clausen 32661bfbdb8SLars-Peter Clausen if (unlikely(i)) { 32761bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_RXFIFO_RD_REQ); 32861bfbdb8SLars-Peter Clausen if (unlikely(timeout)) 32961bfbdb8SLars-Peter Clausen goto poll_timeout; 33061bfbdb8SLars-Peter Clausen 33161bfbdb8SLars-Peter Clausen while (i >= 4) { 33261bfbdb8SLars-Peter Clausen *buf++ = readl(fifo_addr); 33361bfbdb8SLars-Peter Clausen i -= 4; 33461bfbdb8SLars-Peter Clausen } 33561bfbdb8SLars-Peter Clausen if (unlikely(i > 0)) { 33661bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 33761bfbdb8SLars-Peter Clausen memcpy(buf, &d, i); 33861bfbdb8SLars-Peter Clausen } 33961bfbdb8SLars-Peter Clausen } 34061bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->length; 34161bfbdb8SLars-Peter Clausen 34261bfbdb8SLars-Peter Clausen /* This can go away once MIPS implements 34361bfbdb8SLars-Peter Clausen * flush_kernel_dcache_page */ 34461bfbdb8SLars-Peter Clausen flush_dcache_page(miter->page); 34561bfbdb8SLars-Peter Clausen } 34661bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 34761bfbdb8SLars-Peter Clausen 34861bfbdb8SLars-Peter Clausen /* For whatever reason there is sometime one word more in the fifo then 34961bfbdb8SLars-Peter Clausen * requested */ 35061bfbdb8SLars-Peter Clausen timeout = 1000; 35161bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 35261bfbdb8SLars-Peter Clausen while (!(status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) && --timeout) { 35361bfbdb8SLars-Peter Clausen d = readl(fifo_addr); 35461bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 35561bfbdb8SLars-Peter Clausen } 35661bfbdb8SLars-Peter Clausen 35761bfbdb8SLars-Peter Clausen return false; 35861bfbdb8SLars-Peter Clausen 35961bfbdb8SLars-Peter Clausen poll_timeout: 36061bfbdb8SLars-Peter Clausen miter->consumed = (void *)buf - miter->addr; 36161bfbdb8SLars-Peter Clausen data->bytes_xfered += miter->consumed; 36261bfbdb8SLars-Peter Clausen sg_miter_stop(miter); 36361bfbdb8SLars-Peter Clausen 36461bfbdb8SLars-Peter Clausen return true; 36561bfbdb8SLars-Peter Clausen } 36661bfbdb8SLars-Peter Clausen 36761bfbdb8SLars-Peter Clausen static void jz4740_mmc_timeout(unsigned long data) 36861bfbdb8SLars-Peter Clausen { 36961bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)data; 37061bfbdb8SLars-Peter Clausen 37161bfbdb8SLars-Peter Clausen if (!test_and_clear_bit(0, &host->waiting)) 37261bfbdb8SLars-Peter Clausen return; 37361bfbdb8SLars-Peter Clausen 37461bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, false); 37561bfbdb8SLars-Peter Clausen 37661bfbdb8SLars-Peter Clausen host->req->cmd->error = -ETIMEDOUT; 37761bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 37861bfbdb8SLars-Peter Clausen } 37961bfbdb8SLars-Peter Clausen 38061bfbdb8SLars-Peter Clausen static void jz4740_mmc_read_response(struct jz4740_mmc_host *host, 38161bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 38261bfbdb8SLars-Peter Clausen { 38361bfbdb8SLars-Peter Clausen int i; 38461bfbdb8SLars-Peter Clausen uint16_t tmp; 38561bfbdb8SLars-Peter Clausen void __iomem *fifo_addr = host->base + JZ_REG_MMC_RESP_FIFO; 38661bfbdb8SLars-Peter Clausen 38761bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_136) { 38861bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 38961bfbdb8SLars-Peter Clausen for (i = 0; i < 4; ++i) { 39061bfbdb8SLars-Peter Clausen cmd->resp[i] = tmp << 24; 39161bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 39261bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp << 8; 39361bfbdb8SLars-Peter Clausen tmp = readw(fifo_addr); 39461bfbdb8SLars-Peter Clausen cmd->resp[i] |= tmp >> 8; 39561bfbdb8SLars-Peter Clausen } 39661bfbdb8SLars-Peter Clausen } else { 39761bfbdb8SLars-Peter Clausen cmd->resp[0] = readw(fifo_addr) << 24; 39861bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) << 8; 39961bfbdb8SLars-Peter Clausen cmd->resp[0] |= readw(fifo_addr) & 0xff; 40061bfbdb8SLars-Peter Clausen } 40161bfbdb8SLars-Peter Clausen } 40261bfbdb8SLars-Peter Clausen 40361bfbdb8SLars-Peter Clausen static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, 40461bfbdb8SLars-Peter Clausen struct mmc_command *cmd) 40561bfbdb8SLars-Peter Clausen { 40661bfbdb8SLars-Peter Clausen uint32_t cmdat = host->cmdat; 40761bfbdb8SLars-Peter Clausen 40861bfbdb8SLars-Peter Clausen host->cmdat &= ~JZ_MMC_CMDAT_INIT; 40961bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 41061bfbdb8SLars-Peter Clausen 41161bfbdb8SLars-Peter Clausen host->cmd = cmd; 41261bfbdb8SLars-Peter Clausen 41361bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_BUSY) 41461bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_BUSY; 41561bfbdb8SLars-Peter Clausen 41661bfbdb8SLars-Peter Clausen switch (mmc_resp_type(cmd)) { 41761bfbdb8SLars-Peter Clausen case MMC_RSP_R1B: 41861bfbdb8SLars-Peter Clausen case MMC_RSP_R1: 41961bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R1; 42061bfbdb8SLars-Peter Clausen break; 42161bfbdb8SLars-Peter Clausen case MMC_RSP_R2: 42261bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R2; 42361bfbdb8SLars-Peter Clausen break; 42461bfbdb8SLars-Peter Clausen case MMC_RSP_R3: 42561bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_RSP_R3; 42661bfbdb8SLars-Peter Clausen break; 42761bfbdb8SLars-Peter Clausen default: 42861bfbdb8SLars-Peter Clausen break; 42961bfbdb8SLars-Peter Clausen } 43061bfbdb8SLars-Peter Clausen 43161bfbdb8SLars-Peter Clausen if (cmd->data) { 43261bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_DATA_EN; 43361bfbdb8SLars-Peter Clausen if (cmd->data->flags & MMC_DATA_WRITE) 43461bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_WRITE; 43561bfbdb8SLars-Peter Clausen if (cmd->data->flags & MMC_DATA_STREAM) 43661bfbdb8SLars-Peter Clausen cmdat |= JZ_MMC_CMDAT_STREAM; 43761bfbdb8SLars-Peter Clausen 43861bfbdb8SLars-Peter Clausen writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); 43961bfbdb8SLars-Peter Clausen writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); 44061bfbdb8SLars-Peter Clausen } 44161bfbdb8SLars-Peter Clausen 44261bfbdb8SLars-Peter Clausen writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); 44361bfbdb8SLars-Peter Clausen writel(cmd->arg, host->base + JZ_REG_MMC_ARG); 44461bfbdb8SLars-Peter Clausen writel(cmdat, host->base + JZ_REG_MMC_CMDAT); 44561bfbdb8SLars-Peter Clausen 44661bfbdb8SLars-Peter Clausen jz4740_mmc_clock_enable(host, 1); 44761bfbdb8SLars-Peter Clausen } 44861bfbdb8SLars-Peter Clausen 44961bfbdb8SLars-Peter Clausen static void jz_mmc_prepare_data_transfer(struct jz4740_mmc_host *host) 45061bfbdb8SLars-Peter Clausen { 45161bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 45261bfbdb8SLars-Peter Clausen struct mmc_data *data = cmd->data; 45361bfbdb8SLars-Peter Clausen int direction; 45461bfbdb8SLars-Peter Clausen 45561bfbdb8SLars-Peter Clausen if (data->flags & MMC_DATA_READ) 45661bfbdb8SLars-Peter Clausen direction = SG_MITER_TO_SG; 45761bfbdb8SLars-Peter Clausen else 45861bfbdb8SLars-Peter Clausen direction = SG_MITER_FROM_SG; 45961bfbdb8SLars-Peter Clausen 46061bfbdb8SLars-Peter Clausen sg_miter_start(&host->miter, data->sg, data->sg_len, direction); 46161bfbdb8SLars-Peter Clausen } 46261bfbdb8SLars-Peter Clausen 46361bfbdb8SLars-Peter Clausen 46461bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) 46561bfbdb8SLars-Peter Clausen { 46661bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = (struct jz4740_mmc_host *)devid; 46761bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->req->cmd; 46861bfbdb8SLars-Peter Clausen struct mmc_request *req = host->req; 46961bfbdb8SLars-Peter Clausen bool timeout = false; 47061bfbdb8SLars-Peter Clausen 47161bfbdb8SLars-Peter Clausen if (cmd->error) 47261bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 47361bfbdb8SLars-Peter Clausen 47461bfbdb8SLars-Peter Clausen switch (host->state) { 47561bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_READ_RESPONSE: 47661bfbdb8SLars-Peter Clausen if (cmd->flags & MMC_RSP_PRESENT) 47761bfbdb8SLars-Peter Clausen jz4740_mmc_read_response(host, cmd); 47861bfbdb8SLars-Peter Clausen 47961bfbdb8SLars-Peter Clausen if (!cmd->data) 48061bfbdb8SLars-Peter Clausen break; 48161bfbdb8SLars-Peter Clausen 48261bfbdb8SLars-Peter Clausen jz_mmc_prepare_data_transfer(host); 48361bfbdb8SLars-Peter Clausen 48461bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_TRANSFER_DATA: 48561bfbdb8SLars-Peter Clausen if (cmd->data->flags & MMC_DATA_READ) 48661bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_read_data(host, cmd->data); 48761bfbdb8SLars-Peter Clausen else 48861bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_write_data(host, cmd->data); 48961bfbdb8SLars-Peter Clausen 49061bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 49161bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_TRANSFER_DATA; 49261bfbdb8SLars-Peter Clausen break; 49361bfbdb8SLars-Peter Clausen } 49461bfbdb8SLars-Peter Clausen 49561bfbdb8SLars-Peter Clausen jz4740_mmc_transfer_check_state(host, cmd->data); 49661bfbdb8SLars-Peter Clausen 49761bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_DATA_TRAN_DONE); 49861bfbdb8SLars-Peter Clausen if (unlikely(timeout)) { 49961bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_SEND_STOP; 50061bfbdb8SLars-Peter Clausen break; 50161bfbdb8SLars-Peter Clausen } 50261bfbdb8SLars-Peter Clausen writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); 50361bfbdb8SLars-Peter Clausen 50461bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_SEND_STOP: 50561bfbdb8SLars-Peter Clausen if (!req->stop) 50661bfbdb8SLars-Peter Clausen break; 50761bfbdb8SLars-Peter Clausen 50861bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->stop); 50961bfbdb8SLars-Peter Clausen 51061bfbdb8SLars-Peter Clausen timeout = jz4740_mmc_poll_irq(host, JZ_MMC_IRQ_PRG_DONE); 51161bfbdb8SLars-Peter Clausen if (timeout) { 51261bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_DONE; 51361bfbdb8SLars-Peter Clausen break; 51461bfbdb8SLars-Peter Clausen } 51561bfbdb8SLars-Peter Clausen case JZ4740_MMC_STATE_DONE: 51661bfbdb8SLars-Peter Clausen break; 51761bfbdb8SLars-Peter Clausen } 51861bfbdb8SLars-Peter Clausen 51961bfbdb8SLars-Peter Clausen if (!timeout) 52061bfbdb8SLars-Peter Clausen jz4740_mmc_request_done(host); 52161bfbdb8SLars-Peter Clausen 52261bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 52361bfbdb8SLars-Peter Clausen } 52461bfbdb8SLars-Peter Clausen 52561bfbdb8SLars-Peter Clausen static irqreturn_t jz_mmc_irq(int irq, void *devid) 52661bfbdb8SLars-Peter Clausen { 52761bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = devid; 52861bfbdb8SLars-Peter Clausen struct mmc_command *cmd = host->cmd; 52961bfbdb8SLars-Peter Clausen uint16_t irq_reg, status, tmp; 53061bfbdb8SLars-Peter Clausen 53161bfbdb8SLars-Peter Clausen irq_reg = readw(host->base + JZ_REG_MMC_IREG); 53261bfbdb8SLars-Peter Clausen 53361bfbdb8SLars-Peter Clausen tmp = irq_reg; 53461bfbdb8SLars-Peter Clausen irq_reg &= ~host->irq_mask; 53561bfbdb8SLars-Peter Clausen 53661bfbdb8SLars-Peter Clausen tmp &= ~(JZ_MMC_IRQ_TXFIFO_WR_REQ | JZ_MMC_IRQ_RXFIFO_RD_REQ | 53761bfbdb8SLars-Peter Clausen JZ_MMC_IRQ_PRG_DONE | JZ_MMC_IRQ_DATA_TRAN_DONE); 53861bfbdb8SLars-Peter Clausen 53961bfbdb8SLars-Peter Clausen if (tmp != irq_reg) 54061bfbdb8SLars-Peter Clausen writew(tmp & ~irq_reg, host->base + JZ_REG_MMC_IREG); 54161bfbdb8SLars-Peter Clausen 54261bfbdb8SLars-Peter Clausen if (irq_reg & JZ_MMC_IRQ_SDIO) { 54361bfbdb8SLars-Peter Clausen writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG); 54461bfbdb8SLars-Peter Clausen mmc_signal_sdio_irq(host->mmc); 54561bfbdb8SLars-Peter Clausen irq_reg &= ~JZ_MMC_IRQ_SDIO; 54661bfbdb8SLars-Peter Clausen } 54761bfbdb8SLars-Peter Clausen 54861bfbdb8SLars-Peter Clausen if (host->req && cmd && irq_reg) { 54961bfbdb8SLars-Peter Clausen if (test_and_clear_bit(0, &host->waiting)) { 55061bfbdb8SLars-Peter Clausen del_timer(&host->timeout_timer); 55161bfbdb8SLars-Peter Clausen 55261bfbdb8SLars-Peter Clausen status = readl(host->base + JZ_REG_MMC_STATUS); 55361bfbdb8SLars-Peter Clausen 55461bfbdb8SLars-Peter Clausen if (status & JZ_MMC_STATUS_TIMEOUT_RES) { 55561bfbdb8SLars-Peter Clausen cmd->error = -ETIMEDOUT; 55661bfbdb8SLars-Peter Clausen } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { 55761bfbdb8SLars-Peter Clausen cmd->error = -EIO; 55861bfbdb8SLars-Peter Clausen } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | 55961bfbdb8SLars-Peter Clausen JZ_MMC_STATUS_CRC_WRITE_ERROR)) { 56061bfbdb8SLars-Peter Clausen if (cmd->data) 56161bfbdb8SLars-Peter Clausen cmd->data->error = -EIO; 56261bfbdb8SLars-Peter Clausen cmd->error = -EIO; 56361bfbdb8SLars-Peter Clausen } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | 56461bfbdb8SLars-Peter Clausen JZ_MMC_STATUS_CRC_WRITE_ERROR)) { 56561bfbdb8SLars-Peter Clausen if (cmd->data) 56661bfbdb8SLars-Peter Clausen cmd->data->error = -EIO; 56761bfbdb8SLars-Peter Clausen cmd->error = -EIO; 56861bfbdb8SLars-Peter Clausen } 56961bfbdb8SLars-Peter Clausen 57061bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, irq_reg, false); 57161bfbdb8SLars-Peter Clausen writew(irq_reg, host->base + JZ_REG_MMC_IREG); 57261bfbdb8SLars-Peter Clausen 57361bfbdb8SLars-Peter Clausen return IRQ_WAKE_THREAD; 57461bfbdb8SLars-Peter Clausen } 57561bfbdb8SLars-Peter Clausen } 57661bfbdb8SLars-Peter Clausen 57761bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 57861bfbdb8SLars-Peter Clausen } 57961bfbdb8SLars-Peter Clausen 58061bfbdb8SLars-Peter Clausen static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) 58161bfbdb8SLars-Peter Clausen { 58261bfbdb8SLars-Peter Clausen int div = 0; 58361bfbdb8SLars-Peter Clausen int real_rate; 58461bfbdb8SLars-Peter Clausen 58561bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 58661bfbdb8SLars-Peter Clausen clk_set_rate(host->clk, JZ_MMC_CLK_RATE); 58761bfbdb8SLars-Peter Clausen 58861bfbdb8SLars-Peter Clausen real_rate = clk_get_rate(host->clk); 58961bfbdb8SLars-Peter Clausen 59061bfbdb8SLars-Peter Clausen while (real_rate > rate && div < 7) { 59161bfbdb8SLars-Peter Clausen ++div; 59261bfbdb8SLars-Peter Clausen real_rate >>= 1; 59361bfbdb8SLars-Peter Clausen } 59461bfbdb8SLars-Peter Clausen 59561bfbdb8SLars-Peter Clausen writew(div, host->base + JZ_REG_MMC_CLKRT); 59661bfbdb8SLars-Peter Clausen return real_rate; 59761bfbdb8SLars-Peter Clausen } 59861bfbdb8SLars-Peter Clausen 59961bfbdb8SLars-Peter Clausen static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) 60061bfbdb8SLars-Peter Clausen { 60161bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 60261bfbdb8SLars-Peter Clausen 60361bfbdb8SLars-Peter Clausen host->req = req; 60461bfbdb8SLars-Peter Clausen 60561bfbdb8SLars-Peter Clausen writew(0xffff, host->base + JZ_REG_MMC_IREG); 60661bfbdb8SLars-Peter Clausen 60761bfbdb8SLars-Peter Clausen writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); 60861bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_END_CMD_RES, true); 60961bfbdb8SLars-Peter Clausen 61061bfbdb8SLars-Peter Clausen host->state = JZ4740_MMC_STATE_READ_RESPONSE; 61161bfbdb8SLars-Peter Clausen set_bit(0, &host->waiting); 61261bfbdb8SLars-Peter Clausen mod_timer(&host->timeout_timer, jiffies + 5*HZ); 61361bfbdb8SLars-Peter Clausen jz4740_mmc_send_command(host, req->cmd); 61461bfbdb8SLars-Peter Clausen } 61561bfbdb8SLars-Peter Clausen 61661bfbdb8SLars-Peter Clausen static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) 61761bfbdb8SLars-Peter Clausen { 61861bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 61961bfbdb8SLars-Peter Clausen if (ios->clock) 62061bfbdb8SLars-Peter Clausen jz4740_mmc_set_clock_rate(host, ios->clock); 62161bfbdb8SLars-Peter Clausen 62261bfbdb8SLars-Peter Clausen switch (ios->power_mode) { 62361bfbdb8SLars-Peter Clausen case MMC_POWER_UP: 62461bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 62561bfbdb8SLars-Peter Clausen if (gpio_is_valid(host->pdata->gpio_power)) 62661bfbdb8SLars-Peter Clausen gpio_set_value(host->pdata->gpio_power, 62761bfbdb8SLars-Peter Clausen !host->pdata->power_active_low); 62861bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_INIT; 629fca9661cSLars-Peter Clausen clk_prepare_enable(host->clk); 63061bfbdb8SLars-Peter Clausen break; 63161bfbdb8SLars-Peter Clausen case MMC_POWER_ON: 63261bfbdb8SLars-Peter Clausen break; 63361bfbdb8SLars-Peter Clausen default: 63461bfbdb8SLars-Peter Clausen if (gpio_is_valid(host->pdata->gpio_power)) 63561bfbdb8SLars-Peter Clausen gpio_set_value(host->pdata->gpio_power, 63661bfbdb8SLars-Peter Clausen host->pdata->power_active_low); 637fca9661cSLars-Peter Clausen clk_disable_unprepare(host->clk); 63861bfbdb8SLars-Peter Clausen break; 63961bfbdb8SLars-Peter Clausen } 64061bfbdb8SLars-Peter Clausen 64161bfbdb8SLars-Peter Clausen switch (ios->bus_width) { 64261bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_1: 64361bfbdb8SLars-Peter Clausen host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 64461bfbdb8SLars-Peter Clausen break; 64561bfbdb8SLars-Peter Clausen case MMC_BUS_WIDTH_4: 64661bfbdb8SLars-Peter Clausen host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; 64761bfbdb8SLars-Peter Clausen break; 64861bfbdb8SLars-Peter Clausen default: 64961bfbdb8SLars-Peter Clausen break; 65061bfbdb8SLars-Peter Clausen } 65161bfbdb8SLars-Peter Clausen } 65261bfbdb8SLars-Peter Clausen 65361bfbdb8SLars-Peter Clausen static int jz4740_mmc_get_ro(struct mmc_host *mmc) 65461bfbdb8SLars-Peter Clausen { 65561bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 65661bfbdb8SLars-Peter Clausen if (!gpio_is_valid(host->pdata->gpio_read_only)) 65761bfbdb8SLars-Peter Clausen return -ENOSYS; 65861bfbdb8SLars-Peter Clausen 65961bfbdb8SLars-Peter Clausen return gpio_get_value(host->pdata->gpio_read_only) ^ 66061bfbdb8SLars-Peter Clausen host->pdata->read_only_active_low; 66161bfbdb8SLars-Peter Clausen } 66261bfbdb8SLars-Peter Clausen 66361bfbdb8SLars-Peter Clausen static int jz4740_mmc_get_cd(struct mmc_host *mmc) 66461bfbdb8SLars-Peter Clausen { 66561bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 66661bfbdb8SLars-Peter Clausen if (!gpio_is_valid(host->pdata->gpio_card_detect)) 66761bfbdb8SLars-Peter Clausen return -ENOSYS; 66861bfbdb8SLars-Peter Clausen 66961bfbdb8SLars-Peter Clausen return gpio_get_value(host->pdata->gpio_card_detect) ^ 67061bfbdb8SLars-Peter Clausen host->pdata->card_detect_active_low; 67161bfbdb8SLars-Peter Clausen } 67261bfbdb8SLars-Peter Clausen 67361bfbdb8SLars-Peter Clausen static irqreturn_t jz4740_mmc_card_detect_irq(int irq, void *devid) 67461bfbdb8SLars-Peter Clausen { 67561bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = devid; 67661bfbdb8SLars-Peter Clausen 67761bfbdb8SLars-Peter Clausen mmc_detect_change(host->mmc, HZ / 2); 67861bfbdb8SLars-Peter Clausen 67961bfbdb8SLars-Peter Clausen return IRQ_HANDLED; 68061bfbdb8SLars-Peter Clausen } 68161bfbdb8SLars-Peter Clausen 68261bfbdb8SLars-Peter Clausen static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) 68361bfbdb8SLars-Peter Clausen { 68461bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = mmc_priv(mmc); 68561bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable); 68661bfbdb8SLars-Peter Clausen } 68761bfbdb8SLars-Peter Clausen 68861bfbdb8SLars-Peter Clausen static const struct mmc_host_ops jz4740_mmc_ops = { 68961bfbdb8SLars-Peter Clausen .request = jz4740_mmc_request, 69061bfbdb8SLars-Peter Clausen .set_ios = jz4740_mmc_set_ios, 69161bfbdb8SLars-Peter Clausen .get_ro = jz4740_mmc_get_ro, 69261bfbdb8SLars-Peter Clausen .get_cd = jz4740_mmc_get_cd, 69361bfbdb8SLars-Peter Clausen .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, 69461bfbdb8SLars-Peter Clausen }; 69561bfbdb8SLars-Peter Clausen 69661bfbdb8SLars-Peter Clausen static const struct jz_gpio_bulk_request jz4740_mmc_pins[] = { 69761bfbdb8SLars-Peter Clausen JZ_GPIO_BULK_PIN(MSC_CMD), 69861bfbdb8SLars-Peter Clausen JZ_GPIO_BULK_PIN(MSC_CLK), 69961bfbdb8SLars-Peter Clausen JZ_GPIO_BULK_PIN(MSC_DATA0), 70061bfbdb8SLars-Peter Clausen JZ_GPIO_BULK_PIN(MSC_DATA1), 70161bfbdb8SLars-Peter Clausen JZ_GPIO_BULK_PIN(MSC_DATA2), 70261bfbdb8SLars-Peter Clausen JZ_GPIO_BULK_PIN(MSC_DATA3), 70361bfbdb8SLars-Peter Clausen }; 70461bfbdb8SLars-Peter Clausen 705c3be1efdSBill Pemberton static int jz4740_mmc_request_gpio(struct device *dev, int gpio, 70661bfbdb8SLars-Peter Clausen const char *name, bool output, int value) 70761bfbdb8SLars-Peter Clausen { 70861bfbdb8SLars-Peter Clausen int ret; 70961bfbdb8SLars-Peter Clausen 71061bfbdb8SLars-Peter Clausen if (!gpio_is_valid(gpio)) 71161bfbdb8SLars-Peter Clausen return 0; 71261bfbdb8SLars-Peter Clausen 71361bfbdb8SLars-Peter Clausen ret = gpio_request(gpio, name); 71461bfbdb8SLars-Peter Clausen if (ret) { 71561bfbdb8SLars-Peter Clausen dev_err(dev, "Failed to request %s gpio: %d\n", name, ret); 71661bfbdb8SLars-Peter Clausen return ret; 71761bfbdb8SLars-Peter Clausen } 71861bfbdb8SLars-Peter Clausen 71961bfbdb8SLars-Peter Clausen if (output) 72061bfbdb8SLars-Peter Clausen gpio_direction_output(gpio, value); 72161bfbdb8SLars-Peter Clausen else 72261bfbdb8SLars-Peter Clausen gpio_direction_input(gpio); 72361bfbdb8SLars-Peter Clausen 72461bfbdb8SLars-Peter Clausen return 0; 72561bfbdb8SLars-Peter Clausen } 72661bfbdb8SLars-Peter Clausen 727c3be1efdSBill Pemberton static int jz4740_mmc_request_gpios(struct platform_device *pdev) 72861bfbdb8SLars-Peter Clausen { 72961bfbdb8SLars-Peter Clausen int ret; 73061bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; 73161bfbdb8SLars-Peter Clausen 73261bfbdb8SLars-Peter Clausen if (!pdata) 73361bfbdb8SLars-Peter Clausen return 0; 73461bfbdb8SLars-Peter Clausen 73561bfbdb8SLars-Peter Clausen ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_card_detect, 73661bfbdb8SLars-Peter Clausen "MMC detect change", false, 0); 73761bfbdb8SLars-Peter Clausen if (ret) 73861bfbdb8SLars-Peter Clausen goto err; 73961bfbdb8SLars-Peter Clausen 74061bfbdb8SLars-Peter Clausen ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_read_only, 74161bfbdb8SLars-Peter Clausen "MMC read only", false, 0); 74261bfbdb8SLars-Peter Clausen if (ret) 74361bfbdb8SLars-Peter Clausen goto err_free_gpio_card_detect; 74461bfbdb8SLars-Peter Clausen 74561bfbdb8SLars-Peter Clausen ret = jz4740_mmc_request_gpio(&pdev->dev, pdata->gpio_power, 74661bfbdb8SLars-Peter Clausen "MMC read only", true, pdata->power_active_low); 74761bfbdb8SLars-Peter Clausen if (ret) 74861bfbdb8SLars-Peter Clausen goto err_free_gpio_read_only; 74961bfbdb8SLars-Peter Clausen 75061bfbdb8SLars-Peter Clausen return 0; 75161bfbdb8SLars-Peter Clausen 75261bfbdb8SLars-Peter Clausen err_free_gpio_read_only: 75361bfbdb8SLars-Peter Clausen if (gpio_is_valid(pdata->gpio_read_only)) 75461bfbdb8SLars-Peter Clausen gpio_free(pdata->gpio_read_only); 75561bfbdb8SLars-Peter Clausen err_free_gpio_card_detect: 75661bfbdb8SLars-Peter Clausen if (gpio_is_valid(pdata->gpio_card_detect)) 75761bfbdb8SLars-Peter Clausen gpio_free(pdata->gpio_card_detect); 75861bfbdb8SLars-Peter Clausen err: 75961bfbdb8SLars-Peter Clausen return ret; 76061bfbdb8SLars-Peter Clausen } 76161bfbdb8SLars-Peter Clausen 762c3be1efdSBill Pemberton static int jz4740_mmc_request_cd_irq(struct platform_device *pdev, 76361bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host) 76461bfbdb8SLars-Peter Clausen { 76561bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; 76661bfbdb8SLars-Peter Clausen 76761bfbdb8SLars-Peter Clausen if (!gpio_is_valid(pdata->gpio_card_detect)) 76861bfbdb8SLars-Peter Clausen return 0; 76961bfbdb8SLars-Peter Clausen 77061bfbdb8SLars-Peter Clausen host->card_detect_irq = gpio_to_irq(pdata->gpio_card_detect); 77161bfbdb8SLars-Peter Clausen if (host->card_detect_irq < 0) { 77261bfbdb8SLars-Peter Clausen dev_warn(&pdev->dev, "Failed to get card detect irq\n"); 77361bfbdb8SLars-Peter Clausen return 0; 77461bfbdb8SLars-Peter Clausen } 77561bfbdb8SLars-Peter Clausen 77661bfbdb8SLars-Peter Clausen return request_irq(host->card_detect_irq, jz4740_mmc_card_detect_irq, 77761bfbdb8SLars-Peter Clausen IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, 77861bfbdb8SLars-Peter Clausen "MMC card detect", host); 77961bfbdb8SLars-Peter Clausen } 78061bfbdb8SLars-Peter Clausen 78161bfbdb8SLars-Peter Clausen static void jz4740_mmc_free_gpios(struct platform_device *pdev) 78261bfbdb8SLars-Peter Clausen { 78361bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; 78461bfbdb8SLars-Peter Clausen 78561bfbdb8SLars-Peter Clausen if (!pdata) 78661bfbdb8SLars-Peter Clausen return; 78761bfbdb8SLars-Peter Clausen 78861bfbdb8SLars-Peter Clausen if (gpio_is_valid(pdata->gpio_power)) 78961bfbdb8SLars-Peter Clausen gpio_free(pdata->gpio_power); 79061bfbdb8SLars-Peter Clausen if (gpio_is_valid(pdata->gpio_read_only)) 79161bfbdb8SLars-Peter Clausen gpio_free(pdata->gpio_read_only); 79261bfbdb8SLars-Peter Clausen if (gpio_is_valid(pdata->gpio_card_detect)) 79361bfbdb8SLars-Peter Clausen gpio_free(pdata->gpio_card_detect); 79461bfbdb8SLars-Peter Clausen } 79561bfbdb8SLars-Peter Clausen 79661bfbdb8SLars-Peter Clausen static inline size_t jz4740_mmc_num_pins(struct jz4740_mmc_host *host) 79761bfbdb8SLars-Peter Clausen { 79861bfbdb8SLars-Peter Clausen size_t num_pins = ARRAY_SIZE(jz4740_mmc_pins); 79961bfbdb8SLars-Peter Clausen if (host->pdata && host->pdata->data_1bit) 80061bfbdb8SLars-Peter Clausen num_pins -= 3; 80161bfbdb8SLars-Peter Clausen 80261bfbdb8SLars-Peter Clausen return num_pins; 80361bfbdb8SLars-Peter Clausen } 80461bfbdb8SLars-Peter Clausen 805c3be1efdSBill Pemberton static int jz4740_mmc_probe(struct platform_device* pdev) 80661bfbdb8SLars-Peter Clausen { 80761bfbdb8SLars-Peter Clausen int ret; 80861bfbdb8SLars-Peter Clausen struct mmc_host *mmc; 80961bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host; 81061bfbdb8SLars-Peter Clausen struct jz4740_mmc_platform_data *pdata; 81161bfbdb8SLars-Peter Clausen 81261bfbdb8SLars-Peter Clausen pdata = pdev->dev.platform_data; 81361bfbdb8SLars-Peter Clausen 81461bfbdb8SLars-Peter Clausen mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); 81561bfbdb8SLars-Peter Clausen if (!mmc) { 81661bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); 81761bfbdb8SLars-Peter Clausen return -ENOMEM; 81861bfbdb8SLars-Peter Clausen } 81961bfbdb8SLars-Peter Clausen 82061bfbdb8SLars-Peter Clausen host = mmc_priv(mmc); 82161bfbdb8SLars-Peter Clausen host->pdata = pdata; 82261bfbdb8SLars-Peter Clausen 82361bfbdb8SLars-Peter Clausen host->irq = platform_get_irq(pdev, 0); 82461bfbdb8SLars-Peter Clausen if (host->irq < 0) { 82561bfbdb8SLars-Peter Clausen ret = host->irq; 82661bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); 82761bfbdb8SLars-Peter Clausen goto err_free_host; 82861bfbdb8SLars-Peter Clausen } 82961bfbdb8SLars-Peter Clausen 83061bfbdb8SLars-Peter Clausen host->clk = clk_get(&pdev->dev, "mmc"); 8313119cbdaSJamie Iles if (IS_ERR(host->clk)) { 8323119cbdaSJamie Iles ret = PTR_ERR(host->clk); 83361bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get mmc clock\n"); 83461bfbdb8SLars-Peter Clausen goto err_free_host; 83561bfbdb8SLars-Peter Clausen } 83661bfbdb8SLars-Peter Clausen 83761bfbdb8SLars-Peter Clausen host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 83861bfbdb8SLars-Peter Clausen if (!host->mem) { 83961bfbdb8SLars-Peter Clausen ret = -ENOENT; 84061bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to get base platform memory\n"); 84161bfbdb8SLars-Peter Clausen goto err_clk_put; 84261bfbdb8SLars-Peter Clausen } 84361bfbdb8SLars-Peter Clausen 84461bfbdb8SLars-Peter Clausen host->mem = request_mem_region(host->mem->start, 84561bfbdb8SLars-Peter Clausen resource_size(host->mem), pdev->name); 84661bfbdb8SLars-Peter Clausen if (!host->mem) { 84761bfbdb8SLars-Peter Clausen ret = -EBUSY; 84861bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to request base memory region\n"); 84961bfbdb8SLars-Peter Clausen goto err_clk_put; 85061bfbdb8SLars-Peter Clausen } 85161bfbdb8SLars-Peter Clausen 85261bfbdb8SLars-Peter Clausen host->base = ioremap_nocache(host->mem->start, resource_size(host->mem)); 85361bfbdb8SLars-Peter Clausen if (!host->base) { 85461bfbdb8SLars-Peter Clausen ret = -EBUSY; 85561bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to ioremap base memory\n"); 85661bfbdb8SLars-Peter Clausen goto err_release_mem_region; 85761bfbdb8SLars-Peter Clausen } 85861bfbdb8SLars-Peter Clausen 85961bfbdb8SLars-Peter Clausen ret = jz_gpio_bulk_request(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); 86061bfbdb8SLars-Peter Clausen if (ret) { 86161bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to request mmc pins: %d\n", ret); 86261bfbdb8SLars-Peter Clausen goto err_iounmap; 86361bfbdb8SLars-Peter Clausen } 86461bfbdb8SLars-Peter Clausen 86561bfbdb8SLars-Peter Clausen ret = jz4740_mmc_request_gpios(pdev); 86661bfbdb8SLars-Peter Clausen if (ret) 86761bfbdb8SLars-Peter Clausen goto err_gpio_bulk_free; 86861bfbdb8SLars-Peter Clausen 86961bfbdb8SLars-Peter Clausen mmc->ops = &jz4740_mmc_ops; 87061bfbdb8SLars-Peter Clausen mmc->f_min = JZ_MMC_CLK_RATE / 128; 87161bfbdb8SLars-Peter Clausen mmc->f_max = JZ_MMC_CLK_RATE; 87261bfbdb8SLars-Peter Clausen mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; 87361bfbdb8SLars-Peter Clausen mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA; 87461bfbdb8SLars-Peter Clausen mmc->caps |= MMC_CAP_SDIO_IRQ; 87561bfbdb8SLars-Peter Clausen 87661bfbdb8SLars-Peter Clausen mmc->max_blk_size = (1 << 10) - 1; 87761bfbdb8SLars-Peter Clausen mmc->max_blk_count = (1 << 15) - 1; 87861bfbdb8SLars-Peter Clausen mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; 87961bfbdb8SLars-Peter Clausen 880a36274e0SMartin K. Petersen mmc->max_segs = 128; 88161bfbdb8SLars-Peter Clausen mmc->max_seg_size = mmc->max_req_size; 88261bfbdb8SLars-Peter Clausen 88361bfbdb8SLars-Peter Clausen host->mmc = mmc; 88461bfbdb8SLars-Peter Clausen host->pdev = pdev; 88561bfbdb8SLars-Peter Clausen spin_lock_init(&host->lock); 88661bfbdb8SLars-Peter Clausen host->irq_mask = 0xffff; 88761bfbdb8SLars-Peter Clausen 88861bfbdb8SLars-Peter Clausen ret = jz4740_mmc_request_cd_irq(pdev, host); 88961bfbdb8SLars-Peter Clausen if (ret) { 89061bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to request card detect irq\n"); 89161bfbdb8SLars-Peter Clausen goto err_free_gpios; 89261bfbdb8SLars-Peter Clausen } 89361bfbdb8SLars-Peter Clausen 89461bfbdb8SLars-Peter Clausen ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, 0, 89561bfbdb8SLars-Peter Clausen dev_name(&pdev->dev), host); 89661bfbdb8SLars-Peter Clausen if (ret) { 89761bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); 89861bfbdb8SLars-Peter Clausen goto err_free_card_detect_irq; 89961bfbdb8SLars-Peter Clausen } 90061bfbdb8SLars-Peter Clausen 90161bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 90261bfbdb8SLars-Peter Clausen jz4740_mmc_clock_disable(host); 90361bfbdb8SLars-Peter Clausen setup_timer(&host->timeout_timer, jz4740_mmc_timeout, 90461bfbdb8SLars-Peter Clausen (unsigned long)host); 90561bfbdb8SLars-Peter Clausen /* It is not important when it times out, it just needs to timeout. */ 90661bfbdb8SLars-Peter Clausen set_timer_slack(&host->timeout_timer, HZ); 90761bfbdb8SLars-Peter Clausen 90861bfbdb8SLars-Peter Clausen platform_set_drvdata(pdev, host); 90961bfbdb8SLars-Peter Clausen ret = mmc_add_host(mmc); 91061bfbdb8SLars-Peter Clausen 91161bfbdb8SLars-Peter Clausen if (ret) { 91261bfbdb8SLars-Peter Clausen dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); 91361bfbdb8SLars-Peter Clausen goto err_free_irq; 91461bfbdb8SLars-Peter Clausen } 91561bfbdb8SLars-Peter Clausen dev_info(&pdev->dev, "JZ SD/MMC card driver registered\n"); 91661bfbdb8SLars-Peter Clausen 91761bfbdb8SLars-Peter Clausen return 0; 91861bfbdb8SLars-Peter Clausen 91961bfbdb8SLars-Peter Clausen err_free_irq: 92061bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 92161bfbdb8SLars-Peter Clausen err_free_card_detect_irq: 92261bfbdb8SLars-Peter Clausen if (host->card_detect_irq >= 0) 92361bfbdb8SLars-Peter Clausen free_irq(host->card_detect_irq, host); 92461bfbdb8SLars-Peter Clausen err_free_gpios: 92561bfbdb8SLars-Peter Clausen jz4740_mmc_free_gpios(pdev); 92661bfbdb8SLars-Peter Clausen err_gpio_bulk_free: 92761bfbdb8SLars-Peter Clausen jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); 92861bfbdb8SLars-Peter Clausen err_iounmap: 92961bfbdb8SLars-Peter Clausen iounmap(host->base); 93061bfbdb8SLars-Peter Clausen err_release_mem_region: 93161bfbdb8SLars-Peter Clausen release_mem_region(host->mem->start, resource_size(host->mem)); 93261bfbdb8SLars-Peter Clausen err_clk_put: 93361bfbdb8SLars-Peter Clausen clk_put(host->clk); 93461bfbdb8SLars-Peter Clausen err_free_host: 93561bfbdb8SLars-Peter Clausen mmc_free_host(mmc); 93661bfbdb8SLars-Peter Clausen 93761bfbdb8SLars-Peter Clausen return ret; 93861bfbdb8SLars-Peter Clausen } 93961bfbdb8SLars-Peter Clausen 9406e0ee714SBill Pemberton static int jz4740_mmc_remove(struct platform_device *pdev) 94161bfbdb8SLars-Peter Clausen { 94261bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = platform_get_drvdata(pdev); 94361bfbdb8SLars-Peter Clausen 94461bfbdb8SLars-Peter Clausen del_timer_sync(&host->timeout_timer); 94561bfbdb8SLars-Peter Clausen jz4740_mmc_set_irq_enabled(host, 0xff, false); 94661bfbdb8SLars-Peter Clausen jz4740_mmc_reset(host); 94761bfbdb8SLars-Peter Clausen 94861bfbdb8SLars-Peter Clausen mmc_remove_host(host->mmc); 94961bfbdb8SLars-Peter Clausen 95061bfbdb8SLars-Peter Clausen free_irq(host->irq, host); 95161bfbdb8SLars-Peter Clausen if (host->card_detect_irq >= 0) 95261bfbdb8SLars-Peter Clausen free_irq(host->card_detect_irq, host); 95361bfbdb8SLars-Peter Clausen 95461bfbdb8SLars-Peter Clausen jz4740_mmc_free_gpios(pdev); 95561bfbdb8SLars-Peter Clausen jz_gpio_bulk_free(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); 95661bfbdb8SLars-Peter Clausen 95761bfbdb8SLars-Peter Clausen iounmap(host->base); 95861bfbdb8SLars-Peter Clausen release_mem_region(host->mem->start, resource_size(host->mem)); 95961bfbdb8SLars-Peter Clausen 96061bfbdb8SLars-Peter Clausen clk_put(host->clk); 96161bfbdb8SLars-Peter Clausen 96261bfbdb8SLars-Peter Clausen mmc_free_host(host->mmc); 96361bfbdb8SLars-Peter Clausen 96461bfbdb8SLars-Peter Clausen return 0; 96561bfbdb8SLars-Peter Clausen } 96661bfbdb8SLars-Peter Clausen 96761bfbdb8SLars-Peter Clausen #ifdef CONFIG_PM 96861bfbdb8SLars-Peter Clausen 96961bfbdb8SLars-Peter Clausen static int jz4740_mmc_suspend(struct device *dev) 97061bfbdb8SLars-Peter Clausen { 97161bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = dev_get_drvdata(dev); 97261bfbdb8SLars-Peter Clausen 97361bfbdb8SLars-Peter Clausen mmc_suspend_host(host->mmc); 97461bfbdb8SLars-Peter Clausen 97561bfbdb8SLars-Peter Clausen jz_gpio_bulk_suspend(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); 97661bfbdb8SLars-Peter Clausen 97761bfbdb8SLars-Peter Clausen return 0; 97861bfbdb8SLars-Peter Clausen } 97961bfbdb8SLars-Peter Clausen 98061bfbdb8SLars-Peter Clausen static int jz4740_mmc_resume(struct device *dev) 98161bfbdb8SLars-Peter Clausen { 98261bfbdb8SLars-Peter Clausen struct jz4740_mmc_host *host = dev_get_drvdata(dev); 98361bfbdb8SLars-Peter Clausen 98461bfbdb8SLars-Peter Clausen jz_gpio_bulk_resume(jz4740_mmc_pins, jz4740_mmc_num_pins(host)); 98561bfbdb8SLars-Peter Clausen 98661bfbdb8SLars-Peter Clausen mmc_resume_host(host->mmc); 98761bfbdb8SLars-Peter Clausen 98861bfbdb8SLars-Peter Clausen return 0; 98961bfbdb8SLars-Peter Clausen } 99061bfbdb8SLars-Peter Clausen 99161bfbdb8SLars-Peter Clausen const struct dev_pm_ops jz4740_mmc_pm_ops = { 99261bfbdb8SLars-Peter Clausen .suspend = jz4740_mmc_suspend, 99361bfbdb8SLars-Peter Clausen .resume = jz4740_mmc_resume, 99461bfbdb8SLars-Peter Clausen .poweroff = jz4740_mmc_suspend, 99561bfbdb8SLars-Peter Clausen .restore = jz4740_mmc_resume, 99661bfbdb8SLars-Peter Clausen }; 99761bfbdb8SLars-Peter Clausen 99861bfbdb8SLars-Peter Clausen #define JZ4740_MMC_PM_OPS (&jz4740_mmc_pm_ops) 99961bfbdb8SLars-Peter Clausen #else 100061bfbdb8SLars-Peter Clausen #define JZ4740_MMC_PM_OPS NULL 100161bfbdb8SLars-Peter Clausen #endif 100261bfbdb8SLars-Peter Clausen 100361bfbdb8SLars-Peter Clausen static struct platform_driver jz4740_mmc_driver = { 100461bfbdb8SLars-Peter Clausen .probe = jz4740_mmc_probe, 10050433c143SBill Pemberton .remove = jz4740_mmc_remove, 100661bfbdb8SLars-Peter Clausen .driver = { 100761bfbdb8SLars-Peter Clausen .name = "jz4740-mmc", 100861bfbdb8SLars-Peter Clausen .owner = THIS_MODULE, 100961bfbdb8SLars-Peter Clausen .pm = JZ4740_MMC_PM_OPS, 101061bfbdb8SLars-Peter Clausen }, 101161bfbdb8SLars-Peter Clausen }; 101261bfbdb8SLars-Peter Clausen 1013d1f81a64SAxel Lin module_platform_driver(jz4740_mmc_driver); 101461bfbdb8SLars-Peter Clausen 101561bfbdb8SLars-Peter Clausen MODULE_DESCRIPTION("JZ4740 SD/MMC controller driver"); 101661bfbdb8SLars-Peter Clausen MODULE_LICENSE("GPL"); 101761bfbdb8SLars-Peter Clausen MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); 1018