1/* 2 * OpenFirmware bindings for Secure Digital Host Controller Interface. 3 * 4 * Copyright (c) 2007 Freescale Semiconductor, Inc. 5 * Copyright (c) 2009 MontaVista Software, Inc. 6 * 7 * Authors: Xiaobo Xie <X.Xie@freescale.com> 8 * Anton Vorontsov <avorontsov@ru.mvista.com> 9 * 10 * This program is free software; you can redistribute it and/or modify 11 * it under the terms of the GNU General Public License as published by 12 * the Free Software Foundation; either version 2 of the License, or (at 13 * your option) any later version. 14 */ 15 16#include <linux/module.h> 17#include <linux/init.h> 18#include <linux/io.h> 19#include <linux/interrupt.h> 20#include <linux/delay.h> 21#include <linux/of.h> 22#include <linux/of_platform.h> 23#include <linux/mmc/host.h> 24#include <asm/machdep.h> 25#include "sdhci-of.h" 26#include "sdhci.h" 27 28#ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER 29 30/* 31 * These accessors are designed for big endian hosts doing I/O to 32 * little endian controllers incorporating a 32-bit hardware byte swapper. 33 */ 34 35u32 sdhci_be32bs_readl(struct sdhci_host *host, int reg) 36{ 37 return in_be32(host->ioaddr + reg); 38} 39 40u16 sdhci_be32bs_readw(struct sdhci_host *host, int reg) 41{ 42 return in_be16(host->ioaddr + (reg ^ 0x2)); 43} 44 45u8 sdhci_be32bs_readb(struct sdhci_host *host, int reg) 46{ 47 return in_8(host->ioaddr + (reg ^ 0x3)); 48} 49 50void sdhci_be32bs_writel(struct sdhci_host *host, u32 val, int reg) 51{ 52 out_be32(host->ioaddr + reg, val); 53} 54 55void sdhci_be32bs_writew(struct sdhci_host *host, u16 val, int reg) 56{ 57 struct sdhci_of_host *of_host = sdhci_priv(host); 58 int base = reg & ~0x3; 59 int shift = (reg & 0x2) * 8; 60 61 switch (reg) { 62 case SDHCI_TRANSFER_MODE: 63 /* 64 * Postpone this write, we must do it together with a 65 * command write that is down below. 66 */ 67 of_host->xfer_mode_shadow = val; 68 return; 69 case SDHCI_COMMAND: 70 sdhci_be32bs_writel(host, val << 16 | of_host->xfer_mode_shadow, 71 SDHCI_TRANSFER_MODE); 72 return; 73 } 74 clrsetbits_be32(host->ioaddr + base, 0xffff << shift, val << shift); 75} 76 77void sdhci_be32bs_writeb(struct sdhci_host *host, u8 val, int reg) 78{ 79 int base = reg & ~0x3; 80 int shift = (reg & 0x3) * 8; 81 82 clrsetbits_be32(host->ioaddr + base , 0xff << shift, val << shift); 83} 84#endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */ 85 86#ifdef CONFIG_PM 87 88static int sdhci_of_suspend(struct platform_device *ofdev, pm_message_t state) 89{ 90 struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); 91 92 return mmc_suspend_host(host->mmc); 93} 94 95static int sdhci_of_resume(struct platform_device *ofdev) 96{ 97 struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); 98 99 return mmc_resume_host(host->mmc); 100} 101 102#else 103 104#define sdhci_of_suspend NULL 105#define sdhci_of_resume NULL 106 107#endif 108 109static bool __devinit sdhci_of_wp_inverted(struct device_node *np) 110{ 111 if (of_get_property(np, "sdhci,wp-inverted", NULL)) 112 return true; 113 114 /* Old device trees don't have the wp-inverted property. */ 115 return machine_is(mpc837x_rdb) || machine_is(mpc837x_mds); 116} 117 118static int __devinit sdhci_of_probe(struct platform_device *ofdev, 119 const struct of_device_id *match) 120{ 121 struct device_node *np = ofdev->dev.of_node; 122 struct sdhci_of_data *sdhci_of_data = match->data; 123 struct sdhci_host *host; 124 struct sdhci_of_host *of_host; 125 const u32 *clk; 126 int size; 127 int ret; 128 129 if (!of_device_is_available(np)) 130 return -ENODEV; 131 132 host = sdhci_alloc_host(&ofdev->dev, sizeof(*of_host)); 133 if (IS_ERR(host)) 134 return -ENOMEM; 135 136 of_host = sdhci_priv(host); 137 dev_set_drvdata(&ofdev->dev, host); 138 139 host->ioaddr = of_iomap(np, 0); 140 if (!host->ioaddr) { 141 ret = -ENOMEM; 142 goto err_addr_map; 143 } 144 145 host->irq = irq_of_parse_and_map(np, 0); 146 if (!host->irq) { 147 ret = -EINVAL; 148 goto err_no_irq; 149 } 150 151 host->hw_name = dev_name(&ofdev->dev); 152 if (sdhci_of_data) { 153 host->quirks = sdhci_of_data->quirks; 154 host->ops = &sdhci_of_data->ops; 155 } 156 157 if (of_get_property(np, "sdhci,auto-cmd12", NULL)) 158 host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12; 159 160 161 if (of_get_property(np, "sdhci,1-bit-only", NULL)) 162 host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA; 163 164 if (sdhci_of_wp_inverted(np)) 165 host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT; 166 167 clk = of_get_property(np, "clock-frequency", &size); 168 if (clk && size == sizeof(*clk) && *clk) 169 of_host->clock = *clk; 170 171 ret = sdhci_add_host(host); 172 if (ret) 173 goto err_add_host; 174 175 return 0; 176 177err_add_host: 178 irq_dispose_mapping(host->irq); 179err_no_irq: 180 iounmap(host->ioaddr); 181err_addr_map: 182 sdhci_free_host(host); 183 return ret; 184} 185 186static int __devexit sdhci_of_remove(struct platform_device *ofdev) 187{ 188 struct sdhci_host *host = dev_get_drvdata(&ofdev->dev); 189 190 sdhci_remove_host(host, 0); 191 sdhci_free_host(host); 192 irq_dispose_mapping(host->irq); 193 iounmap(host->ioaddr); 194 return 0; 195} 196 197static const struct of_device_id sdhci_of_match[] = { 198#ifdef CONFIG_MMC_SDHCI_OF_ESDHC 199 { .compatible = "fsl,mpc8379-esdhc", .data = &sdhci_esdhc, }, 200 { .compatible = "fsl,mpc8536-esdhc", .data = &sdhci_esdhc, }, 201 { .compatible = "fsl,esdhc", .data = &sdhci_esdhc, }, 202#endif 203#ifdef CONFIG_MMC_SDHCI_OF_HLWD 204 { .compatible = "nintendo,hollywood-sdhci", .data = &sdhci_hlwd, }, 205#endif 206 { .compatible = "generic-sdhci", }, 207 {}, 208}; 209MODULE_DEVICE_TABLE(of, sdhci_of_match); 210 211static struct of_platform_driver sdhci_of_driver = { 212 .driver = { 213 .name = "sdhci-of", 214 .owner = THIS_MODULE, 215 .of_match_table = sdhci_of_match, 216 }, 217 .probe = sdhci_of_probe, 218 .remove = __devexit_p(sdhci_of_remove), 219 .suspend = sdhci_of_suspend, 220 .resume = sdhci_of_resume, 221}; 222 223static int __init sdhci_of_init(void) 224{ 225 return of_register_platform_driver(&sdhci_of_driver); 226} 227module_init(sdhci_of_init); 228 229static void __exit sdhci_of_exit(void) 230{ 231 of_unregister_platform_driver(&sdhci_of_driver); 232} 233module_exit(sdhci_of_exit); 234 235MODULE_DESCRIPTION("Secure Digital Host Controller Interface OF driver"); 236MODULE_AUTHOR("Xiaobo Xie <X.Xie@freescale.com>, " 237 "Anton Vorontsov <avorontsov@ru.mvista.com>"); 238MODULE_LICENSE("GPL"); 239