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