1 /* 2 * Copyright (C) 2010 Google, Inc. 3 * 4 * This software is licensed under the terms of the GNU General Public 5 * License version 2, as published by the Free Software Foundation, and 6 * may be copied, distributed, and modified under those terms. 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 * 13 */ 14 15 #include <linux/err.h> 16 #include <linux/init.h> 17 #include <linux/platform_device.h> 18 #include <linux/clk.h> 19 #include <linux/io.h> 20 #include <linux/gpio.h> 21 #include <linux/mmc/card.h> 22 #include <linux/mmc/host.h> 23 24 #include <mach/gpio.h> 25 #include <mach/sdhci.h> 26 27 #include "sdhci.h" 28 #include "sdhci-pltfm.h" 29 30 static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg) 31 { 32 u32 val; 33 34 if (unlikely(reg == SDHCI_PRESENT_STATE)) { 35 /* Use wp_gpio here instead? */ 36 val = readl(host->ioaddr + reg); 37 return val | SDHCI_WRITE_PROTECT; 38 } 39 40 return readl(host->ioaddr + reg); 41 } 42 43 static u16 tegra_sdhci_readw(struct sdhci_host *host, int reg) 44 { 45 if (unlikely(reg == SDHCI_HOST_VERSION)) { 46 /* Erratum: Version register is invalid in HW. */ 47 return SDHCI_SPEC_200; 48 } 49 50 return readw(host->ioaddr + reg); 51 } 52 53 static void tegra_sdhci_writel(struct sdhci_host *host, u32 val, int reg) 54 { 55 /* Seems like we're getting spurious timeout and crc errors, so 56 * disable signalling of them. In case of real errors software 57 * timers should take care of eventually detecting them. 58 */ 59 if (unlikely(reg == SDHCI_SIGNAL_ENABLE)) 60 val &= ~(SDHCI_INT_TIMEOUT|SDHCI_INT_CRC); 61 62 writel(val, host->ioaddr + reg); 63 64 if (unlikely(reg == SDHCI_INT_ENABLE)) { 65 /* Erratum: Must enable block gap interrupt detection */ 66 u8 gap_ctrl = readb(host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 67 if (val & SDHCI_INT_CARD_INT) 68 gap_ctrl |= 0x8; 69 else 70 gap_ctrl &= ~0x8; 71 writeb(gap_ctrl, host->ioaddr + SDHCI_BLOCK_GAP_CONTROL); 72 } 73 } 74 75 static unsigned int tegra_sdhci_get_ro(struct sdhci_host *sdhci) 76 { 77 struct platform_device *pdev = to_platform_device(mmc_dev(sdhci->mmc)); 78 struct tegra_sdhci_platform_data *plat; 79 80 plat = pdev->dev.platform_data; 81 82 if (!gpio_is_valid(plat->wp_gpio)) 83 return -1; 84 85 return gpio_get_value(plat->wp_gpio); 86 } 87 88 static irqreturn_t carddetect_irq(int irq, void *data) 89 { 90 struct sdhci_host *sdhost = (struct sdhci_host *)data; 91 92 tasklet_schedule(&sdhost->card_tasklet); 93 return IRQ_HANDLED; 94 }; 95 96 static int tegra_sdhci_8bit(struct sdhci_host *host, int bus_width) 97 { 98 struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); 99 struct tegra_sdhci_platform_data *plat; 100 u32 ctrl; 101 102 plat = pdev->dev.platform_data; 103 104 ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); 105 if (plat->is_8bit && bus_width == MMC_BUS_WIDTH_8) { 106 ctrl &= ~SDHCI_CTRL_4BITBUS; 107 ctrl |= SDHCI_CTRL_8BITBUS; 108 } else { 109 ctrl &= ~SDHCI_CTRL_8BITBUS; 110 if (bus_width == MMC_BUS_WIDTH_4) 111 ctrl |= SDHCI_CTRL_4BITBUS; 112 else 113 ctrl &= ~SDHCI_CTRL_4BITBUS; 114 } 115 sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); 116 return 0; 117 } 118 119 120 static int tegra_sdhci_pltfm_init(struct sdhci_host *host, 121 struct sdhci_pltfm_data *pdata) 122 { 123 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 124 struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); 125 struct tegra_sdhci_platform_data *plat; 126 struct clk *clk; 127 int rc; 128 129 plat = pdev->dev.platform_data; 130 if (plat == NULL) { 131 dev_err(mmc_dev(host->mmc), "missing platform data\n"); 132 return -ENXIO; 133 } 134 135 if (gpio_is_valid(plat->power_gpio)) { 136 rc = gpio_request(plat->power_gpio, "sdhci_power"); 137 if (rc) { 138 dev_err(mmc_dev(host->mmc), 139 "failed to allocate power gpio\n"); 140 goto out; 141 } 142 tegra_gpio_enable(plat->power_gpio); 143 gpio_direction_output(plat->power_gpio, 1); 144 } 145 146 if (gpio_is_valid(plat->cd_gpio)) { 147 rc = gpio_request(plat->cd_gpio, "sdhci_cd"); 148 if (rc) { 149 dev_err(mmc_dev(host->mmc), 150 "failed to allocate cd gpio\n"); 151 goto out_power; 152 } 153 tegra_gpio_enable(plat->cd_gpio); 154 gpio_direction_input(plat->cd_gpio); 155 156 rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq, 157 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 158 mmc_hostname(host->mmc), host); 159 160 if (rc) { 161 dev_err(mmc_dev(host->mmc), "request irq error\n"); 162 goto out_cd; 163 } 164 165 } 166 167 if (gpio_is_valid(plat->wp_gpio)) { 168 rc = gpio_request(plat->wp_gpio, "sdhci_wp"); 169 if (rc) { 170 dev_err(mmc_dev(host->mmc), 171 "failed to allocate wp gpio\n"); 172 goto out_irq; 173 } 174 tegra_gpio_enable(plat->wp_gpio); 175 gpio_direction_input(plat->wp_gpio); 176 } 177 178 clk = clk_get(mmc_dev(host->mmc), NULL); 179 if (IS_ERR(clk)) { 180 dev_err(mmc_dev(host->mmc), "clk err\n"); 181 rc = PTR_ERR(clk); 182 goto out_wp; 183 } 184 clk_enable(clk); 185 pltfm_host->clk = clk; 186 187 if (plat->is_8bit) 188 host->mmc->caps |= MMC_CAP_8_BIT_DATA; 189 190 return 0; 191 192 out_wp: 193 if (gpio_is_valid(plat->wp_gpio)) { 194 tegra_gpio_disable(plat->wp_gpio); 195 gpio_free(plat->wp_gpio); 196 } 197 198 out_irq: 199 if (gpio_is_valid(plat->cd_gpio)) 200 free_irq(gpio_to_irq(plat->cd_gpio), host); 201 out_cd: 202 if (gpio_is_valid(plat->cd_gpio)) { 203 tegra_gpio_disable(plat->cd_gpio); 204 gpio_free(plat->cd_gpio); 205 } 206 207 out_power: 208 if (gpio_is_valid(plat->power_gpio)) { 209 tegra_gpio_disable(plat->power_gpio); 210 gpio_free(plat->power_gpio); 211 } 212 213 out: 214 return rc; 215 } 216 217 static void tegra_sdhci_pltfm_exit(struct sdhci_host *host) 218 { 219 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 220 struct platform_device *pdev = to_platform_device(mmc_dev(host->mmc)); 221 struct tegra_sdhci_platform_data *plat; 222 223 plat = pdev->dev.platform_data; 224 225 if (gpio_is_valid(plat->wp_gpio)) { 226 tegra_gpio_disable(plat->wp_gpio); 227 gpio_free(plat->wp_gpio); 228 } 229 230 if (gpio_is_valid(plat->cd_gpio)) { 231 free_irq(gpio_to_irq(plat->cd_gpio), host); 232 tegra_gpio_disable(plat->cd_gpio); 233 gpio_free(plat->cd_gpio); 234 } 235 236 if (gpio_is_valid(plat->power_gpio)) { 237 tegra_gpio_disable(plat->power_gpio); 238 gpio_free(plat->power_gpio); 239 } 240 241 clk_disable(pltfm_host->clk); 242 clk_put(pltfm_host->clk); 243 } 244 245 static struct sdhci_ops tegra_sdhci_ops = { 246 .get_ro = tegra_sdhci_get_ro, 247 .read_l = tegra_sdhci_readl, 248 .read_w = tegra_sdhci_readw, 249 .write_l = tegra_sdhci_writel, 250 .platform_8bit_width = tegra_sdhci_8bit, 251 }; 252 253 struct sdhci_pltfm_data sdhci_tegra_pdata = { 254 .quirks = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 255 SDHCI_QUIRK_SINGLE_POWER_WRITE | 256 SDHCI_QUIRK_NO_HISPD_BIT | 257 SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC, 258 .ops = &tegra_sdhci_ops, 259 .init = tegra_sdhci_pltfm_init, 260 .exit = tegra_sdhci_pltfm_exit, 261 }; 262