Path: blob/master/drivers/mmc/host/sdhci-esdhc-imx.c
15112 views
/*1* Freescale eSDHC i.MX controller driver for the platform bus.2*3* derived from the OF-version.4*5* Copyright (c) 2010 Pengutronix e.K.6* Author: Wolfram Sang <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License.11*/1213#include <linux/io.h>14#include <linux/delay.h>15#include <linux/err.h>16#include <linux/clk.h>17#include <linux/gpio.h>18#include <linux/slab.h>19#include <linux/mmc/host.h>20#include <linux/mmc/sdhci-pltfm.h>21#include <linux/mmc/mmc.h>22#include <linux/mmc/sdio.h>23#include <mach/hardware.h>24#include <mach/esdhc.h>25#include "sdhci.h"26#include "sdhci-pltfm.h"27#include "sdhci-esdhc.h"2829/* VENDOR SPEC register */30#define SDHCI_VENDOR_SPEC 0xC031#define SDHCI_VENDOR_SPEC_SDIO_QUIRK 0x000000023233#define ESDHC_FLAG_GPIO_FOR_CD_WP (1 << 0)34/*35* The CMDTYPE of the CMD register (offset 0xE) should be set to36* "11" when the STOP CMD12 is issued on imx53 to abort one37* open ended multi-blk IO. Otherwise the TC INT wouldn't38* be generated.39* In exact block transfer, the controller doesn't complete the40* operations automatically as required at the end of the41* transfer and remains on hold if the abort command is not sent.42* As a result, the TC flag is not asserted and SW received timeout43* exeception. Bit1 of Vendor Spec registor is used to fix it.44*/45#define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1)4647struct pltfm_imx_data {48int flags;49u32 scratchpad;50};5152static inline void esdhc_clrset_le(struct sdhci_host *host, u32 mask, u32 val, int reg)53{54void __iomem *base = host->ioaddr + (reg & ~0x3);55u32 shift = (reg & 0x3) * 8;5657writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);58}5960static u32 esdhc_readl_le(struct sdhci_host *host, int reg)61{62struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);63struct pltfm_imx_data *imx_data = pltfm_host->priv;6465/* fake CARD_PRESENT flag on mx25/35 */66u32 val = readl(host->ioaddr + reg);6768if (unlikely((reg == SDHCI_PRESENT_STATE)69&& (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP))) {70struct esdhc_platform_data *boarddata =71host->mmc->parent->platform_data;7273if (boarddata && gpio_is_valid(boarddata->cd_gpio)74&& gpio_get_value(boarddata->cd_gpio))75/* no card, if a valid gpio says so... */76val &= SDHCI_CARD_PRESENT;77else78/* ... in all other cases assume card is present */79val |= SDHCI_CARD_PRESENT;80}8182return val;83}8485static void esdhc_writel_le(struct sdhci_host *host, u32 val, int reg)86{87struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);88struct pltfm_imx_data *imx_data = pltfm_host->priv;8990if (unlikely((reg == SDHCI_INT_ENABLE || reg == SDHCI_SIGNAL_ENABLE)91&& (imx_data->flags & ESDHC_FLAG_GPIO_FOR_CD_WP)))92/*93* these interrupts won't work with a custom card_detect gpio94* (only applied to mx25/35)95*/96val &= ~(SDHCI_INT_CARD_REMOVE | SDHCI_INT_CARD_INSERT);9798if (unlikely((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)99&& (reg == SDHCI_INT_STATUS)100&& (val & SDHCI_INT_DATA_END))) {101u32 v;102v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);103v &= ~SDHCI_VENDOR_SPEC_SDIO_QUIRK;104writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);105}106107writel(val, host->ioaddr + reg);108}109110static u16 esdhc_readw_le(struct sdhci_host *host, int reg)111{112if (unlikely(reg == SDHCI_HOST_VERSION))113reg ^= 2;114115return readw(host->ioaddr + reg);116}117118static void esdhc_writew_le(struct sdhci_host *host, u16 val, int reg)119{120struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);121struct pltfm_imx_data *imx_data = pltfm_host->priv;122123switch (reg) {124case SDHCI_TRANSFER_MODE:125/*126* Postpone this write, we must do it together with a127* command write that is down below.128*/129if ((imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT)130&& (host->cmd->opcode == SD_IO_RW_EXTENDED)131&& (host->cmd->data->blocks > 1)132&& (host->cmd->data->flags & MMC_DATA_READ)) {133u32 v;134v = readl(host->ioaddr + SDHCI_VENDOR_SPEC);135v |= SDHCI_VENDOR_SPEC_SDIO_QUIRK;136writel(v, host->ioaddr + SDHCI_VENDOR_SPEC);137}138imx_data->scratchpad = val;139return;140case SDHCI_COMMAND:141if ((host->cmd->opcode == MMC_STOP_TRANSMISSION)142&& (imx_data->flags & ESDHC_FLAG_MULTIBLK_NO_INT))143val |= SDHCI_CMD_ABORTCMD;144writel(val << 16 | imx_data->scratchpad,145host->ioaddr + SDHCI_TRANSFER_MODE);146return;147case SDHCI_BLOCK_SIZE:148val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);149break;150}151esdhc_clrset_le(host, 0xffff, val, reg);152}153154static void esdhc_writeb_le(struct sdhci_host *host, u8 val, int reg)155{156u32 new_val;157158switch (reg) {159case SDHCI_POWER_CONTROL:160/*161* FSL put some DMA bits here162* If your board has a regulator, code should be here163*/164return;165case SDHCI_HOST_CONTROL:166/* FSL messed up here, so we can just keep those two */167new_val = val & (SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS);168/* ensure the endianess */169new_val |= ESDHC_HOST_CONTROL_LE;170/* DMA mode bits are shifted */171new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;172173esdhc_clrset_le(host, 0xffff, new_val, reg);174return;175}176esdhc_clrset_le(host, 0xff, val, reg);177}178179static unsigned int esdhc_pltfm_get_max_clock(struct sdhci_host *host)180{181struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);182183return clk_get_rate(pltfm_host->clk);184}185186static unsigned int esdhc_pltfm_get_min_clock(struct sdhci_host *host)187{188struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);189190return clk_get_rate(pltfm_host->clk) / 256 / 16;191}192193static unsigned int esdhc_pltfm_get_ro(struct sdhci_host *host)194{195struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;196197if (boarddata && gpio_is_valid(boarddata->wp_gpio))198return gpio_get_value(boarddata->wp_gpio);199else200return -ENOSYS;201}202203static struct sdhci_ops sdhci_esdhc_ops = {204.read_l = esdhc_readl_le,205.read_w = esdhc_readw_le,206.write_l = esdhc_writel_le,207.write_w = esdhc_writew_le,208.write_b = esdhc_writeb_le,209.set_clock = esdhc_set_clock,210.get_max_clock = esdhc_pltfm_get_max_clock,211.get_min_clock = esdhc_pltfm_get_min_clock,212};213214static irqreturn_t cd_irq(int irq, void *data)215{216struct sdhci_host *sdhost = (struct sdhci_host *)data;217218tasklet_schedule(&sdhost->card_tasklet);219return IRQ_HANDLED;220};221222static int esdhc_pltfm_init(struct sdhci_host *host, struct sdhci_pltfm_data *pdata)223{224struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);225struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;226struct clk *clk;227int err;228struct pltfm_imx_data *imx_data;229230clk = clk_get(mmc_dev(host->mmc), NULL);231if (IS_ERR(clk)) {232dev_err(mmc_dev(host->mmc), "clk err\n");233return PTR_ERR(clk);234}235clk_enable(clk);236pltfm_host->clk = clk;237238imx_data = kzalloc(sizeof(struct pltfm_imx_data), GFP_KERNEL);239if (!imx_data) {240clk_disable(pltfm_host->clk);241clk_put(pltfm_host->clk);242return -ENOMEM;243}244pltfm_host->priv = imx_data;245246if (!cpu_is_mx25())247host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;248249if (cpu_is_mx25() || cpu_is_mx35()) {250/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */251host->quirks |= SDHCI_QUIRK_NO_MULTIBLOCK;252/* write_protect can't be routed to controller, use gpio */253sdhci_esdhc_ops.get_ro = esdhc_pltfm_get_ro;254}255256if (!(cpu_is_mx25() || cpu_is_mx35() || cpu_is_mx51()))257imx_data->flags |= ESDHC_FLAG_MULTIBLK_NO_INT;258259if (boarddata) {260err = gpio_request_one(boarddata->wp_gpio, GPIOF_IN, "ESDHC_WP");261if (err) {262dev_warn(mmc_dev(host->mmc),263"no write-protect pin available!\n");264boarddata->wp_gpio = err;265}266267err = gpio_request_one(boarddata->cd_gpio, GPIOF_IN, "ESDHC_CD");268if (err) {269dev_warn(mmc_dev(host->mmc),270"no card-detect pin available!\n");271goto no_card_detect_pin;272}273274/* i.MX5x has issues to be researched */275if (!cpu_is_mx25() && !cpu_is_mx35())276goto not_supported;277278err = request_irq(gpio_to_irq(boarddata->cd_gpio), cd_irq,279IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,280mmc_hostname(host->mmc), host);281if (err) {282dev_warn(mmc_dev(host->mmc), "request irq error\n");283goto no_card_detect_irq;284}285286imx_data->flags |= ESDHC_FLAG_GPIO_FOR_CD_WP;287/* Now we have a working card_detect again */288host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;289}290291return 0;292293no_card_detect_irq:294gpio_free(boarddata->cd_gpio);295no_card_detect_pin:296boarddata->cd_gpio = err;297not_supported:298kfree(imx_data);299return 0;300}301302static void esdhc_pltfm_exit(struct sdhci_host *host)303{304struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);305struct esdhc_platform_data *boarddata = host->mmc->parent->platform_data;306struct pltfm_imx_data *imx_data = pltfm_host->priv;307308if (boarddata && gpio_is_valid(boarddata->wp_gpio))309gpio_free(boarddata->wp_gpio);310311if (boarddata && gpio_is_valid(boarddata->cd_gpio)) {312gpio_free(boarddata->cd_gpio);313314if (!(host->quirks & SDHCI_QUIRK_BROKEN_CARD_DETECTION))315free_irq(gpio_to_irq(boarddata->cd_gpio), host);316}317318clk_disable(pltfm_host->clk);319clk_put(pltfm_host->clk);320kfree(imx_data);321}322323struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {324.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_ADMA325| SDHCI_QUIRK_BROKEN_CARD_DETECTION,326/* ADMA has issues. Might be fixable */327.ops = &sdhci_esdhc_ops,328.init = esdhc_pltfm_init,329.exit = esdhc_pltfm_exit,330};331332333